From b65bad99f7e9c7c2d9a19a54ff385146f522082c Mon Sep 17 00:00:00 2001 From: Spencer Imbleau Date: Wed, 12 May 2021 09:02:13 -0400 Subject: [PATCH 001/621] Clarified the attribute which prompts the warning The example call was lacking clarification of the `#![warn(rustdoc::invalid_codeblock_attributes)]` attribute which generates the specified warning. --- src/doc/rustdoc/src/lints.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index a6626679a7d60..c494eef1b5705 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -227,6 +227,8 @@ This lint **warns by default**. It detects code block attributes in documentation examples that have potentially mis-typed values. For example: ```rust +#![warn(rustdoc::invalid_codeblock_attributes)] + /// Example. /// /// ```should-panic From 2b27c82fbb691f6b2d3440535a13780fdf36ee23 Mon Sep 17 00:00:00 2001 From: Spencer Imbleau Date: Wed, 12 May 2021 09:52:28 -0400 Subject: [PATCH 002/621] Clarified all attributes which prompt warnings All calls which trigger rustdoc warnings and are now properly verbose for consistency. This uses the attribute in the examples which provides the user with more context. --- src/doc/rustdoc/src/lints.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index c494eef1b5705..dddff0475ad5c 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -70,6 +70,8 @@ This lint **warns by default**. This lint detects when [intra-doc links] from pu For example: ```rust +#![warn(private_intra_doc_links)] + /// [private] pub fn public() {} fn private() {} @@ -302,6 +304,8 @@ This lint is **warn-by-default**. It detects URLs which are not links. For example: ```rust +#![warn(bare_urls)] + /// http://example.org /// [http://example.net] pub fn foo() {} From d7884d4b08f0eedbdc6a3dabd0fdaa3779d393b6 Mon Sep 17 00:00:00 2001 From: Spencer Imbleau Date: Sun, 16 May 2021 12:54:53 -0400 Subject: [PATCH 003/621] Noted necessessity and fixed rustdoc:: prefix Now shows that certain warnings are unnecessary but includes them for consistency. --- src/doc/rustdoc/src/lints.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index dddff0475ad5c..a496db600ef93 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -70,7 +70,7 @@ This lint **warns by default**. This lint detects when [intra-doc links] from pu For example: ```rust -#![warn(private_intra_doc_links)] +#![warn(rustdoc::private_intra_doc_links)] // note: unecessary - warns by default. /// [private] pub fn public() {} @@ -112,7 +112,7 @@ This lint is **allowed by default**. It detects items missing documentation. For example: ```rust -#![warn(missing_docs)] +#![warn(rustdoc::missing_docs)] pub fn undocumented() {} # fn main() {} @@ -229,7 +229,7 @@ This lint **warns by default**. It detects code block attributes in documentation examples that have potentially mis-typed values. For example: ```rust -#![warn(rustdoc::invalid_codeblock_attributes)] +#![warn(rustdoc::invalid_codeblock_attributes)] // note: unecessary - warns by default. /// Example. /// @@ -304,7 +304,7 @@ This lint is **warn-by-default**. It detects URLs which are not links. For example: ```rust -#![warn(bare_urls)] +#![warn(rustdoc::bare_urls)] // note: unecessary - warns by default. /// http://example.org /// [http://example.net] From 8a7bb2bb66f86f2e106b85145eae824f84da2419 Mon Sep 17 00:00:00 2001 From: Spencer Imbleau Date: Sun, 16 May 2021 12:58:52 -0400 Subject: [PATCH 004/621] Fixed item typo which did not need prefix --- src/doc/rustdoc/src/lints.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index a496db600ef93..1c85ee0eeb553 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -112,7 +112,7 @@ This lint is **allowed by default**. It detects items missing documentation. For example: ```rust -#![warn(rustdoc::missing_docs)] +#![warn(missing_docs)] pub fn undocumented() {} # fn main() {} From c17e3e2c09ad984341d34fff9cd67d706d46157d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 16 Apr 2021 20:40:50 -0700 Subject: [PATCH 005/621] Add improper ctypes test for ptr to unit, ptr to tuple --- src/test/ui/lint/lint-ctypes-fn.rs | 4 ++ src/test/ui/lint/lint-ctypes-fn.stderr | 40 +++++++-------- src/test/ui/lint/lint-ctypes.rs | 2 + src/test/ui/lint/lint-ctypes.stderr | 68 ++++++++++++++++---------- 4 files changed, 69 insertions(+), 45 deletions(-) diff --git a/src/test/ui/lint/lint-ctypes-fn.rs b/src/test/ui/lint/lint-ctypes-fn.rs index c18cb881032a6..d3b36a9d59c70 100644 --- a/src/test/ui/lint/lint-ctypes-fn.rs +++ b/src/test/ui/lint/lint-ctypes-fn.rs @@ -66,6 +66,10 @@ pub extern "C" fn ptr_type1(size: *const Foo) { } pub extern "C" fn ptr_type2(size: *const Foo) { } +pub extern "C" fn ptr_unit(p: *const ()) { } + +pub extern "C" fn ptr_tuple(p: *const ((),)) { } + pub extern "C" fn slice_type(p: &[u32]) { } //~^ ERROR: uses type `[u32]` diff --git a/src/test/ui/lint/lint-ctypes-fn.stderr b/src/test/ui/lint/lint-ctypes-fn.stderr index d591d4ad292dd..740075ca7dfff 100644 --- a/src/test/ui/lint/lint-ctypes-fn.stderr +++ b/src/test/ui/lint/lint-ctypes-fn.stderr @@ -1,5 +1,5 @@ error: `extern` fn uses type `[u32]`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:69:33 + --> $DIR/lint-ctypes-fn.rs:73:33 | LL | pub extern "C" fn slice_type(p: &[u32]) { } | ^^^^^^ not FFI-safe @@ -13,7 +13,7 @@ LL | #![deny(improper_ctypes_definitions)] = note: slices have no C equivalent error: `extern` fn uses type `str`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:72:31 + --> $DIR/lint-ctypes-fn.rs:76:31 | LL | pub extern "C" fn str_type(p: &str) { } | ^^^^ not FFI-safe @@ -22,7 +22,7 @@ LL | pub extern "C" fn str_type(p: &str) { } = note: string slices have no C equivalent error: `extern` fn uses type `Box<[u8]>`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:79:34 + --> $DIR/lint-ctypes-fn.rs:83:34 | LL | pub extern "C" fn boxed_slice(p: Box<[u8]>) { } | ^^^^^^^^^ not FFI-safe @@ -30,7 +30,7 @@ LL | pub extern "C" fn boxed_slice(p: Box<[u8]>) { } = note: box cannot be represented as a single pointer error: `extern` fn uses type `Box`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:82:35 + --> $DIR/lint-ctypes-fn.rs:86:35 | LL | pub extern "C" fn boxed_string(p: Box) { } | ^^^^^^^^ not FFI-safe @@ -38,7 +38,7 @@ LL | pub extern "C" fn boxed_string(p: Box) { } = note: box cannot be represented as a single pointer error: `extern` fn uses type `Box`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:85:34 + --> $DIR/lint-ctypes-fn.rs:89:34 | LL | pub extern "C" fn boxed_trait(p: Box) { } | ^^^^^^^^^^^^^^ not FFI-safe @@ -46,7 +46,7 @@ LL | pub extern "C" fn boxed_trait(p: Box) { } = note: box cannot be represented as a single pointer error: `extern` fn uses type `char`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:88:32 + --> $DIR/lint-ctypes-fn.rs:92:32 | LL | pub extern "C" fn char_type(p: char) { } | ^^^^ not FFI-safe @@ -55,7 +55,7 @@ LL | pub extern "C" fn char_type(p: char) { } = note: the `char` type has no C equivalent error: `extern` fn uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:91:32 + --> $DIR/lint-ctypes-fn.rs:95:32 | LL | pub extern "C" fn i128_type(p: i128) { } | ^^^^ not FFI-safe @@ -63,7 +63,7 @@ LL | pub extern "C" fn i128_type(p: i128) { } = note: 128-bit integers don't currently have a known stable ABI error: `extern` fn uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:94:32 + --> $DIR/lint-ctypes-fn.rs:98:32 | LL | pub extern "C" fn u128_type(p: u128) { } | ^^^^ not FFI-safe @@ -71,7 +71,7 @@ LL | pub extern "C" fn u128_type(p: u128) { } = note: 128-bit integers don't currently have a known stable ABI error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:97:33 + --> $DIR/lint-ctypes-fn.rs:101:33 | LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } | ^^^^^^^^^^ not FFI-safe @@ -80,7 +80,7 @@ LL | pub extern "C" fn tuple_type(p: (i32, i32)) { } = note: tuples have unspecified layout error: `extern` fn uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:100:34 + --> $DIR/lint-ctypes-fn.rs:104:34 | LL | pub extern "C" fn tuple_type2(p: I32Pair) { } | ^^^^^^^ not FFI-safe @@ -89,7 +89,7 @@ LL | pub extern "C" fn tuple_type2(p: I32Pair) { } = note: tuples have unspecified layout error: `extern` fn uses type `ZeroSize`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:103:32 + --> $DIR/lint-ctypes-fn.rs:107:32 | LL | pub extern "C" fn zero_size(p: ZeroSize) { } | ^^^^^^^^ not FFI-safe @@ -103,7 +103,7 @@ LL | pub struct ZeroSize; | ^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:106:40 + --> $DIR/lint-ctypes-fn.rs:110:40 | LL | pub extern "C" fn zero_size_phantom(p: ZeroSizeWithPhantomData) { } | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -116,7 +116,7 @@ LL | pub struct ZeroSizeWithPhantomData(PhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` fn uses type `PhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:109:51 + --> $DIR/lint-ctypes-fn.rs:113:51 | LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -124,7 +124,7 @@ LL | pub extern "C" fn zero_size_phantom_toplevel() -> PhantomData { = note: composed only of `PhantomData` error: `extern` fn uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:114:30 + --> $DIR/lint-ctypes-fn.rs:118:30 | LL | pub extern "C" fn fn_type(p: RustFn) { } | ^^^^^^ not FFI-safe @@ -133,7 +133,7 @@ LL | pub extern "C" fn fn_type(p: RustFn) { } = note: this function pointer has Rust-specific calling convention error: `extern` fn uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:117:31 + --> $DIR/lint-ctypes-fn.rs:121:31 | LL | pub extern "C" fn fn_type2(p: fn()) { } | ^^^^ not FFI-safe @@ -142,7 +142,7 @@ LL | pub extern "C" fn fn_type2(p: fn()) { } = note: this function pointer has Rust-specific calling convention error: `extern` fn uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:122:39 + --> $DIR/lint-ctypes-fn.rs:126:39 | LL | pub extern "C" fn transparent_i128(p: TransparentI128) { } | ^^^^^^^^^^^^^^^ not FFI-safe @@ -150,7 +150,7 @@ LL | pub extern "C" fn transparent_i128(p: TransparentI128) { } = note: 128-bit integers don't currently have a known stable ABI error: `extern` fn uses type `str`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:125:38 + --> $DIR/lint-ctypes-fn.rs:129:38 | LL | pub extern "C" fn transparent_str(p: TransparentStr) { } | ^^^^^^^^^^^^^^ not FFI-safe @@ -159,7 +159,7 @@ LL | pub extern "C" fn transparent_str(p: TransparentStr) { } = note: string slices have no C equivalent error: `extern` fn uses type `PhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:171:43 + --> $DIR/lint-ctypes-fn.rs:175:43 | LL | pub extern "C" fn unused_generic2() -> PhantomData { | ^^^^^^^^^^^^^^^^^ not FFI-safe @@ -167,7 +167,7 @@ LL | pub extern "C" fn unused_generic2() -> PhantomData { = note: composed only of `PhantomData` error: `extern` fn uses type `Vec`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:184:39 + --> $DIR/lint-ctypes-fn.rs:188:39 | LL | pub extern "C" fn used_generic4(x: Vec) { } | ^^^^^^ not FFI-safe @@ -176,7 +176,7 @@ LL | pub extern "C" fn used_generic4(x: Vec) { } = note: this struct has unspecified layout error: `extern` fn uses type `Vec`, which is not FFI-safe - --> $DIR/lint-ctypes-fn.rs:187:41 + --> $DIR/lint-ctypes-fn.rs:191:41 | LL | pub extern "C" fn used_generic5() -> Vec { | ^^^^^^ not FFI-safe diff --git a/src/test/ui/lint/lint-ctypes.rs b/src/test/ui/lint/lint-ctypes.rs index a291471f56670..4a55b7cb25eb8 100644 --- a/src/test/ui/lint/lint-ctypes.rs +++ b/src/test/ui/lint/lint-ctypes.rs @@ -47,6 +47,8 @@ pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData); extern "C" { pub fn ptr_type1(size: *const Foo); //~ ERROR: uses type `Foo` pub fn ptr_type2(size: *const Foo); //~ ERROR: uses type `Foo` + pub fn ptr_unit(p: *const ()); //~ ERROR: uses type `()` + pub fn ptr_tuple(p: *const ((),)); //~ ERROR: uses type `((),)` pub fn slice_type(p: &[u32]); //~ ERROR: uses type `[u32]` pub fn str_type(p: &str); //~ ERROR: uses type `str` pub fn box_type(p: Box); //~ ERROR uses type `Box` diff --git a/src/test/ui/lint/lint-ctypes.stderr b/src/test/ui/lint/lint-ctypes.stderr index 7f21e412c385c..8295d0655e305 100644 --- a/src/test/ui/lint/lint-ctypes.stderr +++ b/src/test/ui/lint/lint-ctypes.stderr @@ -31,8 +31,26 @@ note: the type is defined here LL | pub struct Foo; | ^^^^^^^^^^^^^^^ +error: `extern` block uses type `()`, which is not FFI-safe + --> $DIR/lint-ctypes.rs:50:24 + | +LL | pub fn ptr_unit(p: *const ()); + | ^^^^^^^^^ not FFI-safe + | + = help: consider using a struct instead + = note: tuples have unspecified layout + +error: `extern` block uses type `((),)`, which is not FFI-safe + --> $DIR/lint-ctypes.rs:51:25 + | +LL | pub fn ptr_tuple(p: *const ((),)); + | ^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a struct instead + = note: tuples have unspecified layout + error: `extern` block uses type `[u32]`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:50:26 + --> $DIR/lint-ctypes.rs:52:26 | LL | pub fn slice_type(p: &[u32]); | ^^^^^^ not FFI-safe @@ -41,7 +59,7 @@ LL | pub fn slice_type(p: &[u32]); = note: slices have no C equivalent error: `extern` block uses type `str`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:51:24 + --> $DIR/lint-ctypes.rs:53:24 | LL | pub fn str_type(p: &str); | ^^^^ not FFI-safe @@ -50,7 +68,7 @@ LL | pub fn str_type(p: &str); = note: string slices have no C equivalent error: `extern` block uses type `Box`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:52:24 + --> $DIR/lint-ctypes.rs:54:24 | LL | pub fn box_type(p: Box); | ^^^^^^^^ not FFI-safe @@ -59,7 +77,7 @@ LL | pub fn box_type(p: Box); = note: this struct has unspecified layout error: `extern` block uses type `Option>`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:53:28 + --> $DIR/lint-ctypes.rs:55:28 | LL | pub fn opt_box_type(p: Option>); | ^^^^^^^^^^^^^^^^ not FFI-safe @@ -68,7 +86,7 @@ LL | pub fn opt_box_type(p: Option>); = note: enum has no representation hint error: `extern` block uses type `char`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:55:25 + --> $DIR/lint-ctypes.rs:57:25 | LL | pub fn char_type(p: char); | ^^^^ not FFI-safe @@ -77,7 +95,7 @@ LL | pub fn char_type(p: char); = note: the `char` type has no C equivalent error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:56:25 + --> $DIR/lint-ctypes.rs:58:25 | LL | pub fn i128_type(p: i128); | ^^^^ not FFI-safe @@ -85,7 +103,7 @@ LL | pub fn i128_type(p: i128); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:57:25 + --> $DIR/lint-ctypes.rs:59:25 | LL | pub fn u128_type(p: u128); | ^^^^ not FFI-safe @@ -93,7 +111,7 @@ LL | pub fn u128_type(p: u128); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `dyn Bar`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:58:26 + --> $DIR/lint-ctypes.rs:60:26 | LL | pub fn trait_type(p: &dyn Bar); | ^^^^^^^^ not FFI-safe @@ -101,7 +119,7 @@ LL | pub fn trait_type(p: &dyn Bar); = note: trait objects have no C equivalent error: `extern` block uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:59:26 + --> $DIR/lint-ctypes.rs:61:26 | LL | pub fn tuple_type(p: (i32, i32)); | ^^^^^^^^^^ not FFI-safe @@ -110,7 +128,7 @@ LL | pub fn tuple_type(p: (i32, i32)); = note: tuples have unspecified layout error: `extern` block uses type `(i32, i32)`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:60:27 + --> $DIR/lint-ctypes.rs:62:27 | LL | pub fn tuple_type2(p: I32Pair); | ^^^^^^^ not FFI-safe @@ -119,7 +137,7 @@ LL | pub fn tuple_type2(p: I32Pair); = note: tuples have unspecified layout error: `extern` block uses type `ZeroSize`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:61:25 + --> $DIR/lint-ctypes.rs:63:25 | LL | pub fn zero_size(p: ZeroSize); | ^^^^^^^^ not FFI-safe @@ -133,7 +151,7 @@ LL | pub struct ZeroSize; | ^^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `ZeroSizeWithPhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:62:33 + --> $DIR/lint-ctypes.rs:64:33 | LL | pub fn zero_size_phantom(p: ZeroSizeWithPhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -146,7 +164,7 @@ LL | pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `extern` block uses type `PhantomData`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:65:12 + --> $DIR/lint-ctypes.rs:67:12 | LL | -> ::std::marker::PhantomData; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -154,7 +172,7 @@ LL | -> ::std::marker::PhantomData; = note: composed only of `PhantomData` error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:66:23 + --> $DIR/lint-ctypes.rs:68:23 | LL | pub fn fn_type(p: RustFn); | ^^^^^^ not FFI-safe @@ -163,7 +181,7 @@ LL | pub fn fn_type(p: RustFn); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `fn()`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:67:24 + --> $DIR/lint-ctypes.rs:69:24 | LL | pub fn fn_type2(p: fn()); | ^^^^ not FFI-safe @@ -172,7 +190,7 @@ LL | pub fn fn_type2(p: fn()); = note: this function pointer has Rust-specific calling convention error: `extern` block uses type `Box`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:68:28 + --> $DIR/lint-ctypes.rs:70:28 | LL | pub fn fn_contained(p: RustBadRet); | ^^^^^^^^^^ not FFI-safe @@ -181,7 +199,7 @@ LL | pub fn fn_contained(p: RustBadRet); = note: this struct has unspecified layout error: `extern` block uses type `i128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:69:32 + --> $DIR/lint-ctypes.rs:71:32 | LL | pub fn transparent_i128(p: TransparentI128); | ^^^^^^^^^^^^^^^ not FFI-safe @@ -189,7 +207,7 @@ LL | pub fn transparent_i128(p: TransparentI128); = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `str`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:70:31 + --> $DIR/lint-ctypes.rs:72:31 | LL | pub fn transparent_str(p: TransparentStr); | ^^^^^^^^^^^^^^ not FFI-safe @@ -198,7 +216,7 @@ LL | pub fn transparent_str(p: TransparentStr); = note: string slices have no C equivalent error: `extern` block uses type `Box`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:71:30 + --> $DIR/lint-ctypes.rs:73:30 | LL | pub fn transparent_fn(p: TransparentBadFn); | ^^^^^^^^^^^^^^^^ not FFI-safe @@ -207,7 +225,7 @@ LL | pub fn transparent_fn(p: TransparentBadFn); = note: this struct has unspecified layout error: `extern` block uses type `[u8; 8]`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:72:27 + --> $DIR/lint-ctypes.rs:74:27 | LL | pub fn raw_array(arr: [u8; 8]); | ^^^^^^^ not FFI-safe @@ -216,7 +234,7 @@ LL | pub fn raw_array(arr: [u8; 8]); = note: passing raw arrays by value is not FFI-safe error: `extern` block uses type `Option>`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:74:26 + --> $DIR/lint-ctypes.rs:76:26 | LL | pub fn no_niche_a(a: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -225,7 +243,7 @@ LL | pub fn no_niche_a(a: Option>); = note: enum has no representation hint error: `extern` block uses type `Option>`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:76:26 + --> $DIR/lint-ctypes.rs:78:26 | LL | pub fn no_niche_b(b: Option>); | ^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe @@ -234,7 +252,7 @@ LL | pub fn no_niche_b(b: Option>); = note: enum has no representation hint error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:79:34 + --> $DIR/lint-ctypes.rs:81:34 | LL | pub static static_u128_type: u128; | ^^^^ not FFI-safe @@ -242,12 +260,12 @@ LL | pub static static_u128_type: u128; = note: 128-bit integers don't currently have a known stable ABI error: `extern` block uses type `u128`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:80:40 + --> $DIR/lint-ctypes.rs:82:40 | LL | pub static static_u128_array_type: [u128; 16]; | ^^^^^^^^^^ not FFI-safe | = note: 128-bit integers don't currently have a known stable ABI -error: aborting due to 26 previous errors +error: aborting due to 28 previous errors From abfad74ec6326b806509a676b6527e8d645d4fdf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 16 Apr 2021 11:50:51 -0700 Subject: [PATCH 006/621] Make *const (), *mut () okay for FFI --- compiler/rustc_lint/src/types.rs | 9 +++++++++ src/test/ui/lint/lint-ctypes.rs | 2 +- src/test/ui/lint/lint-ctypes.stderr | 11 +---------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 5d2256100ff67..4e3bbc58559d9 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1060,6 +1060,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { FfiSafe } + ty::RawPtr(ty::TypeAndMut { ty, .. }) + if match ty.kind() { + ty::Tuple(tuple) => tuple.is_empty(), + _ => false, + } => + { + FfiSafe + } + ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => { self.check_type_for_ffi(cache, ty) } diff --git a/src/test/ui/lint/lint-ctypes.rs b/src/test/ui/lint/lint-ctypes.rs index 4a55b7cb25eb8..9165e14b7ffe3 100644 --- a/src/test/ui/lint/lint-ctypes.rs +++ b/src/test/ui/lint/lint-ctypes.rs @@ -47,7 +47,7 @@ pub struct ZeroSizeWithPhantomData(::std::marker::PhantomData); extern "C" { pub fn ptr_type1(size: *const Foo); //~ ERROR: uses type `Foo` pub fn ptr_type2(size: *const Foo); //~ ERROR: uses type `Foo` - pub fn ptr_unit(p: *const ()); //~ ERROR: uses type `()` + pub fn ptr_unit(p: *const ()); pub fn ptr_tuple(p: *const ((),)); //~ ERROR: uses type `((),)` pub fn slice_type(p: &[u32]); //~ ERROR: uses type `[u32]` pub fn str_type(p: &str); //~ ERROR: uses type `str` diff --git a/src/test/ui/lint/lint-ctypes.stderr b/src/test/ui/lint/lint-ctypes.stderr index 8295d0655e305..342b6bfc6f87a 100644 --- a/src/test/ui/lint/lint-ctypes.stderr +++ b/src/test/ui/lint/lint-ctypes.stderr @@ -31,15 +31,6 @@ note: the type is defined here LL | pub struct Foo; | ^^^^^^^^^^^^^^^ -error: `extern` block uses type `()`, which is not FFI-safe - --> $DIR/lint-ctypes.rs:50:24 - | -LL | pub fn ptr_unit(p: *const ()); - | ^^^^^^^^^ not FFI-safe - | - = help: consider using a struct instead - = note: tuples have unspecified layout - error: `extern` block uses type `((),)`, which is not FFI-safe --> $DIR/lint-ctypes.rs:51:25 | @@ -267,5 +258,5 @@ LL | pub static static_u128_array_type: [u128; 16]; | = note: 128-bit integers don't currently have a known stable ABI -error: aborting due to 28 previous errors +error: aborting due to 27 previous errors From e2d6334191917c9c78f27aadb19722511cad3f47 Mon Sep 17 00:00:00 2001 From: Christiaan Dirkx Date: Fri, 18 Jun 2021 15:56:22 +0200 Subject: [PATCH 007/621] Add `Ipv6Addr::is_benchmarking` --- library/std/src/net/ip.rs | 22 ++++++++++ library/std/src/net/ip/tests.rs | 74 ++++++++++++++++++++------------- 2 files changed, 67 insertions(+), 29 deletions(-) diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs index baf1c5aa2b941..d09a7a99d565c 100644 --- a/library/std/src/net/ip.rs +++ b/library/std/src/net/ip.rs @@ -1370,6 +1370,28 @@ impl Ipv6Addr { (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) } + /// Returns [`true`] if this is an address reserved for benchmarking (`2001:2::/48`). + /// + /// This property is defined in [IETF RFC 5180], where it is mistakenly specified as covering the range `2001:0200::/48`. + /// This is corrected in [IETF RFC Errata 1752] to `2001:0002::/48`. + /// + /// [IETF RFC 5180]: https://tools.ietf.org/html/rfc5180 + /// [IETF RFC Errata 1752]: https://www.rfc-editor.org/errata_search.php?eid=1752 + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc613, 0x0).is_benchmarking(), false); + /// assert_eq!(Ipv6Addr::new(0x2001, 0x2, 0, 0, 0, 0, 0, 0).is_benchmarking(), true); + /// ``` + #[unstable(feature = "ip", issue = "27709")] + #[inline] + pub const fn is_benchmarking(&self) -> bool { + (self.segments()[0] == 0x2001) && (self.segments()[1] == 0x2) && (self.segments()[2] == 0) + } + /// Returns [`true`] if the address is a globally routable unicast address. /// /// The following return false: diff --git a/library/std/src/net/ip/tests.rs b/library/std/src/net/ip/tests.rs index 2109980ad058d..557e04081cadf 100644 --- a/library/std/src/net/ip/tests.rs +++ b/library/std/src/net/ip/tests.rs @@ -475,21 +475,22 @@ fn ipv6_properties() { assert_eq!(&ip!($s).octets(), octets); assert_eq!(Ipv6Addr::from(*octets), ip!($s)); - let unspecified: u16 = 1 << 0; - let loopback: u16 = 1 << 1; - let unique_local: u16 = 1 << 2; - let global: u16 = 1 << 3; - let unicast_link_local: u16 = 1 << 4; - let unicast_global: u16 = 1 << 7; - let documentation: u16 = 1 << 8; - let multicast_interface_local: u16 = 1 << 9; - let multicast_link_local: u16 = 1 << 10; - let multicast_realm_local: u16 = 1 << 11; - let multicast_admin_local: u16 = 1 << 12; - let multicast_site_local: u16 = 1 << 13; - let multicast_organization_local: u16 = 1 << 14; - let multicast_global: u16 = 1 << 15; - let multicast: u16 = multicast_interface_local + let unspecified: u32 = 1 << 0; + let loopback: u32 = 1 << 1; + let unique_local: u32 = 1 << 2; + let global: u32 = 1 << 3; + let unicast_link_local: u32 = 1 << 4; + let unicast_global: u32 = 1 << 7; + let documentation: u32 = 1 << 8; + let benchmarking: u32 = 1 << 16; + let multicast_interface_local: u32 = 1 << 9; + let multicast_link_local: u32 = 1 << 10; + let multicast_realm_local: u32 = 1 << 11; + let multicast_admin_local: u32 = 1 << 12; + let multicast_site_local: u32 = 1 << 13; + let multicast_organization_local: u32 = 1 << 14; + let multicast_global: u32 = 1 << 15; + let multicast: u32 = multicast_interface_local | multicast_admin_local | multicast_global | multicast_link_local @@ -532,6 +533,11 @@ fn ipv6_properties() { } else { assert!(!ip!($s).is_documentation()); } + if ($mask & benchmarking) == benchmarking { + assert!(ip!($s).is_benchmarking()); + } else { + assert!(!ip!($s).is_benchmarking()); + } if ($mask & multicast) != 0 { assert!(ip!($s).multicast_scope().is_some()); assert!(ip!($s).is_multicast()); @@ -570,20 +576,21 @@ fn ipv6_properties() { } } - let unspecified: u16 = 1 << 0; - let loopback: u16 = 1 << 1; - let unique_local: u16 = 1 << 2; - let global: u16 = 1 << 3; - let unicast_link_local: u16 = 1 << 4; - let unicast_global: u16 = 1 << 7; - let documentation: u16 = 1 << 8; - let multicast_interface_local: u16 = 1 << 9; - let multicast_link_local: u16 = 1 << 10; - let multicast_realm_local: u16 = 1 << 11; - let multicast_admin_local: u16 = 1 << 12; - let multicast_site_local: u16 = 1 << 13; - let multicast_organization_local: u16 = 1 << 14; - let multicast_global: u16 = 1 << 15; + let unspecified: u32 = 1 << 0; + let loopback: u32 = 1 << 1; + let unique_local: u32 = 1 << 2; + let global: u32 = 1 << 3; + let unicast_link_local: u32 = 1 << 4; + let unicast_global: u32 = 1 << 7; + let documentation: u32 = 1 << 8; + let benchmarking: u32 = 1 << 16; + let multicast_interface_local: u32 = 1 << 9; + let multicast_link_local: u32 = 1 << 10; + let multicast_realm_local: u32 = 1 << 11; + let multicast_admin_local: u32 = 1 << 12; + let multicast_site_local: u32 = 1 << 13; + let multicast_organization_local: u32 = 1 << 14; + let multicast_global: u32 = 1 << 15; check!("::", &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], unspecified); @@ -679,6 +686,12 @@ fn ipv6_properties() { documentation ); + check!( + "2001:2::ac32:23ff:21", + &[0x20, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0xac, 0x32, 0x23, 0xff, 0, 0x21], + global | unicast_global | benchmarking + ); + check!( "102:304:506:708:90a:b0c:d0e:f10", &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], @@ -885,6 +898,9 @@ fn ipv6_const() { const IS_DOCUMENTATION: bool = IP_ADDRESS.is_documentation(); assert!(!IS_DOCUMENTATION); + const IS_BENCHMARKING: bool = IP_ADDRESS.is_benchmarking(); + assert!(!IS_BENCHMARKING); + const IS_UNICAST_GLOBAL: bool = IP_ADDRESS.is_unicast_global(); assert!(!IS_UNICAST_GLOBAL); From cbaccc12c700a2f4d4d22e2765c10b105497025e Mon Sep 17 00:00:00 2001 From: Christiaan Dirkx Date: Fri, 18 Jun 2021 16:13:05 +0200 Subject: [PATCH 008/621] Add `IpAddr::is_benchmarking` --- library/std/src/net/ip.rs | 24 ++++++++++++++++++++++++ library/std/src/net/ip/tests.rs | 15 ++++++++++++--- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs index d09a7a99d565c..c57a50f5edede 100644 --- a/library/std/src/net/ip.rs +++ b/library/std/src/net/ip.rs @@ -252,6 +252,30 @@ impl IpAddr { } } + /// Returns [`true`] if this address is in a range designated for benchmarking. + /// + /// See the documentation for [`Ipv4Addr::is_benchmarking()`] and + /// [`Ipv6Addr::is_benchmarking()`] for more details. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(198, 19, 255, 255)).is_benchmarking(), true); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0x2, 0, 0, 0, 0, 0, 0)).is_benchmarking(), true); + /// ``` + #[unstable(feature = "ip", issue = "27709")] + #[inline] + pub const fn is_benchmarking(&self) -> bool { + match self { + IpAddr::V4(ip) => ip.is_benchmarking(), + IpAddr::V6(ip) => ip.is_benchmarking(), + } + } + /// Returns [`true`] if this address is an [`IPv4` address], and [`false`] /// otherwise. /// diff --git a/library/std/src/net/ip/tests.rs b/library/std/src/net/ip/tests.rs index 557e04081cadf..ba387c3c048ce 100644 --- a/library/std/src/net/ip/tests.rs +++ b/library/std/src/net/ip/tests.rs @@ -224,6 +224,7 @@ fn ip_properties() { let global: u8 = 1 << 2; let multicast: u8 = 1 << 3; let doc: u8 = 1 << 4; + let benchmarking: u8 = 1 << 5; if ($mask & unspec) == unspec { assert!(ip!($s).is_unspecified()); @@ -254,6 +255,12 @@ fn ip_properties() { } else { assert!(!ip!($s).is_documentation()); } + + if ($mask & benchmarking) == benchmarking { + assert!(ip!($s).is_benchmarking()); + } else { + assert!(!ip!($s).is_benchmarking()); + } }}; } @@ -262,6 +269,7 @@ fn ip_properties() { let global: u8 = 1 << 2; let multicast: u8 = 1 << 3; let doc: u8 = 1 << 4; + let benchmarking: u8 = 1 << 5; check!("0.0.0.0", unspec); check!("0.0.0.1"); @@ -280,9 +288,9 @@ fn ip_properties() { check!("239.255.255.255", global | multicast); check!("255.255.255.255"); // make sure benchmarking addresses are not global - check!("198.18.0.0"); - check!("198.18.54.2"); - check!("198.19.255.255"); + check!("198.18.0.0", benchmarking); + check!("198.18.54.2", benchmarking); + check!("198.19.255.255", benchmarking); // make sure addresses reserved for protocol assignment are not global check!("192.0.0.0"); check!("192.0.0.255"); @@ -313,6 +321,7 @@ fn ip_properties() { check!("ff08::", multicast); check!("ff0e::", global | multicast); check!("2001:db8:85a3::8a2e:370:7334", doc); + check!("2001:2::ac32:23ff:21", global | benchmarking); check!("102:304:506:708:90a:b0c:d0e:f10", global); } From c0e01718809abcf98fd7e7a5e3afba1c11738721 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 29 Jul 2021 12:10:18 +0200 Subject: [PATCH 009/621] Rename two lints to comply with our lint naming convention self_named_constructor -> self_named_constructors append_instead_of_extend -> extend_with_drain --- CHANGELOG.md | 4 ++-- clippy_lints/src/lib.rs | 16 ++++++++-------- ...instead_of_extend.rs => extend_with_drain.rs} | 4 ++-- clippy_lints/src/methods/mod.rs | 8 ++++---- ...constructor.rs => self_named_constructors.rs} | 8 ++++---- ...d_of_extend.fixed => extend_with_drain.fixed} | 2 +- ...instead_of_extend.rs => extend_with_drain.rs} | 2 +- ...of_extend.stderr => extend_with_drain.stderr} | 8 ++++---- tests/ui/needless_bool/fixable.fixed | 2 +- tests/ui/needless_bool/fixable.rs | 2 +- ...constructor.rs => self_named_constructors.rs} | 2 +- ...tor.stderr => self_named_constructors.stderr} | 4 ++-- tests/ui/unit_arg.rs | 2 +- tests/ui/use_self.fixed | 2 +- tests/ui/use_self.rs | 2 +- 15 files changed, 34 insertions(+), 34 deletions(-) rename clippy_lints/src/methods/{append_instead_of_extend.rs => extend_with_drain.rs} (95%) rename clippy_lints/src/{self_named_constructor.rs => self_named_constructors.rs} (92%) rename tests/ui/{append_instead_of_extend.fixed => extend_with_drain.fixed} (96%) rename tests/ui/{append_instead_of_extend.rs => extend_with_drain.rs} (96%) rename tests/ui/{append_instead_of_extend.stderr => extend_with_drain.stderr} (77%) rename tests/ui/{self_named_constructor.rs => self_named_constructors.rs} (96%) rename tests/ui/{self_named_constructor.stderr => self_named_constructors.stderr} (65%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e00dec2e775f..acbefc8064dda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2423,7 +2423,6 @@ Released 2018-09-13 [`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons [`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped -[`append_instead_of_extend`]: https://rust-lang.github.io/rust-clippy/master/index.html#append_instead_of_extend [`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant [`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions [`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants @@ -2522,6 +2521,7 @@ Released 2018-09-13 [`explicit_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop [`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write [`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice +[`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain [`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes [`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from [`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default @@ -2772,7 +2772,7 @@ Released 2018-09-13 [`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some [`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment -[`self_named_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructor +[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors [`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index aa763b5c5e666..2a1b6d0280b9b 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -330,7 +330,7 @@ mod regex; mod repeat_once; mod returns; mod self_assignment; -mod self_named_constructor; +mod self_named_constructors; mod semicolon_if_nothing_returned; mod serde_api; mod shadow; @@ -741,7 +741,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: mem_replace::MEM_REPLACE_OPTION_WITH_NONE, mem_replace::MEM_REPLACE_WITH_DEFAULT, mem_replace::MEM_REPLACE_WITH_UNINIT, - methods::APPEND_INSTEAD_OF_EXTEND, methods::BIND_INSTEAD_OF_MAP, methods::BYTES_NTH, methods::CHARS_LAST_CMP, @@ -752,6 +751,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: methods::CLONE_ON_REF_PTR, methods::EXPECT_FUN_CALL, methods::EXPECT_USED, + methods::EXTEND_WITH_DRAIN, methods::FILETYPE_IS_FILE, methods::FILTER_MAP_IDENTITY, methods::FILTER_MAP_NEXT, @@ -901,7 +901,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: returns::LET_AND_RETURN, returns::NEEDLESS_RETURN, self_assignment::SELF_ASSIGNMENT, - self_named_constructor::SELF_NAMED_CONSTRUCTOR, + self_named_constructors::SELF_NAMED_CONSTRUCTORS, semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED, serde_api::SERDE_API_MISUSE, shadow::SHADOW_REUSE, @@ -1297,7 +1297,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE), LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT), LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), - LintId::of(methods::APPEND_INSTEAD_OF_EXTEND), LintId::of(methods::BIND_INSTEAD_OF_MAP), LintId::of(methods::BYTES_NTH), LintId::of(methods::CHARS_LAST_CMP), @@ -1305,6 +1304,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(methods::CLONE_DOUBLE_REF), LintId::of(methods::CLONE_ON_COPY), LintId::of(methods::EXPECT_FUN_CALL), + LintId::of(methods::EXTEND_WITH_DRAIN), LintId::of(methods::FILTER_MAP_IDENTITY), LintId::of(methods::FILTER_NEXT), LintId::of(methods::FLAT_MAP_IDENTITY), @@ -1408,7 +1408,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(returns::LET_AND_RETURN), LintId::of(returns::NEEDLESS_RETURN), LintId::of(self_assignment::SELF_ASSIGNMENT), - LintId::of(self_named_constructor::SELF_NAMED_CONSTRUCTOR), + LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), LintId::of(serde_api::SERDE_API_MISUSE), LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), @@ -1562,7 +1562,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), LintId::of(returns::LET_AND_RETURN), LintId::of(returns::NEEDLESS_RETURN), - LintId::of(self_named_constructor::SELF_NAMED_CONSTRUCTOR), + LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), @@ -1763,8 +1763,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(loops::MANUAL_MEMCPY), LintId::of(loops::NEEDLESS_COLLECT), - LintId::of(methods::APPEND_INSTEAD_OF_EXTEND), LintId::of(methods::EXPECT_FUN_CALL), + LintId::of(methods::EXTEND_WITH_DRAIN), LintId::of(methods::ITER_NTH), LintId::of(methods::MANUAL_STR_REPEAT), LintId::of(methods::OR_FUN_CALL), @@ -2105,7 +2105,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let scripts = conf.allowed_scripts.clone(); store.register_early_pass(move || box disallowed_script_idents::DisallowedScriptIdents::new(&scripts)); store.register_late_pass(|| box strlen_on_c_strings::StrlenOnCStrings); - store.register_late_pass(move || box self_named_constructor::SelfNamedConstructor); + store.register_late_pass(move || box self_named_constructors::SelfNamedConstructors); } #[rustfmt::skip] diff --git a/clippy_lints/src/methods/append_instead_of_extend.rs b/clippy_lints/src/methods/extend_with_drain.rs similarity index 95% rename from clippy_lints/src/methods/append_instead_of_extend.rs rename to clippy_lints/src/methods/extend_with_drain.rs index e39a5a1efd1e1..57e10ce42f8bb 100644 --- a/clippy_lints/src/methods/append_instead_of_extend.rs +++ b/clippy_lints/src/methods/extend_with_drain.rs @@ -7,7 +7,7 @@ use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; use rustc_span::symbol::sym; -use super::APPEND_INSTEAD_OF_EXTEND; +use super::EXTEND_WITH_DRAIN; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); @@ -25,7 +25,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, - APPEND_INSTEAD_OF_EXTEND, + EXTEND_WITH_DRAIN, expr.span, "use of `extend` instead of `append` for adding the full range of a second vector", "try this", diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 283fcf281df18..50e558b718ab3 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1,4 +1,3 @@ -mod append_instead_of_extend; mod bind_instead_of_map; mod bytes_nth; mod chars_cmp; @@ -12,6 +11,7 @@ mod clone_on_ref_ptr; mod cloned_instead_of_copied; mod expect_fun_call; mod expect_used; +mod extend_with_drain; mod filetype_is_file; mod filter_map; mod filter_map_identity; @@ -1052,7 +1052,7 @@ declare_clippy_lint! { /// // Good /// a.append(&mut b); /// ``` - pub APPEND_INSTEAD_OF_EXTEND, + pub EXTEND_WITH_DRAIN, perf, "using vec.append(&mut vec) to move the full range of a vecor to another" } @@ -1811,7 +1811,7 @@ impl_lint_pass!(Methods => [ IMPLICIT_CLONE, SUSPICIOUS_SPLITN, MANUAL_STR_REPEAT, - APPEND_INSTEAD_OF_EXTEND + EXTEND_WITH_DRAIN ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2075,7 +2075,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, ("extend", [arg]) => { string_extend_chars::check(cx, expr, recv, arg); - append_instead_of_extend::check(cx, expr, recv, arg); + extend_with_drain::check(cx, expr, recv, arg); }, ("filter_map", [arg]) => { unnecessary_filter_map::check(cx, expr, arg); diff --git a/clippy_lints/src/self_named_constructor.rs b/clippy_lints/src/self_named_constructors.rs similarity index 92% rename from clippy_lints/src/self_named_constructor.rs rename to clippy_lints/src/self_named_constructors.rs index da991e1d90c8e..d9df3964af2a4 100644 --- a/clippy_lints/src/self_named_constructor.rs +++ b/clippy_lints/src/self_named_constructors.rs @@ -33,14 +33,14 @@ declare_clippy_lint! { /// } /// } /// ``` - pub SELF_NAMED_CONSTRUCTOR, + pub SELF_NAMED_CONSTRUCTORS, style, "method should not have the same name as the type it is implemented for" } -declare_lint_pass!(SelfNamedConstructor => [SELF_NAMED_CONSTRUCTOR]); +declare_lint_pass!(SelfNamedConstructors => [SELF_NAMED_CONSTRUCTORS]); -impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructor { +impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { match impl_item.kind { ImplItemKind::Fn(ref sig, _) => { @@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructor { then { span_lint( cx, - SELF_NAMED_CONSTRUCTOR, + SELF_NAMED_CONSTRUCTORS, impl_item.span, &format!("constructor `{}` has the same name as the type", impl_item.ident.name), ); diff --git a/tests/ui/append_instead_of_extend.fixed b/tests/ui/extend_with_drain.fixed similarity index 96% rename from tests/ui/append_instead_of_extend.fixed rename to tests/ui/extend_with_drain.fixed index 283358333cdfd..00170e649e251 100644 --- a/tests/ui/append_instead_of_extend.fixed +++ b/tests/ui/extend_with_drain.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::append_instead_of_extend)] +#![warn(clippy::extend_with_drain)] use std::collections::BinaryHeap; fn main() { //gets linted diff --git a/tests/ui/append_instead_of_extend.rs b/tests/ui/extend_with_drain.rs similarity index 96% rename from tests/ui/append_instead_of_extend.rs rename to tests/ui/extend_with_drain.rs index abde5cdac5cf7..d76458c32891a 100644 --- a/tests/ui/append_instead_of_extend.rs +++ b/tests/ui/extend_with_drain.rs @@ -1,5 +1,5 @@ // run-rustfix -#![warn(clippy::append_instead_of_extend)] +#![warn(clippy::extend_with_drain)] use std::collections::BinaryHeap; fn main() { //gets linted diff --git a/tests/ui/append_instead_of_extend.stderr b/tests/ui/extend_with_drain.stderr similarity index 77% rename from tests/ui/append_instead_of_extend.stderr rename to tests/ui/extend_with_drain.stderr index 9d309d981def9..57f344716a16d 100644 --- a/tests/ui/append_instead_of_extend.stderr +++ b/tests/ui/extend_with_drain.stderr @@ -1,19 +1,19 @@ error: use of `extend` instead of `append` for adding the full range of a second vector - --> $DIR/append_instead_of_extend.rs:9:5 + --> $DIR/extend_with_drain.rs:9:5 | LL | vec2.extend(vec1.drain(..)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec2.append(&mut vec1)` | - = note: `-D clippy::append-instead-of-extend` implied by `-D warnings` + = note: `-D clippy::extend-with-drain` implied by `-D warnings` error: use of `extend` instead of `append` for adding the full range of a second vector - --> $DIR/append_instead_of_extend.rs:14:5 + --> $DIR/extend_with_drain.rs:14:5 | LL | vec4.extend(vec3.drain(..)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec4.append(&mut vec3)` error: use of `extend` instead of `append` for adding the full range of a second vector - --> $DIR/append_instead_of_extend.rs:18:5 + --> $DIR/extend_with_drain.rs:18:5 | LL | vec11.extend(return_vector().drain(..)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec11.append(&mut return_vector())` diff --git a/tests/ui/needless_bool/fixable.fixed b/tests/ui/needless_bool/fixable.fixed index 5917ffc3e12ea..639eac8b8b315 100644 --- a/tests/ui/needless_bool/fixable.fixed +++ b/tests/ui/needless_bool/fixable.fixed @@ -7,7 +7,7 @@ clippy::no_effect, clippy::if_same_then_else, clippy::needless_return, - clippy::self_named_constructor + clippy::self_named_constructors )] use std::cell::Cell; diff --git a/tests/ui/needless_bool/fixable.rs b/tests/ui/needless_bool/fixable.rs index d26dcb9fcc33e..a3ce086a1c90a 100644 --- a/tests/ui/needless_bool/fixable.rs +++ b/tests/ui/needless_bool/fixable.rs @@ -7,7 +7,7 @@ clippy::no_effect, clippy::if_same_then_else, clippy::needless_return, - clippy::self_named_constructor + clippy::self_named_constructors )] use std::cell::Cell; diff --git a/tests/ui/self_named_constructor.rs b/tests/ui/self_named_constructors.rs similarity index 96% rename from tests/ui/self_named_constructor.rs rename to tests/ui/self_named_constructors.rs index 7658b86a8d6d4..356f701c98544 100644 --- a/tests/ui/self_named_constructor.rs +++ b/tests/ui/self_named_constructors.rs @@ -1,4 +1,4 @@ -#![warn(clippy::self_named_constructor)] +#![warn(clippy::self_named_constructors)] struct ShouldSpawn; struct ShouldNotSpawn; diff --git a/tests/ui/self_named_constructor.stderr b/tests/ui/self_named_constructors.stderr similarity index 65% rename from tests/ui/self_named_constructor.stderr rename to tests/ui/self_named_constructors.stderr index 1e2c34ac2f7b9..ba989f06dc80d 100644 --- a/tests/ui/self_named_constructor.stderr +++ b/tests/ui/self_named_constructors.stderr @@ -1,12 +1,12 @@ error: constructor `should_spawn` has the same name as the type - --> $DIR/self_named_constructor.rs:7:5 + --> $DIR/self_named_constructors.rs:7:5 | LL | / pub fn should_spawn() -> ShouldSpawn { LL | | ShouldSpawn LL | | } | |_____^ | - = note: `-D clippy::self-named-constructor` implied by `-D warnings` + = note: `-D clippy::self-named-constructors` implied by `-D warnings` error: aborting due to previous error diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index df0fdaccb344a..535683729f686 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -7,7 +7,7 @@ clippy::unnecessary_wraps, clippy::or_fun_call, clippy::needless_question_mark, - clippy::self_named_constructor + clippy::self_named_constructors )] use std::fmt::Debug; diff --git a/tests/ui/use_self.fixed b/tests/ui/use_self.fixed index 23fc7632511c2..dcf818f807630 100644 --- a/tests/ui/use_self.fixed +++ b/tests/ui/use_self.fixed @@ -8,7 +8,7 @@ clippy::should_implement_trait, clippy::upper_case_acronyms, clippy::from_over_into, - clippy::self_named_constructor + clippy::self_named_constructors )] #[macro_use] diff --git a/tests/ui/use_self.rs b/tests/ui/use_self.rs index bb46a33992371..9da6fef7a380c 100644 --- a/tests/ui/use_self.rs +++ b/tests/ui/use_self.rs @@ -8,7 +8,7 @@ clippy::should_implement_trait, clippy::upper_case_acronyms, clippy::from_over_into, - clippy::self_named_constructor + clippy::self_named_constructors )] #[macro_use] From cb4519e59c75b848e808a28f2046c8bc2f91b943 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 30 Jul 2021 17:28:44 +0100 Subject: [PATCH 010/621] os current_exe using same approach as linux to get always the full absolute path but in case of failure (e.g. prcfs not mounted) still using getexecname. --- library/std/src/sys/unix/os.rs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index cc0802ed709cb..14f0747bedcc3 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -368,20 +368,24 @@ pub fn current_exe() -> io::Result { #[cfg(any(target_os = "solaris", target_os = "illumos"))] pub fn current_exe() -> io::Result { - extern "C" { - fn getexecname() -> *const c_char; - } - unsafe { - let path = getexecname(); - if path.is_null() { - Err(io::Error::last_os_error()) - } else { - let filename = CStr::from_ptr(path).to_bytes(); - let path = PathBuf::from(::from_bytes(filename)); + if let Ok(path) = crate::fs::read_link("/proc/self/path/a.out") { + Ok(path) + } else { + extern "C" { + fn getexecname() -> *const c_char; + } + unsafe { + let path = getexecname(); + if path.is_null() { + Err(io::Error::last_os_error()) + } else { + let filename = CStr::from_ptr(path).to_bytes(); + let path = PathBuf::from(::from_bytes(filename)); - // Prepend a current working directory to the path if - // it doesn't contain an absolute pathname. - if filename[0] == b'/' { Ok(path) } else { getcwd().map(|cwd| cwd.join(path)) } + // Prepend a current working directory to the path if + // it doesn't contain an absolute pathname. + if filename[0] == b'/' { Ok(path) } else { getcwd().map(|cwd| cwd.join(path)) } + } } } } From 47c50560146fb8deebb3410765f66be4dc87cf81 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 24 Jul 2021 16:08:11 +0200 Subject: [PATCH 011/621] Fix highlighting for union keyword --- src/librustdoc/html/highlight.rs | 70 +++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 3cdb1352bef95..930bdf6d3fa2f 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -8,8 +8,8 @@ use crate::html::escape::Escape; use crate::html::render::Context; +use std::collections::VecDeque; use std::fmt::{Display, Write}; -use std::iter::Peekable; use rustc_lexer::{LiteralKind, TokenKind}; use rustc_span::edition::Edition; @@ -199,10 +199,57 @@ fn get_real_ident_class(text: &str, edition: Edition, allow_path_keywords: bool) }) } +/// This iterator comes from the same idea than "Peekable" except that it allows to "peek" more than +/// just the next item by using `peek_next`. The `peek` method always returns the next item after +/// the current one whereas `peek_next` will return the next item after the last one peeked. +/// +/// You can use both `peek` and `peek_next` at the same time without problem. +struct PeekIter<'a> { + stored: VecDeque<(TokenKind, &'a str)>, + /// This position is reinitialized when using `next`. It is used in `peek_next`. + peek_pos: usize, + iter: TokenIter<'a>, +} + +impl PeekIter<'a> { + fn new(iter: TokenIter<'a>) -> Self { + Self { stored: VecDeque::new(), peek_pos: 0, iter } + } + /// Returns the next item after the current one. It doesn't interfer with `peek_next` output. + fn peek(&mut self) -> Option<&(TokenKind, &'a str)> { + if self.stored.is_empty() { + if let Some(next) = self.iter.next() { + self.stored.push_back(next); + } + } + self.stored.front() + } + /// Returns the next item after the last one peeked. It doesn't interfer with `peek` output. + fn peek_next(&mut self) -> Option<&(TokenKind, &'a str)> { + self.peek_pos += 1; + if self.peek_pos - 1 < self.stored.len() { + self.stored.get(self.peek_pos - 1) + } else if let Some(next) = self.iter.next() { + self.stored.push_back(next); + self.stored.back() + } else { + None + } + } +} + +impl Iterator for PeekIter<'a> { + type Item = (TokenKind, &'a str); + fn next(&mut self) -> Option { + self.peek_pos = 0; + if let Some(first) = self.stored.pop_front() { Some(first) } else { self.iter.next() } + } +} + /// Processes program tokens, classifying strings of text by highlighting /// category (`Class`). struct Classifier<'a> { - tokens: Peekable>, + tokens: PeekIter<'a>, in_attribute: bool, in_macro: bool, in_macro_nonterminal: bool, @@ -216,7 +263,7 @@ impl<'a> Classifier<'a> { /// Takes as argument the source code to HTML-ify, the rust edition to use and the source code /// file span which will be used later on by the `span_correspondance_map`. fn new(src: &str, edition: Edition, file_span: Span) -> Classifier<'_> { - let tokens = TokenIter { src }.peekable(); + let tokens = PeekIter::new(TokenIter { src }); Classifier { tokens, in_attribute: false, @@ -367,7 +414,7 @@ impl<'a> Classifier<'a> { // Assume that '&' or '*' is the reference or dereference operator // or a reference or pointer type. Unless, of course, it looks like // a logical and or a multiplication operator: `&&` or `* `. - TokenKind::Star => match lookahead { + TokenKind::Star => match self.peek() { Some(TokenKind::Whitespace) => Class::Op, _ => Class::RefKeyWord, }, @@ -478,6 +525,9 @@ impl<'a> Classifier<'a> { None => match text { "Option" | "Result" => Class::PreludeTy, "Some" | "None" | "Ok" | "Err" => Class::PreludeVal, + // "union" is a weak keyword and is only considered as a keyword when declaring + // a union type. + "union" if self.check_if_is_union_keyword() => Class::KeyWord, _ if self.in_macro_nonterminal => { self.in_macro_nonterminal = false; Class::MacroNonTerminal @@ -498,7 +548,17 @@ impl<'a> Classifier<'a> { } fn peek(&mut self) -> Option { - self.tokens.peek().map(|(toke_kind, _text)| *toke_kind) + self.tokens.peek().map(|(token_kind, _text)| *token_kind) + } + + fn check_if_is_union_keyword(&mut self) -> bool { + while let Some(kind) = self.tokens.peek_next().map(|(token_kind, _text)| token_kind) { + if *kind == TokenKind::Whitespace { + continue; + } + return *kind == TokenKind::Ident; + } + false } } From ee3811671b8f4b7e6d1498a408182e2586d22613 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 24 Jul 2021 16:08:27 +0200 Subject: [PATCH 012/621] Add test for union keyword highlighting --- src/librustdoc/html/highlight/fixtures/union.html | 8 ++++++++ src/librustdoc/html/highlight/fixtures/union.rs | 8 ++++++++ src/librustdoc/html/highlight/tests.rs | 10 ++++++++++ 3 files changed, 26 insertions(+) create mode 100644 src/librustdoc/html/highlight/fixtures/union.html create mode 100644 src/librustdoc/html/highlight/fixtures/union.rs diff --git a/src/librustdoc/html/highlight/fixtures/union.html b/src/librustdoc/html/highlight/fixtures/union.html new file mode 100644 index 0000000000000..c0acf31a05d08 --- /dev/null +++ b/src/librustdoc/html/highlight/fixtures/union.html @@ -0,0 +1,8 @@ +union Foo { + i: i8, + u: i8, +} + +fn main() { + let union = 0; +} diff --git a/src/librustdoc/html/highlight/fixtures/union.rs b/src/librustdoc/html/highlight/fixtures/union.rs new file mode 100644 index 0000000000000..269ee115d3f8f --- /dev/null +++ b/src/librustdoc/html/highlight/fixtures/union.rs @@ -0,0 +1,8 @@ +union Foo { + i: i8, + u: i8, +} + +fn main() { + let union = 0; +} diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs index 68592ae96c187..450bbfea1ea86 100644 --- a/src/librustdoc/html/highlight/tests.rs +++ b/src/librustdoc/html/highlight/tests.rs @@ -54,3 +54,13 @@ let y = Self::whatever;"; expect_file!["fixtures/highlight.html"].assert_eq(&html.into_inner()); }); } + +#[test] +fn test_union_highlighting() { + create_default_session_globals_then(|| { + let src = include_str!("fixtures/union.rs"); + let mut html = Buffer::new(); + write_code(&mut html, src, Edition::Edition2018, None); + expect_file!["fixtures/union.html"].assert_eq(&html.into_inner()); + }); +} From d2201598100135a6007381139c3a1ec598e9a888 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 6 Aug 2021 16:26:56 +0200 Subject: [PATCH 013/621] Merge commit '05677b6bd6c938ed760835d9b1f6514992654ae3' into sync_cg_clif-2021-08-06 --- .github/workflows/main.yml | 2 + .gitignore | 1 + Cargo.lock | 48 ++-- Cargo.toml | 19 +- build_sysroot/Cargo.lock | 17 +- build_system/build_backend.rs | 2 +- build_system/build_sysroot.rs | 4 +- build_system/prepare.rs | 13 +- clean_all.sh | 2 +- docs/usage.md | 4 +- ...01-stdsimd-Disable-unsupported-tests.patch | 165 +++++++++++++ ...-sysroot-Disable-not-compiling-tests.patch | 8 +- .../0023-sysroot-Ignore-failing-tests.patch | 40 --- ...027-sysroot-128bit-atomic-operations.patch | 64 ++--- rust-toolchain | 2 +- scripts/cargo.rs | 12 +- scripts/filter_profile.rs | 2 +- scripts/setup_rust_fork.sh | 2 +- scripts/test_rustc_tests.sh | 1 - scripts/tests.sh | 17 +- src/abi/mod.rs | 230 +++++++++--------- src/abi/pass_mode.rs | 20 +- src/abi/returning.rs | 90 +++---- src/allocator.rs | 10 +- src/analyze.rs | 11 - src/base.rs | 2 - src/cast.rs | 19 -- src/codegen_i128.rs | 116 ++++----- src/debuginfo/emit.rs | 2 +- src/debuginfo/mod.rs | 2 +- src/intrinsics/mod.rs | 111 ++++----- src/intrinsics/simd.rs | 190 +++++++++++++-- src/lib.rs | 26 +- src/num.rs | 25 +- src/optimize/peephole.rs | 41 +--- src/trap.rs | 2 +- src/unsize.rs | 2 - src/value_and_place.rs | 77 +++++- src/vtable.rs | 8 +- y.rs | 4 +- 40 files changed, 823 insertions(+), 590 deletions(-) create mode 100644 patches/0001-stdsimd-Disable-unsupported-tests.patch diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f81ac87726052..f524b42c5eecd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -49,12 +49,14 @@ jobs: - name: Install MinGW toolchain and wine if: matrix.os == 'ubuntu-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu' run: | + sudo apt-get update sudo apt-get install -y gcc-mingw-w64-x86-64 wine-stable rustup target add x86_64-pc-windows-gnu - name: Install AArch64 toolchain and qemu if: matrix.os == 'ubuntu-latest' && matrix.env.TARGET_TRIPLE == 'aarch64-unknown-linux-gnu' run: | + sudo apt-get update sudo apt-get install -y gcc-aarch64-linux-gnu qemu-user - name: Prepare dependencies diff --git a/.gitignore b/.gitignore index 12e779fe7c7d7..25080488a88b5 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ perf.data.old /rand /regex /simple-raytracer +/stdsimd diff --git a/Cargo.lock b/Cargo.lock index 56d0974b25371..23c1fdc6ee425 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "anyhow" -version = "1.0.38" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" +checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486" [[package]] name = "ar" @@ -34,7 +34,7 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git?branch=main#c71ad9490e7f3e19bbcae7e28bbe50f8a0b4a5d8" +source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" dependencies = [ "cranelift-entity", ] @@ -42,7 +42,7 @@ dependencies = [ [[package]] name = "cranelift-codegen" version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git?branch=main#c71ad9490e7f3e19bbcae7e28bbe50f8a0b4a5d8" +source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" dependencies = [ "cranelift-bforest", "cranelift-codegen-meta", @@ -58,7 +58,7 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git?branch=main#c71ad9490e7f3e19bbcae7e28bbe50f8a0b4a5d8" +source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" dependencies = [ "cranelift-codegen-shared", "cranelift-entity", @@ -67,17 +67,17 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git?branch=main#c71ad9490e7f3e19bbcae7e28bbe50f8a0b4a5d8" +source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" [[package]] name = "cranelift-entity" version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git?branch=main#c71ad9490e7f3e19bbcae7e28bbe50f8a0b4a5d8" +source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" [[package]] name = "cranelift-frontend" version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git?branch=main#c71ad9490e7f3e19bbcae7e28bbe50f8a0b4a5d8" +source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" dependencies = [ "cranelift-codegen", "log", @@ -88,7 +88,7 @@ dependencies = [ [[package]] name = "cranelift-jit" version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git?branch=main#c71ad9490e7f3e19bbcae7e28bbe50f8a0b4a5d8" +source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" dependencies = [ "anyhow", "cranelift-codegen", @@ -105,7 +105,7 @@ dependencies = [ [[package]] name = "cranelift-module" version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git?branch=main#c71ad9490e7f3e19bbcae7e28bbe50f8a0b4a5d8" +source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" dependencies = [ "anyhow", "cranelift-codegen", @@ -116,7 +116,7 @@ dependencies = [ [[package]] name = "cranelift-native" version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git?branch=main#c71ad9490e7f3e19bbcae7e28bbe50f8a0b4a5d8" +source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" dependencies = [ "cranelift-codegen", "libc", @@ -126,7 +126,7 @@ dependencies = [ [[package]] name = "cranelift-object" version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git?branch=main#c71ad9490e7f3e19bbcae7e28bbe50f8a0b4a5d8" +source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" dependencies = [ "anyhow", "cranelift-codegen", @@ -147,24 +147,24 @@ dependencies = [ [[package]] name = "gimli" -version = "0.24.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189" +checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7" dependencies = [ "indexmap", ] [[package]] name = "hashbrown" -version = "0.9.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" [[package]] name = "indexmap" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" dependencies = [ "autocfg", "hashbrown", @@ -172,9 +172,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.97" +version = "0.2.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" +checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" [[package]] name = "libloading" @@ -212,9 +212,9 @@ checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" [[package]] name = "object" -version = "0.25.3" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a38f2be3697a57b4060074ff41b44c16870d916ad7877c17696e063257482bc7" +checksum = "c55827317fb4c08822499848a14237d2874d6f139828893017237e7ab93eb386" dependencies = [ "crc32fast", "indexmap", @@ -277,9 +277,9 @@ checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] name = "target-lexicon" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ae3b39281e4b14b8123bdbaddd472b7dfe215e444181f2f9d2443c2444f834" +checksum = "b0652da4c4121005e9ed22b79f6c5f2d9e2752906b53a33e9490489ba421a6fb" [[package]] name = "winapi" diff --git a/Cargo.toml b/Cargo.toml index 6593ac738fe84..6f40fc0fcb881 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,15 +8,15 @@ crate-type = ["dylib"] [dependencies] # These have to be in sync with each other -cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "main", features = ["unwind", "all-arch"] } -cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "main" } -cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "main" } -cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "main" } -cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "main", optional = true } -cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git", branch = "main" } +cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime.git", features = ["unwind", "all-arch"] } +cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime.git" } +cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime.git" } +cranelift-native = { git = "https://github.com/bytecodealliance/wasmtime.git" } +cranelift-jit = { git = "https://github.com/bytecodealliance/wasmtime.git", optional = true } +cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime.git" } target-lexicon = "0.12.0" -gimli = { version = "0.24.0", default-features = false, features = ["write"]} -object = { version = "0.25.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } +gimli = { version = "0.25.0", default-features = false, features = ["write"]} +object = { version = "0.26.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] } ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "do_not_remove_cg_clif_ranlib" } indexmap = "1.0.2" @@ -36,7 +36,8 @@ smallvec = "1.6.1" #gimli = { path = "../" } [features] -default = ["jit", "inline_asm"] +# Enable features not ready to be enabled when compiling as part of rustc +unstable-features = ["jit", "inline_asm"] jit = ["cranelift-jit", "libloading"] inline_asm = [] diff --git a/build_sysroot/Cargo.lock b/build_sysroot/Cargo.lock index 46f661107e73b..e068f084234bc 100644 --- a/build_sysroot/Cargo.lock +++ b/build_sysroot/Cargo.lock @@ -40,9 +40,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "cc" -version = "1.0.68" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" +checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" [[package]] name = "cfg-if" @@ -132,9 +132,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.97" +version = "0.2.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" +checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" dependencies = [ "rustc-std-workspace-core", ] @@ -270,14 +270,6 @@ dependencies = [ "test", ] -[[package]] -name = "term" -version = "0.0.0" -dependencies = [ - "core", - "std", -] - [[package]] name = "test" version = "0.0.0" @@ -290,7 +282,6 @@ dependencies = [ "panic_unwind", "proc_macro", "std", - "term", ] [[package]] diff --git a/build_system/build_backend.rs b/build_system/build_backend.rs index 1df2bcc4541ca..150b6d01a6b30 100644 --- a/build_system/build_backend.rs +++ b/build_system/build_backend.rs @@ -4,7 +4,7 @@ use std::process::Command; pub(crate) fn build_backend(channel: &str, host_triple: &str) -> PathBuf { let mut cmd = Command::new("cargo"); - cmd.arg("build").arg("--target").arg(host_triple); + cmd.arg("build").arg("--target").arg(host_triple).arg("--features").arg("unstable-features"); match channel { "debug" => {} diff --git a/build_system/build_sysroot.rs b/build_system/build_sysroot.rs index 9fb88c279613f..642abc41f45a7 100644 --- a/build_system/build_sysroot.rs +++ b/build_system/build_sysroot.rs @@ -91,7 +91,9 @@ pub(crate) fn build_sysroot( { let file = file.unwrap().path(); let file_name_str = file.file_name().unwrap().to_str().unwrap(); - if file_name_str.contains("rustc_") + if (file_name_str.contains("rustc_") + && !file_name_str.contains("rustc_std_workspace_") + && !file_name_str.contains("rustc_demangle")) || file_name_str.contains("chalk") || file_name_str.contains("tracing") || file_name_str.contains("regex") diff --git a/build_system/prepare.rs b/build_system/prepare.rs index 401b8271abcc5..4b2051b605abd 100644 --- a/build_system/prepare.rs +++ b/build_system/prepare.rs @@ -27,6 +27,13 @@ pub(crate) fn prepare() { "341f207c1071f7290e3f228c710817c280c8dca1", ); + clone_repo( + "stdsimd", + "https://github.com/rust-lang/stdsimd", + "be96995d8ddec03fac9a0caf4d4c51c7fbc33507", + ); + apply_patches("stdsimd", Path::new("stdsimd")); + clone_repo( "simple-raytracer", "https://github.com/ebobby/simple-raytracer", @@ -60,11 +67,7 @@ fn prepare_sysroot() { copy_dir_recursively(&sysroot_src_orig.join("library"), &sysroot_src.join("library")); let rustc_version = get_rustc_version(); - fs::write( - Path::new("build_sysroot").join("rustc_version"), - &rustc_version, - ) - .unwrap(); + fs::write(Path::new("build_sysroot").join("rustc_version"), &rustc_version).unwrap(); eprintln!("[GIT] init"); let mut git_init_cmd = Command::new("git"); diff --git a/clean_all.sh b/clean_all.sh index f4f8c82d69f10..23e5bf2e0a8fd 100755 --- a/clean_all.sh +++ b/clean_all.sh @@ -3,4 +3,4 @@ set -e rm -rf build_sysroot/{sysroot_src/,target/,compiler-builtins/,rustc_version} rm -rf target/ build/ perf.data{,.old} -rm -rf rand/ regex/ simple-raytracer/ +rm -rf rand/ regex/ simple-raytracer/ stdsimd/ diff --git a/docs/usage.md b/docs/usage.md index 956d5905a97ad..87eec0e818bb2 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -36,7 +36,7 @@ $ $cg_clif_dir/build/cargo jit or ```bash -$ $cg_clif_dir/build/bin/cg_clif -Cllvm-args=mode=jit -Cprefer-dynamic my_crate.rs +$ $cg_clif_dir/build/bin/cg_clif -Zunstable-features -Cllvm-args=mode=jit -Cprefer-dynamic my_crate.rs ``` There is also an experimental lazy jit mode. In this mode functions are only compiled once they are @@ -52,7 +52,7 @@ These are a few functions that allow you to easily run rust code from the shell ```bash function jit_naked() { - echo "$@" | $cg_clif_dir/build/bin/cg_clif - -Cllvm-args=mode=jit -Cprefer-dynamic + echo "$@" | $cg_clif_dir/build/bin/cg_clif - -Zunstable-features -Cllvm-args=mode=jit -Cprefer-dynamic } function jit() { diff --git a/patches/0001-stdsimd-Disable-unsupported-tests.patch b/patches/0001-stdsimd-Disable-unsupported-tests.patch new file mode 100644 index 0000000000000..731c60fda58d6 --- /dev/null +++ b/patches/0001-stdsimd-Disable-unsupported-tests.patch @@ -0,0 +1,165 @@ +From 6bfce5dc2cbf834c74dbccb7538adc08c6eb57e7 Mon Sep 17 00:00:00 2001 +From: bjorn3 +Date: Sun, 25 Jul 2021 18:39:31 +0200 +Subject: [PATCH] Disable unsupported tests + +--- + crates/core_simd/src/array.rs | 2 ++ + crates/core_simd/src/lib.rs | 2 +- + crates/core_simd/src/math.rs | 4 ++++ + crates/core_simd/tests/masks.rs | 12 ------------ + crates/core_simd/tests/ops_macros.rs | 6 ++++++ + crates/core_simd/tests/round.rs | 2 ++ + 6 files changed, 15 insertions(+), 13 deletions(-) + +diff --git a/crates/core_simd/src/array.rs b/crates/core_simd/src/array.rs +index 25c5309..2b3d819 100644 +--- a/crates/core_simd/src/array.rs ++++ b/crates/core_simd/src/array.rs +@@ -22,6 +22,7 @@ where + #[must_use] + fn splat(val: Self::Scalar) -> Self; + ++ /* + /// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices. + /// If an index is out of bounds, that lane instead selects the value from the "or" vector. + /// ``` +@@ -150,6 +151,7 @@ where + // Cleared ☢️ *mut T Zone + } + } ++ */ + } + + macro_rules! impl_simdarray_for { +diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs +index a64904d..299eb11 100644 +--- a/crates/core_simd/src/lib.rs ++++ b/crates/core_simd/src/lib.rs +@@ -1,7 +1,7 @@ + #![no_std] + #![allow(incomplete_features)] + #![feature( +- const_generics, ++ const_generics, + platform_intrinsics, + repr_simd, + simd_ffi, +diff --git a/crates/core_simd/src/math.rs b/crates/core_simd/src/math.rs +index 7290a28..e394730 100644 +--- a/crates/core_simd/src/math.rs ++++ b/crates/core_simd/src/math.rs +@@ -2,6 +2,7 @@ macro_rules! impl_uint_arith { + ($(($name:ident, $n:ident)),+) => { + $( impl $name where Self: crate::LanesAtMost32 { + ++ /* + /// Lanewise saturating add. + /// + /// # Examples +@@ -38,6 +39,7 @@ macro_rules! impl_uint_arith { + pub fn saturating_sub(self, second: Self) -> Self { + unsafe { crate::intrinsics::simd_saturating_sub(self, second) } + } ++ */ + })+ + } + } +@@ -46,6 +48,7 @@ macro_rules! impl_int_arith { + ($(($name:ident, $n:ident)),+) => { + $( impl $name where Self: crate::LanesAtMost32 { + ++ /* + /// Lanewise saturating add. + /// + /// # Examples +@@ -141,6 +144,7 @@ macro_rules! impl_int_arith { + pub fn saturating_neg(self) -> Self { + Self::splat(0).saturating_sub(self) + } ++ */ + })+ + } + } +diff --git a/crates/core_simd/tests/masks.rs b/crates/core_simd/tests/masks.rs +index 61d8e44..2bccae2 100644 +--- a/crates/core_simd/tests/masks.rs ++++ b/crates/core_simd/tests/masks.rs +@@ -67,18 +67,6 @@ macro_rules! test_mask_api { + assert_eq!(int.to_array(), [-1, 0, 0, -1, 0, 0, -1, 0]); + assert_eq!(core_simd::$name::<8>::from_int(int), mask); + } +- +- #[test] +- fn roundtrip_bitmask_conversion() { +- let values = [ +- true, false, false, true, false, false, true, false, +- true, true, false, false, false, false, false, true, +- ]; +- let mask = core_simd::$name::<16>::from_array(values); +- let bitmask = mask.to_bitmask(); +- assert_eq!(bitmask, [0b01001001, 0b10000011]); +- assert_eq!(core_simd::$name::<16>::from_bitmask(bitmask), mask); +- } + } + } + } +diff --git a/crates/core_simd/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs +index cb39e73..fc0ebe1 100644 +--- a/crates/core_simd/tests/ops_macros.rs ++++ b/crates/core_simd/tests/ops_macros.rs +@@ -435,6 +435,7 @@ macro_rules! impl_float_tests { + ) + } + ++ /* + fn mul_add() { + test_helpers::test_ternary_elementwise( + &Vector::::mul_add, +@@ -442,6 +443,7 @@ macro_rules! impl_float_tests { + &|_, _, _| true, + ) + } ++ */ + + fn sqrt() { + test_helpers::test_unary_elementwise( +@@ -581,6 +585,7 @@ macro_rules! impl_float_tests { + }); + } + ++ /* + fn horizontal_max() { + test_helpers::test_1(&|x| { + let vmax = Vector::::from_array(x).horizontal_max(); +@@ -604,6 +609,7 @@ macro_rules! impl_float_tests { + Ok(()) + }); + } ++ */ + } + } + } +diff --git a/crates/core_simd/tests/round.rs b/crates/core_simd/tests/round.rs +index 37044a7..4cdc6b7 100644 +--- a/crates/core_simd/tests/round.rs ++++ b/crates/core_simd/tests/round.rs +@@ -25,6 +25,7 @@ macro_rules! float_rounding_test { + ) + } + ++ /* + fn round() { + test_helpers::test_unary_elementwise( + &Vector::::round, +@@ -32,6 +33,7 @@ macro_rules! float_rounding_test { + &|_| true, + ) + } ++ */ + + fn trunc() { + test_helpers::test_unary_elementwise( +-- +2.26.2.7.g19db9cfb68 + diff --git a/patches/0022-sysroot-Disable-not-compiling-tests.patch b/patches/0022-sysroot-Disable-not-compiling-tests.patch index ba0eaacd82870..25a315f666e27 100644 --- a/patches/0022-sysroot-Disable-not-compiling-tests.patch +++ b/patches/0022-sysroot-Disable-not-compiling-tests.patch @@ -51,14 +51,14 @@ index 1a6be3a..42dbd59 100644 #[test] #[allow(warnings)] // Have a symbol for the test below. It doesn’t 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])); +@@ -277,6 +277,7 @@ pub fn test_variadic_fnptr() { + let mut s = SipHasher::new(); + assert_eq!(p.hash(&mut s), q.hash(&mut s)); } +*/ #[test] - fn align_offset_zst() { + fn write_unaligned_drop() { diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs index 6609bc3..241b497 100644 --- a/library/core/tests/slice.rs diff --git a/patches/0023-sysroot-Ignore-failing-tests.patch b/patches/0023-sysroot-Ignore-failing-tests.patch index 5d2c3049f60eb..50ef0bd9418c7 100644 --- a/patches/0023-sysroot-Ignore-failing-tests.patch +++ b/patches/0023-sysroot-Ignore-failing-tests.patch @@ -46,45 +46,5 @@ index 4bc44e9..8e3c7a4 100644 #[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) diff --git a/patches/0027-sysroot-128bit-atomic-operations.patch b/patches/0027-sysroot-128bit-atomic-operations.patch index 32e5930969061..cda8153083c33 100644 --- a/patches/0027-sysroot-128bit-atomic-operations.patch +++ b/patches/0027-sysroot-128bit-atomic-operations.patch @@ -1,20 +1,44 @@ -From 894e07dfec2624ba539129b1c1d63e1d7d812bda Mon Sep 17 00:00:00 2001 +From 6a4e6f5dc8c8a529a822eb9b57f9e57519595439 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Thu, 18 Feb 2021 18:45:28 +0100 Subject: [PATCH] Disable 128bit atomic operations Cranelift doesn't support them yet --- - library/core/src/sync/atomic.rs | 38 --------------------------------- - library/core/tests/atomic.rs | 4 ---- - library/std/src/panic.rs | 6 ------ + library/core/src/panic/unwind_safe.rs | 6 ----- + library/core/src/sync/atomic.rs | 38 --------------------------- + library/core/tests/atomic.rs | 4 --- 3 files changed, 48 deletions(-) +diff --git a/library/core/src/panic/unwind_safe.rs b/library/core/src/panic/unwind_safe.rs +index 092b7cf..158cf71 100644 +--- a/library/core/src/panic/unwind_safe.rs ++++ b/library/core/src/panic/unwind_safe.rs +@@ -216,9 +216,6 @@ impl RefUnwindSafe for crate::sync::atomic::AtomicI32 {} + #[cfg(target_has_atomic_load_store = "64")] + #[stable(feature = "integer_atomics_stable", since = "1.34.0")] + impl RefUnwindSafe for crate::sync::atomic::AtomicI64 {} +-#[cfg(target_has_atomic_load_store = "128")] +-#[unstable(feature = "integer_atomics", issue = "32976")] +-impl RefUnwindSafe for crate::sync::atomic::AtomicI128 {} + + #[cfg(target_has_atomic_load_store = "ptr")] + #[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] +@@ -235,9 +232,6 @@ impl RefUnwindSafe for crate::sync::atomic::AtomicU32 {} + #[cfg(target_has_atomic_load_store = "64")] + #[stable(feature = "integer_atomics_stable", since = "1.34.0")] + impl RefUnwindSafe for crate::sync::atomic::AtomicU64 {} +-#[cfg(target_has_atomic_load_store = "128")] +-#[unstable(feature = "integer_atomics", issue = "32976")] +-impl RefUnwindSafe for crate::sync::atomic::AtomicU128 {} + + #[cfg(target_has_atomic_load_store = "8")] + #[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs -index 81c9e1d..65c9503 100644 +index 0194c58..25a0038 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs -@@ -2228,44 +2228,6 @@ atomic_int! { +@@ -2229,44 +2229,6 @@ atomic_int! { "AtomicU64::new(0)", u64 AtomicU64 ATOMIC_U64_INIT } @@ -60,10 +84,10 @@ index 81c9e1d..65c9503 100644 macro_rules! atomic_int_ptr_sized { ( $($target_pointer_width:literal $align:literal)* ) => { $( diff --git a/library/core/tests/atomic.rs b/library/core/tests/atomic.rs -index 2d1e449..cb6da5d 100644 +index b735957..ea728b6 100644 --- a/library/core/tests/atomic.rs +++ b/library/core/tests/atomic.rs -@@ -145,10 +145,6 @@ fn atomic_alignment() { +@@ -185,10 +185,6 @@ fn atomic_alignment() { assert_eq!(align_of::(), size_of::()); #[cfg(target_has_atomic = "64")] assert_eq!(align_of::(), size_of::()); @@ -74,30 +98,6 @@ index 2d1e449..cb6da5d 100644 #[cfg(target_has_atomic = "ptr")] assert_eq!(align_of::(), size_of::()); #[cfg(target_has_atomic = "ptr")] -diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs -index 89a822a..779fd88 100644 ---- a/library/std/src/panic.rs -+++ b/library/std/src/panic.rs -@@ -279,9 +279,6 @@ impl RefUnwindSafe for atomic::AtomicI32 {} - #[cfg(target_has_atomic_load_store = "64")] - #[stable(feature = "integer_atomics_stable", since = "1.34.0")] - impl RefUnwindSafe for atomic::AtomicI64 {} --#[cfg(target_has_atomic_load_store = "128")] --#[unstable(feature = "integer_atomics", issue = "32976")] --impl RefUnwindSafe for atomic::AtomicI128 {} - - #[cfg(target_has_atomic_load_store = "ptr")] - #[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] -@@ -298,9 +295,6 @@ impl RefUnwindSafe for atomic::AtomicU32 {} - #[cfg(target_has_atomic_load_store = "64")] - #[stable(feature = "integer_atomics_stable", since = "1.34.0")] - impl RefUnwindSafe for atomic::AtomicU64 {} --#[cfg(target_has_atomic_load_store = "128")] --#[unstable(feature = "integer_atomics", issue = "32976")] --impl RefUnwindSafe for atomic::AtomicU128 {} - - #[cfg(target_has_atomic_load_store = "8")] - #[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] -- 2.26.2.7.g19db9cfb68 diff --git a/rust-toolchain b/rust-toolchain index f806f7bdcd98a..f074ebe7a42e0 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-07-07" +channel = "nightly-2021-08-05" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] diff --git a/scripts/cargo.rs b/scripts/cargo.rs index b7e8dd4497479..89ec8da77d3ec 100644 --- a/scripts/cargo.rs +++ b/scripts/cargo.rs @@ -44,7 +44,11 @@ fn main() { ); std::array::IntoIter::new(["rustc".to_string()]) .chain(env::args().skip(2)) - .chain(["--".to_string(), "-Cllvm-args=mode=jit".to_string()]) + .chain([ + "--".to_string(), + "-Zunstable-features".to_string(), + "-Cllvm-args=mode=jit".to_string(), + ]) .collect() } Some("lazy-jit") => { @@ -54,7 +58,11 @@ fn main() { ); std::array::IntoIter::new(["rustc".to_string()]) .chain(env::args().skip(2)) - .chain(["--".to_string(), "-Cllvm-args=mode=jit-lazy".to_string()]) + .chain([ + "--".to_string(), + "-Zunstable-features".to_string(), + "-Cllvm-args=mode=jit-lazy".to_string(), + ]) .collect() } _ => env::args().skip(1).collect(), diff --git a/scripts/filter_profile.rs b/scripts/filter_profile.rs index 9e196afbe4f57..c4801a0a87b88 100755 --- a/scripts/filter_profile.rs +++ b/scripts/filter_profile.rs @@ -5,7 +5,7 @@ pushd $(dirname "$0")/../ source scripts/config.sh RUSTC="$(pwd)/build/bin/cg_clif" popd -PROFILE=$1 OUTPUT=$2 exec $RUSTC -Cllvm-args=mode=jit -Cprefer-dynamic $0 +PROFILE=$1 OUTPUT=$2 exec $RUSTC -Zunstable-options -Cllvm-args=mode=jit -Cprefer-dynamic $0 #*/ //! This program filters away uninteresting samples and trims uninteresting frames for stackcollapse diff --git a/scripts/setup_rust_fork.sh b/scripts/setup_rust_fork.sh index 52adaaa8de673..ca83e7096b86d 100644 --- a/scripts/setup_rust_fork.sh +++ b/scripts/setup_rust_fork.sh @@ -33,7 +33,7 @@ index d95b5b7f17f..00b6f0e3635 100644 [dependencies] core = { path = "../core" } -compiler_builtins = { version = "0.1.40", features = ['rustc-dep-of-std'] } -+compiler_builtins = { version = "0.1.45", features = ['rustc-dep-of-std', 'no-asm'] } ++compiler_builtins = { version = "0.1.46", features = ['rustc-dep-of-std', 'no-asm'] } [dev-dependencies] rand = "0.7" diff --git a/scripts/test_rustc_tests.sh b/scripts/test_rustc_tests.sh index 2f5c2cf737b05..0ac49dd35740f 100755 --- a/scripts/test_rustc_tests.sh +++ b/scripts/test_rustc_tests.sh @@ -79,7 +79,6 @@ rm src/test/ui/type-alias-impl-trait/cross_crate_ice*.rs # requires removed aux rm src/test/ui/allocator/no_std-alloc-error-handler-default.rs # missing rust_oom definition rm src/test/ui/cfg/cfg-panic.rs -rm src/test/ui/default-alloc-error-hook.rs rm -r src/test/ui/hygiene/ rm -r src/test/ui/polymorphization/ # polymorphization not yet supported diff --git a/scripts/tests.sh b/scripts/tests.sh index 5df04c533a70e..0eef710239bdd 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -16,10 +16,10 @@ function no_sysroot_tests() { if [[ "$JIT_SUPPORTED" = "1" ]]; then echo "[JIT] mini_core_hello_world" - CG_CLIF_JIT_ARGS="abc bcd" $MY_RUSTC -Cllvm-args=mode=jit -Cprefer-dynamic example/mini_core_hello_world.rs --cfg jit --target "$HOST_TRIPLE" + CG_CLIF_JIT_ARGS="abc bcd" $MY_RUSTC -Zunstable-options -Cllvm-args=mode=jit -Cprefer-dynamic example/mini_core_hello_world.rs --cfg jit --target "$HOST_TRIPLE" echo "[JIT-lazy] mini_core_hello_world" - CG_CLIF_JIT_ARGS="abc bcd" $MY_RUSTC -Cllvm-args=mode=jit-lazy -Cprefer-dynamic example/mini_core_hello_world.rs --cfg jit --target "$HOST_TRIPLE" + CG_CLIF_JIT_ARGS="abc bcd" $MY_RUSTC -Zunstable-options -Cllvm-args=mode=jit-lazy -Cprefer-dynamic example/mini_core_hello_world.rs --cfg jit --target "$HOST_TRIPLE" else echo "[JIT] mini_core_hello_world (skipped)" fi @@ -44,10 +44,10 @@ function base_sysroot_tests() { if [[ "$JIT_SUPPORTED" = "1" ]]; then echo "[JIT] std_example" - $MY_RUSTC -Cllvm-args=mode=jit -Cprefer-dynamic example/std_example.rs --target "$HOST_TRIPLE" + $MY_RUSTC -Zunstable-options -Cllvm-args=mode=jit -Cprefer-dynamic example/std_example.rs --target "$HOST_TRIPLE" echo "[JIT-lazy] std_example" - $MY_RUSTC -Cllvm-args=mode=jit-lazy -Cprefer-dynamic example/std_example.rs --target "$HOST_TRIPLE" + $MY_RUSTC -Zunstable-options -Cllvm-args=mode=jit-lazy -Cprefer-dynamic example/std_example.rs --target "$HOST_TRIPLE" else echo "[JIT] std_example (skipped)" fi @@ -136,6 +136,15 @@ function extended_sysroot_tests() { ../build/cargo build --tests --target $TARGET_TRIPLE fi popd + + pushd stdsimd + echo "[TEST] rust-lang/stdsimd" + ../build/cargo clean + ../build/cargo build --all-targets --target $TARGET_TRIPLE + if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then + ../build/cargo test -q + fi + popd } case "$1" in diff --git a/src/abi/mod.rs b/src/abi/mod.rs index 54c8fb0e7b80b..13790409e59f6 100644 --- a/src/abi/mod.rs +++ b/src/abi/mod.rs @@ -9,13 +9,12 @@ use rustc_middle::ty::layout::FnAbiExt; use rustc_target::abi::call::{Conv, FnAbi}; use rustc_target::spec::abi::Abi; -use cranelift_codegen::ir::AbiParam; -use smallvec::smallvec; +use cranelift_codegen::ir::{AbiParam, SigRef}; use self::pass_mode::*; use crate::prelude::*; -pub(crate) use self::returning::{can_return_to_ssa_var, codegen_return}; +pub(crate) use self::returning::codegen_return; fn clif_sig_from_fn_abi<'tcx>( tcx: TyCtxt<'tcx>, @@ -236,27 +235,20 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ // 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()) - }; - - self::comments::add_local_place_comments(fx, place, local); - - assert_eq!(fx.local_map.push(place), local); - continue; - } + // Ownership of the value at the backing storage for an argument is passed to the + // callee per the ABI, 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()) + }; + + self::comments::add_local_place_comments(fx, place, local); + + assert_eq!(fx.local_map.push(place), local); + continue; } } @@ -292,6 +284,22 @@ pub(crate) fn codegen_fn_prelude<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, start_ fx.bcx.ins().jump(*fx.block_map.get(START_BLOCK).unwrap(), &[]); } +struct CallArgument<'tcx> { + value: CValue<'tcx>, + is_owned: bool, +} + +// FIXME avoid intermediate `CValue` before calling `adjust_arg_for_abi` +fn codegen_call_argument_operand<'tcx>( + fx: &mut FunctionCx<'_, '_, 'tcx>, + operand: &Operand<'tcx>, +) -> CallArgument<'tcx> { + CallArgument { + value: codegen_operand(fx, operand), + is_owned: matches!(operand, Operand::Move(_)), + } +} + pub(crate) fn codegen_terminator_call<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, span: Span, @@ -360,12 +368,12 @@ pub(crate) fn codegen_terminator_call<'tcx>( } // Unpack arguments tuple for closures - let args = if fn_sig.abi == Abi::RustCall { + let mut 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 self_arg = codegen_call_argument_operand(fx, &args[0]); + let pack_arg = codegen_call_argument_operand(fx, &args[1]); - let tupled_arguments = match pack_arg.layout().ty.kind() { + let tupled_arguments = match pack_arg.value.layout().ty.kind() { ty::Tuple(ref tupled_arguments) => tupled_arguments, _ => bug!("argument to function with \"rust-call\" ABI is not a tuple"), }; @@ -373,37 +381,53 @@ pub(crate) fn codegen_terminator_call<'tcx>( 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.push(CallArgument { + value: pack_arg.value.value_field(fx, mir::Field::new(i)), + is_owned: pack_arg.is_owned, + }); } args } else { - args.iter().map(|arg| codegen_operand(fx, arg)).collect::>() + args.iter().map(|arg| codegen_call_argument_operand(fx, arg)).collect::>() }; - // | indirect call target - // | | the first argument to be passed - // v v - let (func_ref, first_arg) = match instance { + // Pass the caller location for `#[track_caller]`. + if instance.map(|inst| inst.def.requires_caller_location(fx.tcx)).unwrap_or(false) { + let caller_location = fx.get_caller_location(span); + args.push(CallArgument { value: caller_location, is_owned: false }); + } + + let args = args; + assert_eq!(fn_abi.args.len(), args.len()); + + enum CallTarget { + Direct(FuncRef), + Indirect(SigRef, Value), + } + + let (func_ref, first_arg_override) = match instance { // Trait object call Some(Instance { def: InstanceDef::Virtual(_, idx), .. }) => { if fx.clif_comments.enabled() { let nop_inst = fx.bcx.ins().nop(); fx.add_comment( nop_inst, - format!("virtual call; self arg pass mode: {:?}", &fn_abi.args[0],), + format!("virtual call; self arg pass mode: {:?}", &fn_abi.args[0]), ); } - let (ptr, method) = crate::vtable::get_ptr_and_method_ref(fx, args[0], idx); - (Some(method), smallvec![ptr]) + + let (ptr, method) = crate::vtable::get_ptr_and_method_ref(fx, args[0].value, idx); + let sig = clif_sig_from_fn_abi(fx.tcx, fx.triple(), &fn_abi); + let sig = fx.bcx.import_signature(sig); + + (CallTarget::Indirect(sig, method), Some(ptr)) } // Normal call - Some(_) => ( - None, - args.get(0) - .map(|arg| adjust_arg_for_abi(fx, *arg, &fn_abi.args[0])) - .unwrap_or(smallvec![]), - ), + Some(instance) => { + let func_ref = fx.get_function_ref(instance); + (CallTarget::Direct(func_ref), None) + } // Indirect call None => { @@ -411,80 +435,64 @@ pub(crate) fn codegen_terminator_call<'tcx>( 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, &fn_abi.args[0])) - .unwrap_or(smallvec![]), - ) + let sig = clif_sig_from_fn_abi(fx.tcx, fx.triple(), &fn_abi); + let sig = fx.bcx.import_signature(sig); + + (CallTarget::Indirect(sig, func), None) } }; let ret_place = destination.map(|(place, _)| place); - let (call_inst, call_args) = self::returning::codegen_with_call_return_arg( - fx, - &fn_abi.ret, - ret_place, - |fx, return_ptr| { - let regular_args_count = args.len(); - let mut call_args: Vec = return_ptr - .into_iter() - .chain(first_arg.into_iter()) - .chain( - args.into_iter() - .enumerate() - .skip(1) - .map(|(i, arg)| adjust_arg_for_abi(fx, arg, &fn_abi.args[i]).into_iter()) - .flatten(), - ) - .collect::>(); - - 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, &fn_abi.args[regular_args_count]) - .into_iter(), - ); - assert_eq!(fn_abi.args.len(), regular_args_count + 1); - } else { - assert_eq!(fn_abi.args.len(), regular_args_count); + self::returning::codegen_with_call_return_arg(fx, &fn_abi.ret, ret_place, |fx, return_ptr| { + let call_args = return_ptr + .into_iter() + .chain(first_arg_override.into_iter()) + .chain( + args.into_iter() + .enumerate() + .skip(if first_arg_override.is_some() { 1 } else { 0 }) + .map(|(i, arg)| { + adjust_arg_for_abi(fx, arg.value, &fn_abi.args[i], arg.is_owned).into_iter() + }) + .flatten(), + ) + .collect::>(); + + let call_inst = match func_ref { + CallTarget::Direct(func_ref) => fx.bcx.ins().call(func_ref, &call_args), + CallTarget::Indirect(sig, func_ptr) => { + fx.bcx.ins().call_indirect(sig, func_ptr, &call_args) } + }; - let call_inst = if let Some(func_ref) = func_ref { - let sig = clif_sig_from_fn_abi(fx.tcx, fx.triple(), &fn_abi); - 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 !matches!(fn_sig.abi, Abi::C { .. }) { - fx.tcx.sess.span_fatal(span, &format!("Variadic call for non-C abi {:?}", fn_sig.abi)); + // FIXME find a cleaner way to support varargs + if fn_sig.c_variadic { + if !matches!(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::>(); + fx.bcx.func.dfg.signatures[sig_ref].params = abi_params; } - 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::>(); - fx.bcx.func.dfg.signatures[sig_ref].params = abi_params; - } + + call_inst + }); if let Some((_, dest)) = destination { let ret_block = fx.get_block(dest); @@ -535,7 +543,7 @@ pub(crate) fn codegen_drop<'tcx>( TypeAndMut { ty, mutbl: crate::rustc_hir::Mutability::Mut }, )), ); - let arg_value = adjust_arg_for_abi(fx, arg_value, &fn_abi.args[0]); + let arg_value = adjust_arg_for_abi(fx, arg_value, &fn_abi.args[0], true); let mut call_args: Vec = arg_value.into_iter().collect::>(); @@ -543,7 +551,7 @@ pub(crate) fn codegen_drop<'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, &fn_abi.args[1]).into_iter(), + adjust_arg_for_abi(fx, caller_location, &fn_abi.args[1], false).into_iter(), ); } diff --git a/src/abi/pass_mode.rs b/src/abi/pass_mode.rs index 7c275965199e0..44eae706ea8f6 100644 --- a/src/abi/pass_mode.rs +++ b/src/abi/pass_mode.rs @@ -227,6 +227,7 @@ pub(super) fn adjust_arg_for_abi<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, arg: CValue<'tcx>, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, + is_owned: bool, ) -> SmallVec<[Value; 2]> { assert_assignable(fx, arg.layout().ty, arg_abi.layout.ty); match arg_abi.mode { @@ -237,10 +238,21 @@ pub(super) fn adjust_arg_for_abi<'tcx>( smallvec![a, b] } PassMode::Cast(cast) => to_casted_value(fx, arg, cast), - PassMode::Indirect { .. } => match arg.force_stack(fx) { - (ptr, None) => smallvec![ptr.get_addr(fx)], - (ptr, Some(meta)) => smallvec![ptr.get_addr(fx), meta], - }, + PassMode::Indirect { .. } => { + if is_owned { + match arg.force_stack(fx) { + (ptr, None) => smallvec![ptr.get_addr(fx)], + (ptr, Some(meta)) => smallvec![ptr.get_addr(fx), meta], + } + } else { + // Ownership of the value at the backing storage for an argument is passed to the + // callee per the ABI, so we must make a copy of the argument unless the argument + // local is moved. + let place = CPlace::new_stack_slot(fx, arg.layout()); + place.write_cvalue(fx, arg); + smallvec![place.to_ptr().get_addr(fx)] + } + } } } diff --git a/src/abi/returning.rs b/src/abi/returning.rs index e1c53224b4f84..c1bdba43e6ccb 100644 --- a/src/abi/returning.rs +++ b/src/abi/returning.rs @@ -2,54 +2,9 @@ use crate::prelude::*; -use rustc_middle::ty::layout::FnAbiExt; -use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode}; +use rustc_target::abi::call::{ArgAbi, PassMode}; use smallvec::{smallvec, SmallVec}; -/// 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>( - fx: &FunctionCx<'_, '_, 'tcx>, - func: &mir::Operand<'tcx>, - args: &[mir::Operand<'tcx>], -) -> bool { - 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)); - - // 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); - - match instance.def { - InstanceDef::Intrinsic(_) | InstanceDef::DropGlue(_, _) => { - return true; - } - _ => Some(instance), - } - } else { - None - }; - - let extra_args = &args[fn_sig.inputs().len()..]; - let extra_args = extra_args - .iter() - .map(|op_arg| fx.monomorphize(op_arg.ty(fx.mir, fx.tcx))) - .collect::>(); - let fn_abi = if let Some(instance) = instance { - FnAbi::of_instance(&RevealAllLayoutCx(fx.tcx), instance, &extra_args) - } else { - FnAbi::of_fn_ptr(&RevealAllLayoutCx(fx.tcx), fn_ty.fn_sig(fx.tcx), &extra_args) - }; - match fn_abi.ret.mode { - PassMode::Ignore | PassMode::Direct(_) | PassMode::Pair(_, _) => true, - // FIXME Make it possible to return Cast and Indirect to an ssa var. - PassMode::Cast(_) | PassMode::Indirect { .. } => 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>( @@ -58,8 +13,7 @@ pub(super) fn codegen_return_param<'tcx>( block_params_iter: &mut impl Iterator, ) -> CPlace<'tcx> { let (ret_place, ret_param): (_, SmallVec<[_; 2]>) = match fx.fn_abi.as_ref().unwrap().ret.mode { - PassMode::Ignore => (CPlace::no_place(fx.fn_abi.as_ref().unwrap().ret.layout), smallvec![]), - PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast(_) => { + PassMode::Ignore | PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast(_) => { let is_ssa = ssa_analyzed[RETURN_PLACE] == crate::analyze::SsaKind::Ssa; ( super::make_local_place( @@ -73,7 +27,7 @@ pub(super) fn codegen_return_param<'tcx>( } PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => { let ret_param = block_params_iter.next().unwrap(); - assert_eq!(fx.bcx.func.dfg.value_type(ret_param), pointer_ty(fx.tcx)); + assert_eq!(fx.bcx.func.dfg.value_type(ret_param), fx.pointer_type); ( CPlace::for_ptr(Pointer::new(ret_param), fx.fn_abi.as_ref().unwrap().ret.layout), smallvec![ret_param], @@ -99,25 +53,33 @@ pub(super) fn codegen_return_param<'tcx>( /// 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, T>( +pub(super) fn codegen_with_call_return_arg<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, ret_arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, ret_place: Option>, - f: impl FnOnce(&mut FunctionCx<'_, '_, 'tcx>, Option) -> (Inst, T), -) -> (Inst, T) { - let return_ptr = match ret_arg_abi.mode { - PassMode::Ignore => None, + f: impl FnOnce(&mut FunctionCx<'_, '_, 'tcx>, Option) -> Inst, +) { + let (ret_temp_place, return_ptr) = match ret_arg_abi.mode { + PassMode::Ignore => (None, None), PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => 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 + Some(ret_place) if matches!(ret_place.inner(), CPlaceInner::Addr(_, None)) => { + // This is an optimization to prevent unnecessary copies of the return value when + // the return place is already a memory place as opposed to a register. + // This match arm can be safely removed. + (None, Some(ret_place.to_ptr().get_addr(fx))) + } + _ => { + let place = CPlace::new_stack_slot(fx, ret_arg_abi.layout); + (Some(place), Some(place.to_ptr().get_addr(fx))) + } }, PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => { unreachable!("unsized return value") } - PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast(_) => None, + PassMode::Direct(_) | PassMode::Pair(_, _) | PassMode::Cast(_) => (None, None), }; - let (call_inst, meta) = f(fx, return_ptr); + let call_inst = f(fx, return_ptr); match ret_arg_abi.mode { PassMode::Ignore => {} @@ -150,13 +112,19 @@ pub(super) fn codegen_with_call_return_arg<'tcx, T>( ret_place.write_cvalue(fx, result); } } - PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => {} + PassMode::Indirect { attrs: _, extra_attrs: None, on_stack: _ } => { + if let (Some(ret_place), Some(ret_temp_place)) = (ret_place, ret_temp_place) { + // Both ret_place and ret_temp_place must be Some. If ret_place is None, this is + // a non-returning call. If ret_temp_place is None, it is not necessary to copy the + // return value. + let ret_temp_value = ret_temp_place.to_cvalue(fx); + ret_place.write_cvalue(fx, ret_temp_value); + } + } PassMode::Indirect { attrs: _, extra_attrs: Some(_), on_stack: _ } => { unreachable!("unsized return value") } } - - (call_inst, meta) } /// Codegen a return instruction with the right return value(s) if any. diff --git a/src/allocator.rs b/src/allocator.rs index d39486c2f1002..637d30f9344f9 100644 --- a/src/allocator.rs +++ b/src/allocator.rs @@ -5,7 +5,6 @@ use crate::prelude::*; use cranelift_codegen::binemit::{NullStackMapSink, NullTrapSink}; 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( @@ -20,7 +19,7 @@ pub(crate) fn codegen( if any_dynamic_crate { false } else if let Some(kind) = tcx.allocator_kind(()) { - codegen_inner(module, unwind_context, kind); + codegen_inner(module, unwind_context, kind, tcx.lang_items().oom().is_some()); true } else { false @@ -31,6 +30,7 @@ fn codegen_inner( module: &mut impl Module, unwind_context: &mut UnwindContext, kind: AllocatorKind, + has_alloc_error_handler: bool, ) { let usize_ty = module.target_config().pointer_type(); @@ -65,7 +65,6 @@ fn codegen_inner( 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(); @@ -104,13 +103,12 @@ fn codegen_inner( returns: vec![], }; - let callee_name = kind.fn_name(sym::oom); - //eprintln!("Codegen allocator shim {} -> {} ({:?} -> {:?})", caller_name, callee_name, sig.params, sig.returns); + let callee_name = if has_alloc_error_handler { "__rg_oom" } else { "__rdl_oom" }; 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 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); diff --git a/src/analyze.rs b/src/analyze.rs index efead25552f4d..35b89358b1984 100644 --- a/src/analyze.rs +++ b/src/analyze.rs @@ -38,17 +38,6 @@ pub(crate) fn analyze(fx: &FunctionCx<'_, '_, '_>) -> IndexVec { _ => {} } } - - match &bb.terminator().kind { - TerminatorKind::Call { destination, func, args, .. } => { - if let Some((dest_place, _dest_bb)) = destination { - if !crate::abi::can_return_to_ssa_var(fx, func, args) { - not_ssa(&mut flag_map, dest_place.local) - } - } - } - _ => {} - } } flag_map diff --git a/src/base.rs b/src/base.rs index 3d78eed77b94c..e99a227a3a6ea 100644 --- a/src/base.rs +++ b/src/base.rs @@ -334,8 +334,6 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) { crate::optimize::peephole::maybe_unwrap_bool_not(&mut fx.bcx, discr); let test_zero = if is_inverted { !test_zero } else { test_zero }; let discr = crate::optimize::peephole::maybe_unwrap_bint(&mut fx.bcx, discr); - let discr = - crate::optimize::peephole::make_branchable_value(&mut fx.bcx, discr); if let Some(taken) = crate::optimize::peephole::maybe_known_branch_taken( &fx.bcx, discr, test_zero, ) { diff --git a/src/cast.rs b/src/cast.rs index 74c5e09f08da0..e7e6afeb865bb 100644 --- a/src/cast.rs +++ b/src/cast.rs @@ -14,21 +14,6 @@ pub(crate) fn clif_intcast( (_, _) 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) @@ -38,10 +23,6 @@ pub(crate) fn clif_intcast( } // 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), } } diff --git a/src/codegen_i128.rs b/src/codegen_i128.rs index ffe1922ab9056..638b2d573b5dd 100644 --- a/src/codegen_i128.rs +++ b/src/codegen_i128.rs @@ -19,9 +19,6 @@ pub(crate) fn maybe_codegen<'tcx>( 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 { @@ -30,29 +27,53 @@ pub(crate) fn maybe_codegen<'tcx>( None } BinOp::Add | BinOp::Sub if !checked => None, - BinOp::Mul if !checked => { - let val_ty = if is_signed { fx.tcx.types.i128 } else { fx.tcx.types.u128 }; - if fx.tcx.sess.target.is_like_windows { - let ret_place = CPlace::new_stack_slot(fx, lhs.layout()); - let (lhs_ptr, lhs_extra) = lhs.force_stack(fx); - let (rhs_ptr, rhs_extra) = rhs.force_stack(fx); - assert!(lhs_extra.is_none()); - assert!(rhs_extra.is_none()); - let args = - [ret_place.to_ptr().get_addr(fx), lhs_ptr.get_addr(fx), rhs_ptr.get_addr(fx)]; - fx.lib_call( - "__multi3", + BinOp::Mul if !checked || is_signed => { + if !checked { + let val_ty = if is_signed { fx.tcx.types.i128 } else { fx.tcx.types.u128 }; + if fx.tcx.sess.target.is_like_windows { + let ret_place = CPlace::new_stack_slot(fx, lhs.layout()); + let (lhs_ptr, lhs_extra) = lhs.force_stack(fx); + let (rhs_ptr, rhs_extra) = rhs.force_stack(fx); + assert!(lhs_extra.is_none()); + assert!(rhs_extra.is_none()); + let args = [ + ret_place.to_ptr().get_addr(fx), + lhs_ptr.get_addr(fx), + rhs_ptr.get_addr(fx), + ]; + fx.lib_call( + "__multi3", + vec![ + AbiParam::special(fx.pointer_type, ArgumentPurpose::StructReturn), + AbiParam::new(fx.pointer_type), + AbiParam::new(fx.pointer_type), + ], + vec![], + &args, + ); + Some(ret_place.to_cvalue(fx)) + } else { + Some(fx.easy_call("__multi3", &[lhs, rhs], val_ty)) + } + } else { + let out_ty = fx.tcx.mk_tup([lhs.layout().ty, fx.tcx.types.bool].iter()); + let oflow = CPlace::new_stack_slot(fx, fx.layout_of(fx.tcx.types.i32)); + let lhs = lhs.load_scalar(fx); + let rhs = rhs.load_scalar(fx); + let oflow_ptr = oflow.to_ptr().get_addr(fx); + let res = fx.lib_call( + "__muloti4", vec![ - AbiParam::special(pointer_ty(fx.tcx), ArgumentPurpose::StructReturn), - AbiParam::new(pointer_ty(fx.tcx)), - AbiParam::new(pointer_ty(fx.tcx)), + AbiParam::new(types::I128), + AbiParam::new(types::I128), + AbiParam::new(fx.pointer_type), ], - vec![], - &args, - ); - Some(ret_place.to_cvalue(fx)) - } else { - Some(fx.easy_call("__multi3", &[lhs, rhs], val_ty)) + vec![AbiParam::new(types::I128)], + &[lhs, rhs, oflow_ptr], + )[0]; + let oflow = oflow.to_cvalue(fx).load_scalar(fx); + let oflow = fx.bcx.ins().ireduce(types::I8, oflow); + Some(CValue::by_val_pair(res, oflow, fx.layout_of(out_ty))) } } BinOp::Add | BinOp::Sub | BinOp::Mul => { @@ -66,16 +87,16 @@ pub(crate) fn maybe_codegen<'tcx>( assert!(rhs_extra.is_none()); ( vec![ - AbiParam::special(pointer_ty(fx.tcx), ArgumentPurpose::StructReturn), - AbiParam::new(pointer_ty(fx.tcx)), - AbiParam::new(pointer_ty(fx.tcx)), + AbiParam::special(fx.pointer_type, ArgumentPurpose::StructReturn), + AbiParam::new(fx.pointer_type), + AbiParam::new(fx.pointer_type), ], [out_place.to_ptr().get_addr(fx), lhs_ptr.get_addr(fx), rhs_ptr.get_addr(fx)], ) } else { ( vec![ - AbiParam::special(pointer_ty(fx.tcx), ArgumentPurpose::StructReturn), + AbiParam::special(fx.pointer_type, ArgumentPurpose::StructReturn), AbiParam::new(types::I128), AbiParam::new(types::I128), ], @@ -88,7 +109,6 @@ pub(crate) fn maybe_codegen<'tcx>( (BinOp::Sub, false) => "__rust_u128_subo", (BinOp::Sub, true) => "__rust_i128_subo", (BinOp::Mul, false) => "__rust_u128_mulo", - (BinOp::Mul, true) => "__rust_i128_mulo", _ => unreachable!(), }; fx.lib_call(name, param_types, vec![], &args); @@ -112,7 +132,7 @@ pub(crate) fn maybe_codegen<'tcx>( let args = [lhs_ptr.get_addr(fx), rhs_ptr.get_addr(fx)]; let ret = fx.lib_call( name, - vec![AbiParam::new(pointer_ty(fx.tcx)), AbiParam::new(pointer_ty(fx.tcx))], + vec![AbiParam::new(fx.pointer_type), AbiParam::new(fx.pointer_type)], vec![AbiParam::new(types::I64X2)], &args, )[0]; @@ -128,40 +148,6 @@ pub(crate) fn maybe_codegen<'tcx>( 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 - }; - - let truncated_rhs = clif_intcast(fx, rhs_val, types::I32, false); - let val = match bin_op { - BinOp::Shl => fx.bcx.ins().ishl(lhs_val, truncated_rhs), - BinOp::Shr => { - if is_signed { - fx.bcx.ins().sshr(lhs_val, truncated_rhs) - } else { - fx.bcx.ins().ushr(lhs_val, truncated_rhs) - } - } - _ => unreachable!(), - }; - if let Some(is_overflow) = is_overflow { - let out_ty = fx.tcx.mk_tup([lhs.layout().ty, fx.tcx.types.bool].iter()); - Some(CValue::by_val_pair(val, is_overflow, fx.layout_of(out_ty))) - } else { - Some(CValue::by_val(val, lhs.layout())) - } - } + BinOp::Shl | BinOp::Shr => None, } } diff --git a/src/debuginfo/emit.rs b/src/debuginfo/emit.rs index 6018eefcd42fb..fb6ccd7c53584 100644 --- a/src/debuginfo/emit.rs +++ b/src/debuginfo/emit.rs @@ -160,7 +160,7 @@ impl Writer for WriterRelocate { let val = match eh_pe.application() { gimli::DW_EH_PE_absptr => val, gimli::DW_EH_PE_pcrel => { - // TODO: better handling of sign + // FIXME better handling of sign let offset = self.len() as u64; offset.wrapping_sub(val) } diff --git a/src/debuginfo/mod.rs b/src/debuginfo/mod.rs index c67336eb3f2c3..ceef65d54785f 100644 --- a/src/debuginfo/mod.rs +++ b/src/debuginfo/mod.rs @@ -46,7 +46,7 @@ impl<'tcx> DebugContext<'tcx> { pub(crate) fn new(tcx: TyCtxt<'tcx>, isa: &dyn TargetIsa) -> Self { let encoding = Encoding { format: Format::Dwarf32, - // TODO: this should be configurable + // FIXME this should be configurable // macOS doesn't seem to support DWARF > 3 // 5 version is required for md5 file hash version: if tcx.sess.target.is_like_osx { diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 3979886e10cfc..8669846074749 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -175,12 +175,11 @@ fn simd_for_each_lane<'tcx>( assert_eq!(lane_count, ret_lane_count); for lane_idx in 0..lane_count { - let lane_idx = mir::Field::new(lane_idx.try_into().unwrap()); - let lane = val.value_field(fx, lane_idx).load_scalar(fx); + let lane = val.value_lane(fx, lane_idx).load_scalar(fx); let res_lane = f(fx, lane_layout, ret_lane_layout, lane); - ret.place_field(fx, lane_idx).write_cvalue(fx, res_lane); + ret.place_lane(fx, lane_idx).write_cvalue(fx, res_lane); } } @@ -206,20 +205,20 @@ fn simd_pair_for_each_lane<'tcx>( let ret_lane_layout = fx.layout_of(ret_lane_ty); assert_eq!(lane_count, ret_lane_count); - for lane in 0..lane_count { - let lane = mir::Field::new(lane.try_into().unwrap()); - let x_lane = x.value_field(fx, lane).load_scalar(fx); - let y_lane = y.value_field(fx, lane).load_scalar(fx); + for lane_idx in 0..lane_count { + let x_lane = x.value_lane(fx, lane_idx).load_scalar(fx); + let y_lane = y.value_lane(fx, lane_idx).load_scalar(fx); let res_lane = f(fx, lane_layout, ret_lane_layout, x_lane, y_lane); - ret.place_field(fx, lane).write_cvalue(fx, res_lane); + ret.place_lane(fx, lane_idx).write_cvalue(fx, res_lane); } } fn simd_reduce<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, val: CValue<'tcx>, + acc: Option, ret: CPlace<'tcx>, f: impl Fn(&mut FunctionCx<'_, '_, 'tcx>, TyAndLayout<'tcx>, Value, Value) -> Value, ) { @@ -227,16 +226,17 @@ fn simd_reduce<'tcx>( let lane_layout = fx.layout_of(lane_ty); assert_eq!(lane_layout, ret.layout()); - let mut res_val = val.value_field(fx, mir::Field::new(0)).load_scalar(fx); - for lane_idx in 1..lane_count { - let lane = - val.value_field(fx, mir::Field::new(lane_idx.try_into().unwrap())).load_scalar(fx); + let (mut res_val, start_lane) = + if let Some(acc) = acc { (acc, 0) } else { (val.value_lane(fx, 0).load_scalar(fx), 1) }; + for lane_idx in start_lane..lane_count { + let lane = val.value_lane(fx, lane_idx).load_scalar(fx); res_val = f(fx, lane_layout, res_val, lane); } let res = CValue::by_val(res_val, lane_layout); ret.write_cvalue(fx, res); } +// FIXME move all uses to `simd_reduce` fn simd_reduce_bool<'tcx>( fx: &mut FunctionCx<'_, '_, 'tcx>, val: CValue<'tcx>, @@ -246,14 +246,18 @@ fn simd_reduce_bool<'tcx>( let (lane_count, _lane_ty) = val.layout().ty.simd_size_and_type(fx.tcx); assert!(ret.layout().ty.is_bool()); - let res_val = val.value_field(fx, mir::Field::new(0)).load_scalar(fx); + let res_val = val.value_lane(fx, 0).load_scalar(fx); let mut res_val = fx.bcx.ins().band_imm(res_val, 1); // mask to boolean for lane_idx in 1..lane_count { - let lane = - val.value_field(fx, mir::Field::new(lane_idx.try_into().unwrap())).load_scalar(fx); + let lane = val.value_lane(fx, lane_idx).load_scalar(fx); let lane = fx.bcx.ins().band_imm(lane, 1); // mask to boolean res_val = f(fx, res_val, lane); } + let res_val = if fx.bcx.func.dfg.value_type(res_val) != types::I8 { + fx.bcx.ins().ireduce(types::I8, res_val) + } else { + res_val + }; let res = CValue::by_val(res_val, ret.layout()); ret.write_cvalue(fx, res); } @@ -288,7 +292,11 @@ macro simd_cmp { if let Some(vector_ty) = vector_ty { let x = $x.load_scalar($fx); let y = $y.load_scalar($fx); - let val = $fx.bcx.ins().icmp(IntCC::$cc, x, y); + let val = if vector_ty.lane_type().is_float() { + $fx.bcx.ins().fcmp(FloatCC::$cc_f, x, y) + } else { + $fx.bcx.ins().icmp(IntCC::$cc, x, y) + }; // HACK This depends on the fact that icmp for vectors represents bools as 0 and !0, not 0 and 1. let val = $fx.bcx.ins().raw_bitcast(vector_ty, val); @@ -603,9 +611,6 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( let (val, has_overflow) = checked_res.load_scalar_pair(fx); let clif_ty = fx.clif_type(T).unwrap(); - // `select.i8` is not implemented by Cranelift. - let has_overflow = fx.bcx.ins().uextend(types::I32, has_overflow); - let (min, max) = type_min_max_value(&mut fx.bcx, clif_ty, signed); let val = match (intrinsic, signed) { @@ -632,21 +637,11 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( }; rotate_left, (v x, v y) { let layout = fx.layout_of(T); - let y = if fx.bcx.func.dfg.value_type(y) == types::I128 { - fx.bcx.ins().ireduce(types::I64, y) - } else { - y - }; let res = fx.bcx.ins().rotl(x, y); ret.write_cvalue(fx, CValue::by_val(res, layout)); }; rotate_right, (v x, v y) { let layout = fx.layout_of(T); - let y = if fx.bcx.func.dfg.value_type(y) == types::I128 { - fx.bcx.ins().ireduce(types::I64, y) - } else { - y - }; let res = fx.bcx.ins().rotr(x, y); ret.write_cvalue(fx, CValue::by_val(res, layout)); }; @@ -684,35 +679,13 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( }; ctlz | ctlz_nonzero, (v arg) { // FIXME trap on `ctlz_nonzero` with zero arg. - let res = if T == fx.tcx.types.u128 || T == fx.tcx.types.i128 { - // FIXME verify this algorithm is correct - let (lsb, msb) = fx.bcx.ins().isplit(arg); - let lsb_lz = fx.bcx.ins().clz(lsb); - let msb_lz = fx.bcx.ins().clz(msb); - let msb_is_zero = fx.bcx.ins().icmp_imm(IntCC::Equal, msb, 0); - let lsb_lz_plus_64 = fx.bcx.ins().iadd_imm(lsb_lz, 64); - let res = fx.bcx.ins().select(msb_is_zero, lsb_lz_plus_64, msb_lz); - fx.bcx.ins().uextend(types::I128, res) - } else { - fx.bcx.ins().clz(arg) - }; + let res = fx.bcx.ins().clz(arg); let res = CValue::by_val(res, fx.layout_of(T)); ret.write_cvalue(fx, res); }; cttz | cttz_nonzero, (v arg) { // FIXME trap on `cttz_nonzero` with zero arg. - let res = if T == fx.tcx.types.u128 || T == fx.tcx.types.i128 { - // FIXME verify this algorithm is correct - let (lsb, msb) = fx.bcx.ins().isplit(arg); - let lsb_tz = fx.bcx.ins().ctz(lsb); - let msb_tz = fx.bcx.ins().ctz(msb); - let lsb_is_zero = fx.bcx.ins().icmp_imm(IntCC::Equal, lsb, 0); - let msb_tz_plus_64 = fx.bcx.ins().iadd_imm(msb_tz, 64); - let res = fx.bcx.ins().select(lsb_is_zero, msb_tz_plus_64, lsb_tz); - fx.bcx.ins().uextend(types::I128, res) - } else { - fx.bcx.ins().ctz(arg) - }; + let res = fx.bcx.ins().ctz(arg); let res = CValue::by_val(res, fx.layout_of(T)); ret.write_cvalue(fx, res); }; @@ -995,8 +968,6 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( let old = CValue::by_val(old, layout); ret.write_cvalue(fx, old); }; - - // FIXME https://github.com/bytecodealliance/wasmtime/issues/2647 _ if intrinsic.as_str().starts_with("atomic_nand"), (v ptr, c src) { let layout = src.layout(); validate_atomic_type!(fx, intrinsic, span, layout.ty); @@ -1058,23 +1029,39 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( ret.write_cvalue(fx, old); }; + // In Rust floating point min and max don't propagate NaN. In Cranelift they do however. + // For this reason it is necessary to use `a.is_nan() ? b : (a >= b ? b : a)` for `minnumf*` + // and `a.is_nan() ? b : (a <= b ? b : a)` for `maxnumf*`. NaN checks are done by comparing + // a float against itself. Only in case of NaN is it not equal to itself. minnumf32, (v a, v b) { - let val = fx.bcx.ins().fmin(a, b); + let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a); + let a_ge_b = fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, a, b); + let temp = fx.bcx.ins().select(a_ge_b, b, a); + let val = fx.bcx.ins().select(a_is_nan, b, temp); let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32)); ret.write_cvalue(fx, val); }; minnumf64, (v a, v b) { - let val = fx.bcx.ins().fmin(a, b); + let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a); + let a_ge_b = fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, a, b); + let temp = fx.bcx.ins().select(a_ge_b, b, a); + let val = fx.bcx.ins().select(a_is_nan, b, temp); let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64)); ret.write_cvalue(fx, val); }; maxnumf32, (v a, v b) { - let val = fx.bcx.ins().fmax(a, b); + let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a); + let a_le_b = fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, a, b); + let temp = fx.bcx.ins().select(a_le_b, b, a); + let val = fx.bcx.ins().select(a_is_nan, b, temp); let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32)); ret.write_cvalue(fx, val); }; maxnumf64, (v a, v b) { - let val = fx.bcx.ins().fmax(a, b); + let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a); + let a_le_b = fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, a, b); + let temp = fx.bcx.ins().select(a_le_b, b, a); + let val = fx.bcx.ins().select(a_is_nan, b, temp); let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64)); ret.write_cvalue(fx, val); }; @@ -1122,6 +1109,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( } let size = fx.layout_of(T).layout.size; + // FIXME add and use emit_small_memcmp let is_eq_value = if size == Size::ZERO { // No bytes means they're trivially equal @@ -1137,10 +1125,9 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( } else { // Just call `memcmp` (like slices do in core) when the // size is too large or it's not a power-of-two. - let ptr_ty = pointer_ty(fx.tcx); let signed_bytes = i64::try_from(size.bytes()).unwrap(); - let bytes_val = fx.bcx.ins().iconst(ptr_ty, signed_bytes); - let params = vec![AbiParam::new(ptr_ty); 3]; + let bytes_val = fx.bcx.ins().iconst(fx.pointer_type, signed_bytes); + let params = vec![AbiParam::new(fx.pointer_type); 3]; let returns = vec![AbiParam::new(types::I32)]; let args = &[lhs_ref, rhs_ref, bytes_val]; let cmp = fx.lib_call("memcmp", params, returns, args)[0]; diff --git a/src/intrinsics/simd.rs b/src/intrinsics/simd.rs index c2f469fa021e1..43e68b4afa9ea 100644 --- a/src/intrinsics/simd.rs +++ b/src/intrinsics/simd.rs @@ -108,11 +108,11 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( for (out_idx, in_idx) in indexes.into_iter().enumerate() { let in_lane = if u64::from(in_idx) < lane_count { - x.value_field(fx, mir::Field::new(in_idx.into())) + x.value_lane(fx, in_idx.into()) } else { - y.value_field(fx, mir::Field::new(usize::from(in_idx) - usize::try_from(lane_count).unwrap())) + y.value_lane(fx, u64::from(in_idx) - lane_count) }; - let out_lane = ret.place_field(fx, mir::Field::new(out_idx)); + let out_lane = ret.place_lane(fx, u64::try_from(out_idx).unwrap()); out_lane.write_cvalue(fx, in_lane); } }; @@ -163,10 +163,38 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( fx.tcx.sess.span_fatal(fx.mir.span, &format!("[simd_extract] idx {} >= lane_count {}", idx, lane_count)); } - let ret_lane = v.value_field(fx, mir::Field::new(idx.try_into().unwrap())); + let ret_lane = v.value_lane(fx, idx.try_into().unwrap()); ret.write_cvalue(fx, ret_lane); }; + simd_neg, (c a) { + validate_simd_type!(fx, intrinsic, span, a.layout().ty); + simd_for_each_lane(fx, a, ret, |fx, lane_layout, ret_lane_layout, lane| { + let ret_lane = match lane_layout.ty.kind() { + ty::Int(_) => fx.bcx.ins().ineg(lane), + ty::Float(_) => fx.bcx.ins().fneg(lane), + _ => unreachable!(), + }; + CValue::by_val(ret_lane, ret_lane_layout) + }); + }; + + simd_fabs, (c a) { + validate_simd_type!(fx, intrinsic, span, a.layout().ty); + simd_for_each_lane(fx, a, ret, |fx, _lane_layout, ret_lane_layout, lane| { + let ret_lane = fx.bcx.ins().fabs(lane); + CValue::by_val(ret_lane, ret_lane_layout) + }); + }; + + simd_fsqrt, (c a) { + validate_simd_type!(fx, intrinsic, span, a.layout().ty); + simd_for_each_lane(fx, a, ret, |fx, _lane_layout, ret_lane_layout, lane| { + let ret_lane = fx.bcx.ins().sqrt(lane); + CValue::by_val(ret_lane, ret_lane_layout) + }); + }; + simd_add, (c x, c y) { validate_simd_type!(fx, intrinsic, span, x.layout().ty); simd_int_flt_binop!(fx, iadd|fadd(x, y) -> ret); @@ -183,6 +211,29 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( validate_simd_type!(fx, intrinsic, span, x.layout().ty); simd_int_flt_binop!(fx, udiv|sdiv|fdiv(x, y) -> ret); }; + simd_rem, (c x, c y) { + validate_simd_type!(fx, intrinsic, span, x.layout().ty); + simd_pair_for_each_lane(fx, x, y, ret, |fx, lane_layout, ret_lane_layout, x_lane, y_lane| { + let res_lane = match lane_layout.ty.kind() { + ty::Uint(_) => fx.bcx.ins().urem(x_lane, y_lane), + ty::Int(_) => fx.bcx.ins().srem(x_lane, y_lane), + ty::Float(FloatTy::F32) => fx.lib_call( + "fmodf", + vec![AbiParam::new(types::F32), AbiParam::new(types::F32)], + vec![AbiParam::new(types::F32)], + &[x_lane, y_lane], + )[0], + ty::Float(FloatTy::F64) => fx.lib_call( + "fmod", + vec![AbiParam::new(types::F64), AbiParam::new(types::F64)], + vec![AbiParam::new(types::F64)], + &[x_lane, y_lane], + )[0], + _ => unreachable!("{:?}", lane_layout.ty), + }; + CValue::by_val(res_lane, ret_lane_layout) + }); + }; simd_shl, (c x, c y) { validate_simd_type!(fx, intrinsic, span, x.layout().ty); simd_int_binop!(fx, ishl(x, y) -> ret); @@ -216,15 +267,14 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( let ret_lane_layout = fx.layout_of(ret_lane_ty); for lane in 0..lane_count { - let lane = mir::Field::new(lane.try_into().unwrap()); - let a_lane = a.value_field(fx, lane).load_scalar(fx); - let b_lane = b.value_field(fx, lane).load_scalar(fx); - let c_lane = c.value_field(fx, lane).load_scalar(fx); + let a_lane = a.value_lane(fx, lane).load_scalar(fx); + let b_lane = b.value_lane(fx, lane).load_scalar(fx); + let c_lane = c.value_lane(fx, lane).load_scalar(fx); let mul_lane = fx.bcx.ins().fmul(a_lane, b_lane); let res_lane = CValue::by_val(fx.bcx.ins().fadd(mul_lane, c_lane), ret_lane_layout); - ret.place_field(fx, lane).write_cvalue(fx, res_lane); + ret.place_lane(fx, lane).write_cvalue(fx, res_lane); } }; @@ -237,9 +287,52 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( simd_flt_binop!(fx, fmax(x, y) -> ret); }; - simd_reduce_add_ordered | simd_reduce_add_unordered, (c v) { + simd_round, (c a) { + validate_simd_type!(fx, intrinsic, span, a.layout().ty); + simd_for_each_lane(fx, a, ret, |fx, lane_layout, ret_lane_layout, lane| { + let res_lane = match lane_layout.ty.kind() { + ty::Float(FloatTy::F32) => fx.lib_call( + "roundf", + vec![AbiParam::new(types::F32)], + vec![AbiParam::new(types::F32)], + &[lane], + )[0], + ty::Float(FloatTy::F64) => fx.lib_call( + "round", + vec![AbiParam::new(types::F64)], + vec![AbiParam::new(types::F64)], + &[lane], + )[0], + _ => unreachable!("{:?}", lane_layout.ty), + }; + CValue::by_val(res_lane, ret_lane_layout) + }); + }; + simd_ceil, (c a) { + validate_simd_type!(fx, intrinsic, span, a.layout().ty); + simd_for_each_lane(fx, a, ret, |fx, _lane_layout, ret_lane_layout, lane| { + let ret_lane = fx.bcx.ins().ceil(lane); + CValue::by_val(ret_lane, ret_lane_layout) + }); + }; + simd_floor, (c a) { + validate_simd_type!(fx, intrinsic, span, a.layout().ty); + simd_for_each_lane(fx, a, ret, |fx, _lane_layout, ret_lane_layout, lane| { + let ret_lane = fx.bcx.ins().floor(lane); + CValue::by_val(ret_lane, ret_lane_layout) + }); + }; + simd_trunc, (c a) { + validate_simd_type!(fx, intrinsic, span, a.layout().ty); + simd_for_each_lane(fx, a, ret, |fx, _lane_layout, ret_lane_layout, lane| { + let ret_lane = fx.bcx.ins().trunc(lane); + CValue::by_val(ret_lane, ret_lane_layout) + }); + }; + + simd_reduce_add_ordered | simd_reduce_add_unordered, (c v, v acc) { validate_simd_type!(fx, intrinsic, span, v.layout().ty); - simd_reduce(fx, v, ret, |fx, lane_layout, a, b| { + simd_reduce(fx, v, Some(acc), ret, |fx, lane_layout, a, b| { if lane_layout.ty.is_floating_point() { fx.bcx.ins().fadd(a, b) } else { @@ -248,9 +341,9 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( }); }; - simd_reduce_mul_ordered | simd_reduce_mul_unordered, (c v) { + simd_reduce_mul_ordered | simd_reduce_mul_unordered, (c v, v acc) { validate_simd_type!(fx, intrinsic, span, v.layout().ty); - simd_reduce(fx, v, ret, |fx, lane_layout, a, b| { + simd_reduce(fx, v, Some(acc), ret, |fx, lane_layout, a, b| { if lane_layout.ty.is_floating_point() { fx.bcx.ins().fmul(a, b) } else { @@ -269,13 +362,70 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>( simd_reduce_bool(fx, v, ret, |fx, a, b| fx.bcx.ins().bor(a, b)); }; - // simd_fabs - // simd_saturating_add + simd_reduce_and, (c v) { + validate_simd_type!(fx, intrinsic, span, v.layout().ty); + simd_reduce(fx, v, None, ret, |fx, _layout, a, b| fx.bcx.ins().band(a, b)); + }; + + simd_reduce_or, (c v) { + validate_simd_type!(fx, intrinsic, span, v.layout().ty); + simd_reduce(fx, v, None, ret, |fx, _layout, a, b| fx.bcx.ins().bor(a, b)); + }; + + simd_reduce_xor, (c v) { + validate_simd_type!(fx, intrinsic, span, v.layout().ty); + simd_reduce(fx, v, None, ret, |fx, _layout, a, b| fx.bcx.ins().bxor(a, b)); + }; + + simd_reduce_min, (c v) { + // FIXME support floats + validate_simd_type!(fx, intrinsic, span, v.layout().ty); + simd_reduce(fx, v, None, ret, |fx, layout, a, b| { + let lt = fx.bcx.ins().icmp(if layout.ty.is_signed() { + IntCC::SignedLessThan + } else { + IntCC::UnsignedLessThan + }, a, b); + fx.bcx.ins().select(lt, a, b) + }); + }; + + simd_reduce_max, (c v) { + // FIXME support floats + validate_simd_type!(fx, intrinsic, span, v.layout().ty); + simd_reduce(fx, v, None, ret, |fx, layout, a, b| { + let gt = fx.bcx.ins().icmp(if layout.ty.is_signed() { + IntCC::SignedGreaterThan + } else { + IntCC::UnsignedGreaterThan + }, a, b); + fx.bcx.ins().select(gt, a, b) + }); + }; + + simd_select, (c m, c a, c b) { + validate_simd_type!(fx, intrinsic, span, m.layout().ty); + validate_simd_type!(fx, intrinsic, span, a.layout().ty); + assert_eq!(a.layout(), b.layout()); + + let (lane_count, lane_ty) = a.layout().ty.simd_size_and_type(fx.tcx); + let lane_layout = fx.layout_of(lane_ty); + + for lane in 0..lane_count { + let m_lane = m.value_lane(fx, lane).load_scalar(fx); + let a_lane = a.value_lane(fx, lane).load_scalar(fx); + let b_lane = b.value_lane(fx, lane).load_scalar(fx); + + let m_lane = fx.bcx.ins().icmp_imm(IntCC::Equal, m_lane, 0); + let res_lane = CValue::by_val(fx.bcx.ins().select(m_lane, b_lane, a_lane), lane_layout); + + ret.place_lane(fx, lane).write_cvalue(fx, res_lane); + } + }; + + // simd_saturating_* // simd_bitmask - // simd_select - // simd_rem - // simd_neg - // simd_trunc - // simd_floor + // simd_scatter + // simd_gather } } diff --git a/src/lib.rs b/src/lib.rs index e32dae49131ab..4ef53663ca0d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -184,6 +184,9 @@ impl CodegenBackend for CraneliftCodegenBackend { let config = if let Some(config) = self.config.clone() { config } else { + if !tcx.sess.unstable_options() && !tcx.sess.opts.cg.llvm_args.is_empty() { + tcx.sess.fatal("`-Z unstable-options` must be passed to allow configuring cg_clif"); + } BackendConfig::from_opts(&tcx.sess.opts.cg.llvm_args) .unwrap_or_else(|err| tcx.sess.fatal(&err)) }; @@ -217,16 +220,15 @@ impl CodegenBackend for CraneliftCodegenBackend { ) -> Result<(), ErrorReported> { use rustc_codegen_ssa::back::link::link_binary; - link_binary::>( - sess, - &codegen_results, - outputs, - ) + link_binary::>(sess, &codegen_results, outputs) } } fn target_triple(sess: &Session) -> target_lexicon::Triple { - sess.target.llvm_target.parse().unwrap() + match sess.target.llvm_target.parse() { + Ok(triple) => triple, + Err(err) => sess.fatal(&format!("target not recognized: {}", err)), + } } fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Box { @@ -276,15 +278,21 @@ fn build_isa(sess: &Session, backend_config: &BackendConfig) -> Box { let mut builder = - cranelift_codegen::isa::lookup_variant(target_triple, variant).unwrap(); + cranelift_codegen::isa::lookup_variant(target_triple.clone(), variant) + .unwrap_or_else(|err| { + sess.fatal(&format!("can't compile for {}: {}", target_triple, err)); + }); if let Err(_) = builder.enable(value) { - sess.fatal("The specified target cpu isn't currently supported by Cranelift."); + sess.fatal("the specified target cpu isn't currently supported by Cranelift."); } builder } None => { let mut builder = - cranelift_codegen::isa::lookup_variant(target_triple.clone(), variant).unwrap(); + cranelift_codegen::isa::lookup_variant(target_triple.clone(), variant) + .unwrap_or_else(|err| { + sess.fatal(&format!("can't compile for {}: {}", target_triple, err)); + }); if target_triple.architecture == target_lexicon::Architecture::X86_64 { // Don't use "haswell" as the default, as it implies `has_lzcnt`. // macOS CI is still at Ivy Bridge EP, so `lzcnt` is interpreted as `bsr`. diff --git a/src/num.rs b/src/num.rs index b6d378a5fe10a..545d390e26995 100644 --- a/src/num.rs +++ b/src/num.rs @@ -67,19 +67,6 @@ pub(crate) fn codegen_binop<'tcx>( let lhs = in_lhs.load_scalar(fx); let rhs = in_rhs.load_scalar(fx); - let (lhs, rhs) = if (bin_op == BinOp::Eq || bin_op == BinOp::Ne) - && (in_lhs.layout().ty.kind() == fx.tcx.types.i8.kind() - || in_lhs.layout().ty.kind() == fx.tcx.types.i16.kind()) - { - // FIXME(CraneStation/cranelift#896) icmp_imm.i8/i16 with eq/ne for signed ints is implemented wrong. - ( - fx.bcx.ins().sextend(types::I32, lhs), - fx.bcx.ins().sextend(types::I32, rhs), - ) - } else { - (lhs, rhs) - }; - return codegen_compare_bin_op(fx, bin_op, signed, lhs, rhs); } _ => {} @@ -293,9 +280,8 @@ pub(crate) fn codegen_checked_int_binop<'tcx>( } BinOp::Shl => { let lhs_ty = fx.bcx.func.dfg.value_type(lhs); - let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1)); - let actual_shift = clif_intcast(fx, actual_shift, types::I8, false); - let val = fx.bcx.ins().ishl(lhs, actual_shift); + let masked_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1)); + let val = fx.bcx.ins().ishl(lhs, masked_shift); let ty = fx.bcx.func.dfg.value_type(val); let max_shift = i64::from(ty.bits()) - 1; let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, rhs, max_shift); @@ -303,12 +289,11 @@ pub(crate) fn codegen_checked_int_binop<'tcx>( } BinOp::Shr => { let lhs_ty = fx.bcx.func.dfg.value_type(lhs); - let actual_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1)); - let actual_shift = clif_intcast(fx, actual_shift, types::I8, false); + let masked_shift = fx.bcx.ins().band_imm(rhs, i64::from(lhs_ty.bits() - 1)); let val = if !signed { - fx.bcx.ins().ushr(lhs, actual_shift) + fx.bcx.ins().ushr(lhs, masked_shift) } else { - fx.bcx.ins().sshr(lhs, actual_shift) + fx.bcx.ins().sshr(lhs, masked_shift) }; let ty = fx.bcx.func.dfg.value_type(val); let max_shift = i64::from(ty.bits()) - 1; diff --git a/src/optimize/peephole.rs b/src/optimize/peephole.rs index b95e2d72877d9..d637b4d89293c 100644 --- a/src/optimize/peephole.rs +++ b/src/optimize/peephole.rs @@ -1,8 +1,6 @@ //! Peephole optimizations that can be performed while creating clif ir. -use cranelift_codegen::ir::{ - condcodes::IntCC, types, InstBuilder, InstructionData, Opcode, Value, ValueDef, -}; +use cranelift_codegen::ir::{condcodes::IntCC, InstructionData, Opcode, Value, ValueDef}; use cranelift_frontend::FunctionBuilder; /// If the given value was produced by a `bint` instruction, return it's input, otherwise return the @@ -37,43 +35,6 @@ pub(crate) fn maybe_unwrap_bool_not(bcx: &mut FunctionBuilder<'_>, arg: Value) - } } -pub(crate) fn make_branchable_value(bcx: &mut FunctionBuilder<'_>, arg: Value) -> Value { - if bcx.func.dfg.value_type(arg).is_bool() { - return arg; - } - - (|| { - let arg_inst = if let ValueDef::Result(arg_inst, 0) = bcx.func.dfg.value_def(arg) { - arg_inst - } else { - return None; - }; - - match bcx.func.dfg[arg_inst] { - // This is the lowering of Rvalue::Not - InstructionData::Load { opcode: Opcode::Load, arg: ptr, flags, offset } => { - // Using `load.i8 + uextend.i32` would legalize to `uload8 + ireduce.i8 + - // uextend.i32`. Just `uload8` is much faster. - match bcx.func.dfg.ctrl_typevar(arg_inst) { - types::I8 => Some(bcx.ins().uload8(types::I32, flags, ptr, offset)), - types::I16 => Some(bcx.ins().uload16(types::I32, flags, ptr, offset)), - _ => None, - } - } - _ => None, - } - })() - .unwrap_or_else(|| { - match bcx.func.dfg.value_type(arg) { - types::I8 | types::I16 => { - // WORKAROUND for brz.i8 and brnz.i8 not yet being implemented - bcx.ins().uextend(types::I32, arg) - } - _ => arg, - } - }) -} - /// Returns whether the branch is statically known to be taken or `None` if it isn't statically known. pub(crate) fn maybe_known_branch_taken( bcx: &FunctionBuilder<'_>, diff --git a/src/trap.rs b/src/trap.rs index 21d3e68dbc792..fe8d20fa39fc2 100644 --- a/src/trap.rs +++ b/src/trap.rs @@ -10,7 +10,7 @@ fn codegen_print(fx: &mut FunctionCx<'_, '_, '_>, msg: &str) { Linkage::Import, &Signature { call_conv: CallConv::triple_default(fx.triple()), - params: vec![AbiParam::new(pointer_ty(fx.tcx))], + params: vec![AbiParam::new(fx.pointer_type)], returns: vec![AbiParam::new(types::I32)], }, ) diff --git a/src/unsize.rs b/src/unsize.rs index d594731b4dfce..d9c4647cba3a9 100644 --- a/src/unsize.rs +++ b/src/unsize.rs @@ -77,12 +77,10 @@ fn unsize_ptr<'tcx>( (&ty::Ref(_, a, _), &ty::Ref(_, b, _)) | (&ty::Ref(_, a, _), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { - assert!(!fx.layout_of(a).is_unsized()); (src, unsized_info(fx, a, b, old_info)) } (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => { let (a, b) = (src_layout.ty.boxed_ty(), dst_layout.ty.boxed_ty()); - assert!(!fx.layout_of(a).is_unsized()); (src, unsized_info(fx, a, b, old_info)) } (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { diff --git a/src/value_and_place.rs b/src/value_and_place.rs index ae8ccc626b470..364b3da92b888 100644 --- a/src/value_and_place.rs +++ b/src/value_and_place.rs @@ -34,10 +34,10 @@ fn codegen_field<'tcx>( let (_, unsized_align) = crate::unsize::size_and_align_of_dst(fx, field_layout, extra); - let one = fx.bcx.ins().iconst(pointer_ty(fx.tcx), 1); + let one = fx.bcx.ins().iconst(fx.pointer_type, 1); let align_sub_1 = fx.bcx.ins().isub(unsized_align, one); let and_lhs = fx.bcx.ins().iadd_imm(align_sub_1, unaligned_offset as i64); - let zero = fx.bcx.ins().iconst(pointer_ty(fx.tcx), 0); + let zero = fx.bcx.ins().iconst(fx.pointer_type, 0); let and_rhs = fx.bcx.ins().isub(zero, unsized_align); let offset = fx.bcx.ins().band(and_lhs, and_rhs); @@ -206,6 +206,38 @@ impl<'tcx> CValue<'tcx> { } } + /// Like [`CValue::value_field`] except handling ADTs containing a single array field in a way + /// such that you can access individual lanes. + pub(crate) fn value_lane( + self, + fx: &mut FunctionCx<'_, '_, 'tcx>, + lane_idx: u64, + ) -> CValue<'tcx> { + let layout = self.1; + assert!(layout.ty.is_simd()); + let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx); + let lane_layout = fx.layout_of(lane_ty); + assert!(lane_idx < lane_count); + match self.0 { + CValueInner::ByVal(val) => match layout.abi { + Abi::Vector { element: _, count: _ } => { + assert!(lane_count <= u8::MAX.into(), "SIMD type with more than 255 lanes???"); + let lane_idx = u8::try_from(lane_idx).unwrap(); + let lane = fx.bcx.ins().extractlane(val, lane_idx); + CValue::by_val(lane, lane_layout) + } + _ => unreachable!("value_lane for ByVal with abi {:?}", layout.abi), + }, + CValueInner::ByValPair(_, _) => unreachable!(), + CValueInner::ByRef(ptr, None) => { + let field_offset = lane_layout.size * lane_idx; + let field_ptr = ptr.offset_i64(fx, i64::try_from(field_offset.bytes()).unwrap()); + CValue::by_ref(field_ptr, lane_layout) + } + CValueInner::ByRef(_, Some(_)) => unreachable!(), + } + } + pub(crate) fn unsize_value(self, fx: &mut FunctionCx<'_, '_, 'tcx>, dest: CPlace<'tcx>) { crate::unsize::coerce_unsized_into(fx, self, dest); } @@ -286,17 +318,16 @@ impl<'tcx> CPlace<'tcx> { &self.inner } - pub(crate) fn no_place(layout: TyAndLayout<'tcx>) -> CPlace<'tcx> { - CPlace { inner: CPlaceInner::Addr(Pointer::dangling(layout.align.pref), None), layout } - } - pub(crate) fn new_stack_slot( fx: &mut FunctionCx<'_, '_, 'tcx>, layout: TyAndLayout<'tcx>, ) -> CPlace<'tcx> { assert!(!layout.is_unsized()); if layout.size.bytes() == 0 { - return CPlace::no_place(layout); + return CPlace { + inner: CPlaceInner::Addr(Pointer::dangling(layout.align.pref), None), + layout, + }; } let stack_slot = fx.bcx.create_stack_slot(StackSlotData { @@ -610,6 +641,38 @@ impl<'tcx> CPlace<'tcx> { } } + /// Like [`CPlace::place_field`] except handling ADTs containing a single array field in a way + /// such that you can access individual lanes. + pub(crate) fn place_lane( + self, + fx: &mut FunctionCx<'_, '_, 'tcx>, + lane_idx: u64, + ) -> CPlace<'tcx> { + let layout = self.layout(); + assert!(layout.ty.is_simd()); + let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx); + let lane_layout = fx.layout_of(lane_ty); + assert!(lane_idx < lane_count); + + match self.inner { + CPlaceInner::Var(local, var) => { + assert!(matches!(layout.abi, Abi::Vector { .. })); + CPlace { + inner: CPlaceInner::VarLane(local, var, lane_idx.try_into().unwrap()), + layout: lane_layout, + } + } + CPlaceInner::VarPair(_, _, _) => unreachable!(), + CPlaceInner::VarLane(_, _, _) => unreachable!(), + CPlaceInner::Addr(ptr, None) => { + let field_offset = lane_layout.size * lane_idx; + let field_ptr = ptr.offset_i64(fx, i64::try_from(field_offset.bytes()).unwrap()); + CPlace::for_ptr(field_ptr, lane_layout) + } + CPlaceInner::Addr(_, Some(_)) => unreachable!(), + } + } + pub(crate) fn place_index( self, fx: &mut FunctionCx<'_, '_, 'tcx>, diff --git a/src/vtable.rs b/src/vtable.rs index 1b31587430887..f97d416b66f9f 100644 --- a/src/vtable.rs +++ b/src/vtable.rs @@ -14,7 +14,7 @@ pub(crate) fn vtable_memflags() -> MemFlags { pub(crate) fn drop_fn_of_obj(fx: &mut FunctionCx<'_, '_, '_>, vtable: Value) -> Value { let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes() as usize; fx.bcx.ins().load( - pointer_ty(fx.tcx), + fx.pointer_type, vtable_memflags(), vtable, (ty::COMMON_VTABLE_ENTRIES_DROPINPLACE * usize_size) as i32, @@ -24,7 +24,7 @@ pub(crate) fn drop_fn_of_obj(fx: &mut FunctionCx<'_, '_, '_>, vtable: Value) -> pub(crate) fn size_of_obj(fx: &mut FunctionCx<'_, '_, '_>, vtable: Value) -> Value { let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes() as usize; fx.bcx.ins().load( - pointer_ty(fx.tcx), + fx.pointer_type, vtable_memflags(), vtable, (ty::COMMON_VTABLE_ENTRIES_SIZE * usize_size) as i32, @@ -34,7 +34,7 @@ pub(crate) fn size_of_obj(fx: &mut FunctionCx<'_, '_, '_>, vtable: Value) -> Val pub(crate) fn min_align_of_obj(fx: &mut FunctionCx<'_, '_, '_>, vtable: Value) -> Value { let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes() as usize; fx.bcx.ins().load( - pointer_ty(fx.tcx), + fx.pointer_type, vtable_memflags(), vtable, (ty::COMMON_VTABLE_ENTRIES_ALIGN * usize_size) as i32, @@ -55,7 +55,7 @@ pub(crate) fn get_ptr_and_method_ref<'tcx>( let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes(); let func_ref = fx.bcx.ins().load( - pointer_ty(fx.tcx), + fx.pointer_type, vtable_memflags(), vtable, (idx * usize_size as usize) as i32, diff --git a/y.rs b/y.rs index 43937588b481d..26605003c4200 100755 --- a/y.rs +++ b/y.rs @@ -15,8 +15,8 @@ exec ${0/.rs/.bin} $@ //! for example: //! //! ```shell -//! $ rustc y.rs -o build/y.bin -//! $ build/y.bin +//! $ rustc y.rs -o y.bin +//! $ ./y.bin //! ``` //! //! # Naming From e1b6dc07870137ff2fd8896f2b24fad6547033de Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Sat, 7 Aug 2021 10:33:14 +0200 Subject: [PATCH 014/621] Rustup to rustc 1.56.0-nightly (5ad7389bd 2021-08-06) --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index f074ebe7a42e0..fd95377d06dc8 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-08-05" +channel = "nightly-2021-08-07" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] From 98f45afd6482e67cff143d8337d4d32cfc28ba9a Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Sat, 7 Aug 2021 11:09:03 +0200 Subject: [PATCH 015/621] Update Cranelift --- Cargo.lock | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 23c1fdc6ee425..4afddf76869de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,16 +33,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cranelift-bforest" -version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" +version = "0.76.0" +source = "git+https://github.com/bytecodealliance/wasmtime.git#9c550fcf41425942ed97c747f0169b2ca81f9c1b" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" +version = "0.76.0" +source = "git+https://github.com/bytecodealliance/wasmtime.git#9c550fcf41425942ed97c747f0169b2ca81f9c1b" dependencies = [ "cranelift-bforest", "cranelift-codegen-meta", @@ -57,8 +57,8 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" +version = "0.76.0" +source = "git+https://github.com/bytecodealliance/wasmtime.git#9c550fcf41425942ed97c747f0169b2ca81f9c1b" dependencies = [ "cranelift-codegen-shared", "cranelift-entity", @@ -66,18 +66,18 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" -version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" +version = "0.76.0" +source = "git+https://github.com/bytecodealliance/wasmtime.git#9c550fcf41425942ed97c747f0169b2ca81f9c1b" [[package]] name = "cranelift-entity" -version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" +version = "0.76.0" +source = "git+https://github.com/bytecodealliance/wasmtime.git#9c550fcf41425942ed97c747f0169b2ca81f9c1b" [[package]] name = "cranelift-frontend" -version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" +version = "0.76.0" +source = "git+https://github.com/bytecodealliance/wasmtime.git#9c550fcf41425942ed97c747f0169b2ca81f9c1b" dependencies = [ "cranelift-codegen", "log", @@ -87,8 +87,8 @@ dependencies = [ [[package]] name = "cranelift-jit" -version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" +version = "0.76.0" +source = "git+https://github.com/bytecodealliance/wasmtime.git#9c550fcf41425942ed97c747f0169b2ca81f9c1b" dependencies = [ "anyhow", "cranelift-codegen", @@ -104,8 +104,8 @@ dependencies = [ [[package]] name = "cranelift-module" -version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" +version = "0.76.0" +source = "git+https://github.com/bytecodealliance/wasmtime.git#9c550fcf41425942ed97c747f0169b2ca81f9c1b" dependencies = [ "anyhow", "cranelift-codegen", @@ -115,8 +115,8 @@ dependencies = [ [[package]] name = "cranelift-native" -version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" +version = "0.76.0" +source = "git+https://github.com/bytecodealliance/wasmtime.git#9c550fcf41425942ed97c747f0169b2ca81f9c1b" dependencies = [ "cranelift-codegen", "libc", @@ -125,8 +125,8 @@ dependencies = [ [[package]] name = "cranelift-object" -version = "0.75.0" -source = "git+https://github.com/bytecodealliance/wasmtime.git#5deda279775dca5e37449c829cda1f6276d6542b" +version = "0.76.0" +source = "git+https://github.com/bytecodealliance/wasmtime.git#9c550fcf41425942ed97c747f0169b2ca81f9c1b" dependencies = [ "anyhow", "cranelift-codegen", From 48dd2eb9daa7094ccd26f05a74148713d9680725 Mon Sep 17 00:00:00 2001 From: Waffle Date: Sun, 8 Aug 2021 20:41:24 +0300 Subject: [PATCH 016/621] Make `<[T]>::split_at_unchecked` and `<[T]>::split_at_mut_unchecked` public --- library/core/src/slice/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index de25c984abf90..6663a514426cc 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -1559,7 +1559,7 @@ impl [T] { /// /// # Examples /// - /// ```compile_fail + /// ``` /// #![feature(slice_split_at_unchecked)] /// /// let v = [1, 2, 3, 4, 5, 6]; @@ -1584,7 +1584,7 @@ impl [T] { /// ``` #[unstable(feature = "slice_split_at_unchecked", reason = "new API", issue = "76014")] #[inline] - unsafe fn split_at_unchecked(&self, mid: usize) -> (&[T], &[T]) { + pub unsafe fn split_at_unchecked(&self, mid: usize) -> (&[T], &[T]) { // SAFETY: Caller has to check that `0 <= mid <= self.len()` unsafe { (self.get_unchecked(..mid), self.get_unchecked(mid..)) } } @@ -1608,7 +1608,7 @@ impl [T] { /// /// # Examples /// - /// ```compile_fail + /// ``` /// #![feature(slice_split_at_unchecked)] /// /// let mut v = [1, 0, 3, 0, 5, 6]; @@ -1624,7 +1624,7 @@ impl [T] { /// ``` #[unstable(feature = "slice_split_at_unchecked", reason = "new API", issue = "76014")] #[inline] - unsafe fn split_at_mut_unchecked(&mut self, mid: usize) -> (&mut [T], &mut [T]) { + pub unsafe fn split_at_mut_unchecked(&mut self, mid: usize) -> (&mut [T], &mut [T]) { let len = self.len(); let ptr = self.as_mut_ptr(); From c11d8f5be17c9a2a7514bc8b65247a724c2fc217 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Mon, 9 Aug 2021 12:30:40 +0200 Subject: [PATCH 017/621] Add warning to the jit mode section of the usage docs Closes #1192 --- docs/usage.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/usage.md b/docs/usage.md index 87eec0e818bb2..bcc5745d9d197 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -24,6 +24,8 @@ $ $cg_clif_dir/build/bin/cg_clif my_crate.rs ## Jit mode +> ⚠⚠⚠ The JIT mode is highly experimental. It may be slower than AOT compilation due to lack of incremental compilation. It may also be hard to setup if you have cargo dependencies. ⚠⚠⚠ + 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. From 1da50543dd6d1778856e24433d186fd39327def1 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Sat, 7 Aug 2021 14:11:54 +0200 Subject: [PATCH 018/621] Use the object crate for archive reading during archive building --- src/archive.rs | 61 ++++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/src/archive.rs b/src/archive.rs index 22897c43e7ef9..6c0e30664cd20 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -1,18 +1,21 @@ //! Creation of ar archives like for the lib and staticlib crate type use std::collections::BTreeMap; +use std::convert::TryFrom; use std::fs::File; +use std::io::{self, Read, Seek}; 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, ObjectSymbol, SymbolKind}; +use object::read::archive::ArchiveFile; +use object::{Object, ObjectSymbol, ReadCache, SymbolKind}; #[derive(Debug)] enum ArchiveEntry { - FromArchive { archive_index: usize, entry_index: usize }, + FromArchive { archive_index: usize, file_range: (u64, u64) }, File(PathBuf), } @@ -23,7 +26,7 @@ pub(crate) struct ArArchiveBuilder<'a> { use_gnu_style_archive: bool, no_builtin_ranlib: bool, - src_archives: Vec<(PathBuf, ar::Archive)>, + src_archives: Vec, // 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)>, @@ -34,20 +37,19 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { 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 read_cache = ReadCache::new(File::open(input).unwrap()); + let archive = ArchiveFile::parse(&read_cache).unwrap(); let mut entries = Vec::new(); - let mut i = 0; - while let Some(entry) = archive.next_entry() { + for entry in archive.members() { let entry = entry.unwrap(); entries.push(( - String::from_utf8(entry.header().identifier().to_vec()).unwrap(), - ArchiveEntry::FromArchive { archive_index: 0, entry_index: i }, + String::from_utf8(entry.name().to_vec()).unwrap(), + ArchiveEntry::FromArchive { archive_index: 0, file_range: entry.file_range() }, )); - i += 1; } - (vec![(input.to_owned(), archive)], entries) + (vec![read_cache.into_inner()], entries) } else { (vec![], Vec::new()) }; @@ -98,7 +100,7 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { name: &str, lto: bool, skip_objects: bool, - ) -> std::io::Result<()> { + ) -> io::Result<()> { let obj_start = name.to_owned(); self.add_archive(rlib.to_owned(), move |fname: &str| { @@ -141,14 +143,14 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { // 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 } => { + ArchiveEntry::FromArchive { archive_index, file_range } => { // 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(); + let src_read_cache = &mut self.src_archives[archive_index]; + + src_read_cache.seek(io::SeekFrom::Start(file_range.0)).unwrap(); + let mut data = std::vec::from_elem(0, usize::try_from(file_range.1).unwrap()); + src_read_cache.read_exact(&mut data).unwrap(); + data } ArchiveEntry::File(file) => std::fs::read(file).unwrap_or_else(|err| { @@ -266,26 +268,27 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { } impl<'a> ArArchiveBuilder<'a> { - fn add_archive(&mut self, archive_path: PathBuf, mut skip: F) -> std::io::Result<()> + fn add_archive(&mut self, archive_path: PathBuf, mut skip: F) -> io::Result<()> where F: FnMut(&str) -> bool + 'static, { - let mut archive = ar::Archive::new(std::fs::File::open(&archive_path)?); + let read_cache = ReadCache::new(std::fs::File::open(&archive_path)?); + let archive = ArchiveFile::parse(&read_cache).unwrap(); 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))?; + for entry in archive.members() { + let entry = entry.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; + let file_name = String::from_utf8(entry.name().to_vec()) + .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; if !skip(&file_name) { - self.entries - .push((file_name, ArchiveEntry::FromArchive { archive_index, entry_index: i })); + self.entries.push(( + file_name, + ArchiveEntry::FromArchive { archive_index, file_range: entry.file_range() }, + )); } - i += 1; } - self.src_archives.push((archive_path, archive)); + self.src_archives.push(read_cache.into_inner()); Ok(()) } } From 6681694cb5914073cb2502ee442e7f48f743664f Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Sat, 7 Aug 2021 14:28:07 +0200 Subject: [PATCH 019/621] Avoid converting filenames into strings where possible --- src/archive.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/archive.rs b/src/archive.rs index 6c0e30664cd20..47895f50780dc 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -29,7 +29,7 @@ pub(crate) struct ArArchiveBuilder<'a> { src_archives: Vec, // 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)>, + entries: Vec<(Vec, ArchiveEntry)>, } impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { @@ -44,7 +44,7 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { for entry in archive.members() { let entry = entry.unwrap(); entries.push(( - String::from_utf8(entry.name().to_vec()).unwrap(), + entry.name().to_vec(), ArchiveEntry::FromArchive { archive_index: 0, file_range: entry.file_range() }, )); } @@ -68,21 +68,21 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { } fn src_files(&mut self) -> Vec { - self.entries.iter().map(|(name, _)| name.clone()).collect() + self.entries.iter().map(|(name, _)| String::from_utf8(name.clone()).unwrap()).collect() } fn remove_file(&mut self, name: &str) { let index = self .entries .iter() - .position(|(entry_name, _)| entry_name == name) + .position(|(entry_name, _)| entry_name == name.as_bytes()) .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(), + file.file_name().unwrap().to_str().unwrap().to_string().into_bytes(), ArchiveEntry::File(file.to_owned()), )); } @@ -165,7 +165,7 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { match object::File::parse(&*data) { Ok(object) => { symbol_table.insert( - entry_name.as_bytes().to_vec(), + entry_name.to_vec(), object .symbols() .filter_map(|symbol| { @@ -190,7 +190,8 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { } else { sess.fatal(&format!( "error parsing `{}` during archive creation: {}", - entry_name, err + String::from_utf8_lossy(&entry_name), + err )); } } @@ -209,7 +210,7 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { err )); }), - entries.iter().map(|(name, _)| name.as_bytes().to_vec()).collect(), + entries.iter().map(|(name, _)| name.clone()).collect(), ar::GnuSymbolTableFormat::Size32, symbol_table, ) @@ -232,7 +233,7 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { // Add all files for (entry_name, data) in entries.into_iter() { - let header = ar::Header::new(entry_name.into_bytes(), data.len() as u64); + let header = ar::Header::new(entry_name, 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(), @@ -282,7 +283,7 @@ impl<'a> ArArchiveBuilder<'a> { .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; if !skip(&file_name) { self.entries.push(( - file_name, + file_name.into_bytes(), ArchiveEntry::FromArchive { archive_index, file_range: entry.file_range() }, )); } From d498e6d6971575714353f11aa4d3e63a9d2030b2 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Sat, 7 Aug 2021 14:28:28 +0200 Subject: [PATCH 020/621] Avoid an unnecessary allocation --- src/archive.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/archive.rs b/src/archive.rs index 47895f50780dc..6ba5f8e09e102 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -101,8 +101,6 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { lto: bool, skip_objects: bool, ) -> 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 { @@ -110,13 +108,13 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { } // Don't include Rust objects if LTO is enabled - if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") { + if lto && fname.starts_with(name) && 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")) { + if skip_objects && (!fname.starts_with(name) || !fname.ends_with(".o")) { return true; } @@ -271,7 +269,7 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { impl<'a> ArArchiveBuilder<'a> { fn add_archive(&mut self, archive_path: PathBuf, mut skip: F) -> io::Result<()> where - F: FnMut(&str) -> bool + 'static, + F: FnMut(&str) -> bool, { let read_cache = ReadCache::new(std::fs::File::open(&archive_path)?); let archive = ArchiveFile::parse(&read_cache).unwrap(); From 74b17685fe6d73189dcaf105bc2f4405f6719e7b Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Tue, 10 Aug 2021 11:50:33 +0100 Subject: [PATCH 021/621] Implement `black_box` using intrinsic The new implementation allows some `memcpy`s to be optimized away, so the uninit value in ui/sanitize/memory.rs is constructed directly onto the return place. Therefore the sanitizer now says that the value is allocated by `main` rather than `random`. --- src/intrinsics/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 8669846074749..1c4d307fc50e1 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -1136,6 +1136,11 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( }; ret.write_cvalue(fx, CValue::by_val(is_eq_value, ret.layout())); }; + + black_box, (c a) { + // FIXME implement black_box semantics + ret.write_cvalue(fx, a); + }; } if let Some((_, dest)) = destination { From afae271d5d3719eeb92c18bc004bb6d1965a5f3f Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Sun, 10 May 2020 10:54:30 -0400 Subject: [PATCH 022/621] Initial commit --- .github/FUNDING.yml | 2 + .github/workflows/main.yml | 93 + .gitignore | 17 + Cargo.lock | 373 ++++ Cargo.toml | 51 + LICENSE-APACHE | 176 ++ LICENSE-MIT | 23 + Readme.md | 118 ++ build.sh | 26 + build_sysroot/Cargo.toml | 19 + build_sysroot/build_sysroot.sh | 30 + build_sysroot/prepare_sysroot_src.sh | 39 + build_sysroot/src/lib.rs | 1 + cargo.sh | 23 + clean_all.sh | 5 + config.sh | 48 + example/alloc_example.rs | 41 + example/alloc_system.rs | 212 ++ ...itrary_self_types_pointers_and_wrappers.rs | 69 + example/dst-field-align.rs | 67 + example/example.rs | 208 ++ example/mini_core.rs | 585 ++++++ example/mini_core_hello_world.rs | 424 ++++ example/mod_bench.rs | 37 + example/std_example.rs | 279 +++ example/subslice-patterns-const-eval.rs | 97 + example/track-caller-attribute.rs | 40 + gcc_path | 0 ...022-core-Disable-not-compiling-tests.patch | 63 + patches/0023-core-Ignore-failing-tests.patch | 49 + prepare.sh | 22 + prepare_build.sh | 5 + rust-toolchain | 1 + rustup.sh | 29 + src/abi.rs | 286 +++ src/allocator.rs | 115 ++ src/archive.rs | 270 +++ src/asm.rs | 632 ++++++ src/back/mod.rs | 1 + src/back/write.rs | 234 +++ src/base.rs | 173 ++ src/builder.rs | 1812 +++++++++++++++++ src/callee.rs | 99 + src/common.rs | 448 ++++ src/consts.rs | 527 +++++ src/context.rs | 491 +++++ src/coverageinfo.rs | 140 ++ src/debuginfo.rs | 407 ++++ src/declare.rs | 220 ++ src/intrinsic/llvm.rs | 26 + src/intrinsic/mod.rs | 1286 ++++++++++++ src/intrinsic/simd.rs | 1001 +++++++++ src/lib.rs | 342 ++++ src/mangled_std_symbols.rs | 4 + src/mono_item.rs | 59 + src/type_.rs | 355 ++++ src/type_of.rs | 366 ++++ src/va_arg.rs | 179 ++ test.sh | 197 ++ tests/lib.rs | 50 + tests/run/abort1.rs | 51 + tests/run/abort2.rs | 53 + tests/run/array.rs | 229 +++ tests/run/asm.rs | 66 + tests/run/assign.rs | 153 ++ tests/run/closure.rs | 230 +++ tests/run/condition.rs | 320 +++ tests/run/empty_main.rs | 39 + tests/run/exit.rs | 49 + tests/run/exit_code.rs | 39 + tests/run/fun_ptr.rs | 223 ++ tests/run/int_overflow.rs | 129 ++ tests/run/mut_ref.rs | 165 ++ tests/run/operations.rs | 221 ++ tests/run/ptr_cast.rs | 222 ++ tests/run/return-tuple.rs | 72 + tests/run/slice.rs | 128 ++ tests/run/static.rs | 106 + tests/run/structs.rs | 70 + tests/run/tuple.rs | 51 + 80 files changed, 15608 insertions(+) create mode 100644 .github/FUNDING.yml create mode 100644 .github/workflows/main.yml create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 Readme.md create mode 100755 build.sh create mode 100644 build_sysroot/Cargo.toml create mode 100755 build_sysroot/build_sysroot.sh create mode 100755 build_sysroot/prepare_sysroot_src.sh create mode 100644 build_sysroot/src/lib.rs create mode 100755 cargo.sh create mode 100755 clean_all.sh create mode 100644 config.sh create mode 100644 example/alloc_example.rs create mode 100644 example/alloc_system.rs create mode 100644 example/arbitrary_self_types_pointers_and_wrappers.rs create mode 100644 example/dst-field-align.rs create mode 100644 example/example.rs create mode 100644 example/mini_core.rs create mode 100644 example/mini_core_hello_world.rs create mode 100644 example/mod_bench.rs create mode 100644 example/std_example.rs create mode 100644 example/subslice-patterns-const-eval.rs create mode 100644 example/track-caller-attribute.rs create mode 100644 gcc_path create mode 100644 patches/0022-core-Disable-not-compiling-tests.patch create mode 100644 patches/0023-core-Ignore-failing-tests.patch create mode 100755 prepare.sh create mode 100755 prepare_build.sh create mode 100644 rust-toolchain create mode 100755 rustup.sh create mode 100644 src/abi.rs create mode 100644 src/allocator.rs create mode 100644 src/archive.rs create mode 100644 src/asm.rs create mode 100644 src/back/mod.rs create mode 100644 src/back/write.rs create mode 100644 src/base.rs create mode 100644 src/builder.rs create mode 100644 src/callee.rs create mode 100644 src/common.rs create mode 100644 src/consts.rs create mode 100644 src/context.rs create mode 100644 src/coverageinfo.rs create mode 100644 src/debuginfo.rs create mode 100644 src/declare.rs create mode 100644 src/intrinsic/llvm.rs create mode 100644 src/intrinsic/mod.rs create mode 100644 src/intrinsic/simd.rs create mode 100644 src/lib.rs create mode 100644 src/mangled_std_symbols.rs create mode 100644 src/mono_item.rs create mode 100644 src/type_.rs create mode 100644 src/type_of.rs create mode 100644 src/va_arg.rs create mode 100755 test.sh create mode 100644 tests/lib.rs create mode 100644 tests/run/abort1.rs create mode 100644 tests/run/abort2.rs create mode 100644 tests/run/array.rs create mode 100644 tests/run/asm.rs create mode 100644 tests/run/assign.rs create mode 100644 tests/run/closure.rs create mode 100644 tests/run/condition.rs create mode 100644 tests/run/empty_main.rs create mode 100644 tests/run/exit.rs create mode 100644 tests/run/exit_code.rs create mode 100644 tests/run/fun_ptr.rs create mode 100644 tests/run/int_overflow.rs create mode 100644 tests/run/mut_ref.rs create mode 100644 tests/run/operations.rs create mode 100644 tests/run/ptr_cast.rs create mode 100644 tests/run/return-tuple.rs create mode 100644 tests/run/slice.rs create mode 100644 tests/run/static.rs create mode 100644 tests/run/structs.rs create mode 100644 tests/run/tuple.rs diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000000..cf2dcd4d099c2 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: antoyo +patreon: antoyo diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000000..774a68aa5e1a4 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,93 @@ +name: CI + +on: + - push + - pull_request + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + + steps: + - uses: actions/checkout@v2 + + - name: Install packages + run: sudo apt-get install ninja-build ripgrep + + - name: Download artifact + uses: dawidd6/action-download-artifact@v2 + with: + workflow: main.yml + name: libgccjit.so + path: gcc-build + repo: antoyo/gcc + + - name: Setup path to libgccjit + run: | + echo $(readlink -f gcc-build) > gcc_path + ln gcc-build/libgccjit.so gcc-build/libgccjit.so.0 + + - name: Set LIBRARY_PATH + run: echo "LIBRARY_PATH=$(cat gcc_path)" >> $GITHUB_ENV + + # https://github.com/actions/cache/issues/133 + - name: Fixup owner of ~/.cargo/ + # Don't remove the trailing /. It is necessary to follow the symlink. + run: sudo chown -R $(whoami):$(id -ng) ~/.cargo/ + + - name: Cache cargo installed crates + uses: actions/cache@v1.1.2 + with: + path: ~/.cargo/bin + key: cargo-installed-crates2-ubuntu-latest + + - name: Cache cargo registry + uses: actions/cache@v1 + with: + path: ~/.cargo/registry + key: ${{ runner.os }}-cargo-registry2-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache cargo index + uses: actions/cache@v1 + with: + path: ~/.cargo/git + key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} + + - name: Cache cargo target dir + uses: actions/cache@v1.1.2 + with: + path: target + key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('rust-toolchain') }} + + - name: Build + run: | + ./prepare_build.sh + ./build.sh + ./clean_all.sh + + - name: Prepare dependencies + run: | + git config --global user.email "user@example.com" + git config --global user.name "User" + ./prepare.sh + + # Compile is a separate step, as the actions-rs/cargo action supports error annotations + - name: Compile + uses: actions-rs/cargo@v1.0.3 + with: + command: build + args: --release + + - 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 --release diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000..d9f0ae05ed40d --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +target +**/*.rs.bk +*.rlib +*.o +perf.data +perf.data.old +*.events +*.string* +/build_sysroot/sysroot +/build_sysroot/Cargo.lock +/build_sysroot/test_target/Cargo.lock +/rust +/regex +gimple* +*asm +res +test-backend diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000000..952c03b05c7f4 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,373 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "ar" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "450575f58f7bee32816abbff470cbc47797397c2a81e0eaced4b98436daf52e1" + +[[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 = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68fda3cff2cce84c19e5dfa5179a4b35d2c0f18b893f108002b8a6a54984acca" +dependencies = [ + "regex", +] + +[[package]] +name = "gccjit" +version = "1.0.0" +source = "git+https://github.com/antoyo/gccjit.rs#0572117c7ffdfcb0e6c6526d45266c3f34796bea" +dependencies = [ + "gccjit_sys", +] + +[[package]] +name = "gccjit_sys" +version = "0.0.1" +source = "git+https://github.com/antoyo/gccjit.rs#0572117c7ffdfcb0e6c6526d45266c3f34796bea" +dependencies = [ + "libc 0.1.12", +] + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "libc 0.2.98", + "wasi", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc 0.2.98", +] + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "lang_tester" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd995a092cac79868250589869b5a5d656b02a02bd74c8ebdc566dc7203090" +dependencies = [ + "fm", + "getopts", + "libc 0.2.98", + "num_cpus", + "termcolor", + "threadpool", + "wait-timeout", + "walkdir", +] + +[[package]] +name = "libc" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e32a70cf75e5846d53a673923498228bbec6a8624708a9ea5645f075d6276122" + +[[package]] +name = "libc" +version = "0.2.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" + +[[package]] +name = "memchr" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc 0.2.98", +] + +[[package]] +name = "object" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a38f2be3697a57b4060074ff41b44c16870d916ad7877c17696e063257482bc7" +dependencies = [ + "crc32fast", + "indexmap", + "memchr", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc 0.2.98", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "rustc_codegen_gcc" +version = "0.1.0" +dependencies = [ + "ar", + "gccjit", + "lang_tester", + "object", + "target-lexicon", + "tempfile", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "target-lexicon" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab0e7238dcc7b40a7be719a25365910f6807bd864f4cce6b2e6b873658e2b19d" + +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if", + "libc 0.2.98", + "rand", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc 0.2.98", +] + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[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-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000000..9e8c195c15f60 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,51 @@ +[package] +name = "rustc_codegen_gcc" +version = "0.1.0" +authors = ["Antoni Boucher "] +edition = "2018" +license = "MIT OR Apache-2.0" + +[lib] +crate-type = ["dylib"] + +[[test]] +name = "lang_tests" +path = "tests/lib.rs" +harness = false + +[dependencies] +gccjit = { git = "https://github.com/antoyo/gccjit.rs" } + +# Local copy. +#gccjit = { path = "../gccjit.rs" } + +target-lexicon = "0.10.0" + +ar = "0.8.0" + +[dependencies.object] +version = "0.25.0" +default-features = false +features = ["read", "std", "write"] # We don't need WASM support. + +[dev-dependencies] +lang_tester = "0.3.9" +tempfile = "3.1.0" + +[profile.dev] +# By compiling dependencies with optimizations, performing tests gets much faster. +opt-level = 3 + +[profile.dev.package.rustc_codegen_gcc] +# Disabling optimizations for cg_gccjit itself makes compilation after a change faster. +opt-level = 0 + +# 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 diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000000000..1b5ec8b78e237 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000000000..31aa79387f27e --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000000000..44cb16334742f --- /dev/null +++ b/Readme.md @@ -0,0 +1,118 @@ +# WIP libgccjit codegen backend for rust + +This is a GCC codegen for rustc, which means it can be loaded by the existing rustc frontend, but benefits from GCC: more architectures are supported and GCC's optimizations are used. + +**Despite its name, libgccjit can be used for ahead-of-time compilation, as is used here.** + +## Motivation + +The primary goal of this project is to be able to compile Rust code on platforms unsupported by LLVM. +A secondary goal is to check if using the gcc backend will provide any run-time speed improvement for the programs compiled using rustc. + +## Building + +**This requires a patched libgccjit in order to work. +The patches in [this repostory](https://github.com/antoyo/libgccjit-patches) need to be applied. +(Those patches should work when applied on master, but in case it doesn't work, they are known to work when applied on 079c23cfe079f203d5df83fea8e92a60c7d7e878.) +You can also use my [fork of gcc](https://github.com/antoyo/gcc) which already includes these patches.** + +**Put the path to your custom build of libgccjit in the file `gcc_path`.** + +```bash +$ git clone https://github.com/antoyo/rustc_codegen_gcc.git +$ cd rustc_codegen_gcc +$ ./prepare_build.sh # download and patch sysroot src +$ ./build.sh --release +``` + +To run the tests: + +```bash +$ ./prepare.sh # download and patch sysroot src and install hyperfine for benchmarking +$ ./test.sh --release +``` + +## Usage + +`$cg_gccjit_dir` is the directory you cloned this repo into in the following instructions. + +### Cargo + +```bash +$ CHANNEL="release" $cg_gccjit_dir/cargo.sh run +``` + +If you compiled cg_gccjit in debug mode (aka you didn't pass `--release` to `./test.sh`) you should use `CHANNEL="debug"` instead or omit `CHANNEL="release"` completely. + +### Rustc + +> You should prefer using the Cargo method. + +```bash +$ rustc +$(cat $cg_gccjit_dir/rust-toolchain) -Cpanic=abort -Zcodegen-backend=$cg_gccjit_dir/target/release/librustc_codegen_gcc.so --sysroot $cg_gccjit_dir/build_sysroot/sysroot my_crate.rs +``` + +## Env vars + +
+
CG_GCCJIT_INCR_CACHE_DISABLED
+
Don't cache object files in the incremental cache. Useful during development of cg_gccjit + 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_gccjit.
+
CG_GCCJIT_DISPLAY_CG_TIME
+
Display the time it took to perform codegen for a crate
+
+ +## Debugging + +Sometimes, libgccjit will crash and output an error like this: + +``` +during RTL pass: expand +libgccjit.so: error: in expmed_mode_index, at expmed.h:249 +0x7f0da2e61a35 expmed_mode_index + ../../../gcc/gcc/expmed.h:249 +0x7f0da2e61aa4 expmed_op_cost_ptr + ../../../gcc/gcc/expmed.h:271 +0x7f0da2e620dc sdiv_cost_ptr + ../../../gcc/gcc/expmed.h:540 +0x7f0da2e62129 sdiv_cost + ../../../gcc/gcc/expmed.h:558 +0x7f0da2e73c12 expand_divmod(int, tree_code, machine_mode, rtx_def*, rtx_def*, rtx_def*, int) + ../../../gcc/gcc/expmed.c:4335 +0x7f0da2ea1423 expand_expr_real_2(separate_ops*, rtx_def*, machine_mode, expand_modifier) + ../../../gcc/gcc/expr.c:9240 +0x7f0da2cd1a1e expand_gimple_stmt_1 + ../../../gcc/gcc/cfgexpand.c:3796 +0x7f0da2cd1c30 expand_gimple_stmt + ../../../gcc/gcc/cfgexpand.c:3857 +0x7f0da2cd90a9 expand_gimple_basic_block + ../../../gcc/gcc/cfgexpand.c:5898 +0x7f0da2cdade8 execute + ../../../gcc/gcc/cfgexpand.c:6582 +``` + +To see the code which causes this error, call the following function: + +```c +gcc_jit_context_dump_to_file(ctxt, "/tmp/output.c", 1 /* update_locations */) +``` + +This will create a C-like file and add the locations into the IR pointing to this C file. +Then, rerun the program and it will output the location in the second line: + +``` +libgccjit.so: /tmp/something.c:61322:0: error: in expmed_mode_index, at expmed.h:249 +``` + +Or add a breakpoint to `add_error` in gdb and print the line number using: + +``` +p loc->m_line +``` + +### How to use a custom-build rustc + + * Build the stage1 compiler (`rustup toolchain link debug-current stage2 build/x86_64-unknown-linux-gnu/stage1`). + * Clean and rebuild the codegen with `debug-current` in the file `rust-toolchain`. + * Add `~/.rustup/toolchains/debug-current/lib/rustlib/x86_64-unknown-linux-gnu/lib` to `LD_LIBRARY_PATH`. diff --git a/build.sh b/build.sh new file mode 100755 index 0000000000000..9f1228687e27d --- /dev/null +++ b/build.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +#set -x +set -e + +export GCC_PATH=$(cat gcc_path) + +export LD_LIBRARY_PATH="$GCC_PATH" +export LIBRARY_PATH="$GCC_PATH" + +if [[ "$1" == "--release" ]]; then + export CHANNEL='release' + CARGO_INCREMENTAL=1 cargo rustc --release +else + echo $LD_LIBRARY_PATH + export CHANNEL='debug' + cargo rustc +fi + +source config.sh + +rm -r target/out || true +mkdir -p target/out/gccjit + +echo "[BUILD] sysroot" +time ./build_sysroot/build_sysroot.sh $CHANNEL diff --git a/build_sysroot/Cargo.toml b/build_sysroot/Cargo.toml new file mode 100644 index 0000000000000..cfadf47cc3f86 --- /dev/null +++ b/build_sysroot/Cargo.toml @@ -0,0 +1,19 @@ +[package] +authors = ["bjorn3 "] +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" } + +[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.release] +debug = true diff --git a/build_sysroot/build_sysroot.sh b/build_sysroot/build_sysroot.sh new file mode 100755 index 0000000000000..d1dcf495db8a3 --- /dev/null +++ b/build_sysroot/build_sysroot.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Requires the CHANNEL env var to be set to `debug` or `release.` + +set -e +cd $(dirname "$0") + +pushd ../ >/dev/null +source ./config.sh +popd >/dev/null + +# 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 +rm Cargo.lock test_target/Cargo.lock 2>/dev/null || true +rm -r sysroot/ 2>/dev/null || true + +# Build libs +export RUSTFLAGS="$RUSTFLAGS -Z force-unstable-if-unmarked -Cpanic=abort" +if [[ "$1" == "--release" ]]; then + sysroot_channel='release' + RUSTFLAGS="$RUSTFLAGS -Zmir-opt-level=3" cargo build --target $TARGET_TRIPLE --release +else + sysroot_channel='debug' + cargo build --target $TARGET_TRIPLE +fi + +# Copy files to sysroot +mkdir -p sysroot/lib/rustlib/$TARGET_TRIPLE/lib/ +cp -r target/$TARGET_TRIPLE/$sysroot_channel/deps/* sysroot/lib/rustlib/$TARGET_TRIPLE/lib/ diff --git a/build_sysroot/prepare_sysroot_src.sh b/build_sysroot/prepare_sysroot_src.sh new file mode 100755 index 0000000000000..4d18092419a4b --- /dev/null +++ b/build_sysroot/prepare_sysroot_src.sh @@ -0,0 +1,39 @@ +#!/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 -r $SRC_DIR/library $DST_DIR/ + +pushd $DST_DIR +echo "[GIT] init" +git init +echo "[GIT] add" +git add . +echo "[GIT] commit" + +# This is needed on virgin system where nothing is configured. +# git really needs something here, or it will fail. +# Even using --author is not enough. +git config user.email || git config user.email "none@example.com" +git config user.name || git config user.name "None" + +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" diff --git a/build_sysroot/src/lib.rs b/build_sysroot/src/lib.rs new file mode 100644 index 0000000000000..0c9ac1ac8e4bd --- /dev/null +++ b/build_sysroot/src/lib.rs @@ -0,0 +1 @@ +#![no_std] diff --git a/cargo.sh b/cargo.sh new file mode 100755 index 0000000000000..a85865019c135 --- /dev/null +++ b/cargo.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +if [ -z $CHANNEL ]; then +export CHANNEL='debug' +fi + +pushd $(dirname "$0") >/dev/null +source config.sh + +# read nightly compiler from rust-toolchain file +TOOLCHAIN=$(cat rust-toolchain) + +popd >/dev/null + +if [[ $(rustc -V) != $(rustc +${TOOLCHAIN} -V) ]]; then + echo "rustc_codegen_gcc is build for $(rustc +${TOOLCHAIN} -V) but the default rustc version is $(rustc -V)." + echo "Using $(rustc +${TOOLCHAIN} -V)." +fi + +cmd=$1 +shift + +RUSTDOCFLAGS=$RUSTFLAGS cargo +${TOOLCHAIN} $cmd --target $TARGET_TRIPLE $@ diff --git a/clean_all.sh b/clean_all.sh new file mode 100755 index 0000000000000..a77d1486fe283 --- /dev/null +++ b/clean_all.sh @@ -0,0 +1,5 @@ +#!/bin/bash --verbose +set -e + +rm -rf target/ build_sysroot/{sysroot/,sysroot_src/,target/,Cargo.lock} perf.data{,.old} +rm -rf regex/ simple-raytracer/ diff --git a/config.sh b/config.sh new file mode 100644 index 0000000000000..2311600d1cf57 --- /dev/null +++ b/config.sh @@ -0,0 +1,48 @@ +set -e + +export CARGO_INCREMENTAL=0 + +export GCC_PATH=$(cat gcc_path) + +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="aarch64-unknown-linux-gnu" + +linker='' +RUN_WRAPPER='' +if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then + 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' + else + echo "Unknown non-native platform" + fi +fi + +export RUSTFLAGS=$linker' -Cpanic=abort -Cdebuginfo=2 -Zpanic-abort-tests -Zcodegen-backend='$(pwd)'/target/'$CHANNEL'/librustc_codegen_gcc.'$dylib_ext' --sysroot '$(pwd)'/build_sysroot/sysroot' +#export RUSTFLAGS=$linker' -Cpanic=abort -Cdebuginfo=2 -Zpanic-abort-tests -Zcodegen-backend='$(pwd)'/target/'$CHANNEL'/librustc_codegen_gcc.'$dylib_ext' --sysroot '$(pwd)'/build_sysroot/sysroot -Clto=fat -Cembed-bitcode=yes' + +# FIXME remove once the atomic shim is gone +if [[ `uname` == 'Darwin' ]]; then + export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-undefined -Clink-arg=dynamic_lookup" +fi + +RUSTC="rustc $RUSTFLAGS -L crate=target/out --out-dir target/out" +export RUSTC_LOG=warn # display metadata load errors + +export LD_LIBRARY_PATH="$(pwd)/target/out:$(pwd)/build_sysroot/sysroot/lib/rustlib/$TARGET_TRIPLE/lib:$GCC_PATH" +export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH + +export CG_CLIF_DISPLAY_CG_TIME=1 +export CG_CLIF_INCR_CACHE_DISABLED=1 diff --git a/example/alloc_example.rs b/example/alloc_example.rs new file mode 100644 index 0000000000000..bc6dd007ba010 --- /dev/null +++ b/example/alloc_example.rs @@ -0,0 +1,41 @@ +#![feature(start, box_syntax, 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) -> ! { + unsafe { + core::intrinsics::abort(); + } +} + +#[alloc_error_handler] +fn alloc_error_handler(_: alloc::alloc::Layout) -> ! { + unsafe { + 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 +} diff --git a/example/alloc_system.rs b/example/alloc_system.rs new file mode 100644 index 0000000000000..5f66ca67f2d40 --- /dev/null +++ b/example/alloc_system.rs @@ -0,0 +1,212 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![no_std] +#![feature(allocator_api, rustc_private)] +#![cfg_attr(any(unix, 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")))] +const MIN_ALIGN: usize = 8; +#[cfg(all(any(target_arch = "x86_64", + target_arch = "aarch64", + target_arch = "mips64", + target_arch = "s390x", + target_arch = "sparc64")))] +const MIN_ALIGN: usize = 16; + +pub struct System; +#[cfg(any(windows, unix, 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 = "redox"))] +mod platform { + extern crate libc; + use core::ptr; + use MIN_ALIGN; + use System; + use core::alloc::{GlobalAlloc, Layout}; + 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 + } + 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) + } + } + } +} diff --git a/example/arbitrary_self_types_pointers_and_wrappers.rs b/example/arbitrary_self_types_pointers_and_wrappers.rs new file mode 100644 index 0000000000000..ddeb752f93ed7 --- /dev/null +++ b/example/arbitrary_self_types_pointers_and_wrappers.rs @@ -0,0 +1,69 @@ +// Adapted from rustc run-pass test suite + +#![feature(arbitrary_self_types, unsize, coerce_unsized, dispatch_from_dyn)] +#![feature(rustc_attrs)] + +use std::{ + ops::{Deref, CoerceUnsized, DispatchFromDyn}, + marker::Unsize, +}; + +struct Ptr(Box); + +impl Deref for Ptr { + type Target = T; + + fn deref(&self) -> &T { + &*self.0 + } +} + +impl + ?Sized, U: ?Sized> CoerceUnsized> for Ptr {} +impl + ?Sized, U: ?Sized> DispatchFromDyn> for Ptr {} + +struct Wrapper(T); + +impl Deref for Wrapper { + type Target = T; + + fn deref(&self) -> &T { + &self.0 + } +} + +impl, U> CoerceUnsized> for Wrapper {} +impl, U> DispatchFromDyn> for Wrapper {} + + +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) -> i32; + fn ptr_wrapper(self: Ptr>) -> i32; + fn wrapper_ptr(self: Wrapper>) -> i32; + fn wrapper_ptr_wrapper(self: Wrapper>>) -> i32; +} + +impl Trait for i32 { + fn ptr_wrapper(self: Ptr>) -> i32 { + **self + } + fn wrapper_ptr(self: Wrapper>) -> i32 { + **self + } + fn wrapper_ptr_wrapper(self: Wrapper>>) -> i32 { + ***self + } +} + +fn main() { + let pw = Ptr(Box::new(Wrapper(5))) as Ptr>; + assert_eq!(pw.ptr_wrapper(), 5); + + let wp = Wrapper(Ptr(Box::new(6))) as Wrapper>; + assert_eq!(wp.wrapper_ptr(), 6); + + let wpw = Wrapper(Ptr(Box::new(Wrapper(7)))) as Wrapper>>; + assert_eq!(wpw.wrapper_ptr_wrapper(), 7); +} diff --git a/example/dst-field-align.rs b/example/dst-field-align.rs new file mode 100644 index 0000000000000..6c338e99912ec --- /dev/null +++ b/example/dst-field-align.rs @@ -0,0 +1,67 @@ +// run-pass +#![allow(dead_code)] +struct Foo { + a: u16, + b: T +} + +trait Bar { + fn get(&self) -> usize; +} + +impl Bar for usize { + fn get(&self) -> usize { *self } +} + +struct Baz { + a: T +} + +struct HasDrop { + ptr: Box, + data: T +} + +fn main() { + // Test that zero-offset works properly + let b : Baz = Baz { a: 7 }; + assert_eq!(b.a.get(), 7); + let b : &Baz = &b; + assert_eq!(b.a.get(), 7); + + // Test that the field is aligned properly + let f : Foo = 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 = &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 { a: 0, b: Foo { a: 1, b: 17 }}; + assert_eq!(f.b.b.get(), 17); + let f : &Foo> = &f; + assert_eq!(f.b.b.get(), 17); + + // Test that get the pointer via destructuring works + + let f : Foo = Foo { a: 0, b: 11 }; + let f : &Foo = &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> = HasDrop { + ptr: Box::new(0), + data: Baz { a: [1,2,3,4] } + }; + assert_eq!([1,2,3,4], d.data.a); + + let d : &HasDrop> = &d; + assert_eq!(&[1,2,3,4], &d.data.a); +} diff --git a/example/example.rs b/example/example.rs new file mode 100644 index 0000000000000..5878e8548d926 --- /dev/null +++ b/example/example.rs @@ -0,0 +1,208 @@ +#![feature(no_core, unboxed_closures)] +#![no_core] +#![allow(dead_code)] + +extern crate mini_core; + +use mini_core::*; + +fn abc(a: u8) -> u8 { + a * 2 +} + +fn bcd(b: bool, a: u8) -> u8 { + if b { + a * 2 + } else { + a * 3 + } +} + +fn call() { + abc(42); +} + +fn indirect_call() { + let f: fn() = call; + f(); +} + +enum BoolOption { + Some(bool), + None, +} + +fn option_unwrap_or(o: BoolOption, d: bool) -> bool { + match o { + BoolOption::Some(b) => b, + BoolOption::None => d, + } +} + +fn ret_42() -> u8 { + 42 +} + +fn return_str() -> &'static str { + "hello world" +} + +fn promoted_val() -> &'static u8 { + &(1 * 2) +} + +fn cast_ref_to_raw_ptr(abc: &u8) -> *const u8 { + abc as *const u8 +} + +fn cmp_raw_ptr(a: *const u8, b: *const u8) -> bool { + a == b +} + +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, + ) +} + +fn char_cast(c: char) -> u8 { + c as u8 +} + +pub struct DebugTuple(()); + +fn debug_tuple() -> DebugTuple { + DebugTuple(()) +} + +fn size_of() -> usize { + intrinsics::size_of::() +} + +fn use_size_of() -> usize { + size_of::() +} + +unsafe fn use_copy_intrinsic(src: *const u8, dst: *mut u8) { + intrinsics::copy::(src, dst, 1); +} + +unsafe fn use_copy_intrinsic_ref(src: *const u8, dst: *mut u8) { + let copy2 = &intrinsics::copy::; + copy2(src, dst, 1); +} + +const ABC: u8 = 6 * 7; + +fn use_const() -> u8 { + ABC +} + +pub fn call_closure_3arg() { + (|_, _, _| {})(0u8, 42u16, 0u8) +} + +pub fn call_closure_2arg() { + (|_, _| {})(0u8, 42u16) +} + +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 &[_]),)); +} + +fn eq_char(a: char, b: char) -> bool { + a == b +} + +unsafe fn transmute(c: char) -> u32 { + intrinsics::transmute(c) +} + +unsafe fn deref_str_ptr(s: *const str) -> &'static str { + &*s +} + +fn use_array(arr: [u8; 3]) -> u8 { + arr[1] +} + +fn repeat_array() -> [u8; 3] { + [0; 3] +} + +fn array_as_slice(arr: &[u8; 3]) -> &[u8] { + arr +} + +unsafe fn use_ctlz_nonzero(a: u16) -> u16 { + intrinsics::ctlz_nonzero(a) +} + +fn ptr_as_usize(ptr: *const u8) -> usize { + ptr as usize +} + +fn float_cast(a: f32, b: f64) -> (f64, f32) { + (a as f64, b as f32) +} + +fn int_to_float(a: u8, b: i32) -> (f64, f32) { + (a as f64, b as f32) +} + +fn make_array() -> [u8; 3] { + [42, 0, 5] +} + +fn some_promoted_tuple() -> &'static (&'static str, &'static str) { + &("abc", "some") +} + +fn index_slice(s: &[u8]) -> u8 { + s[2] +} + +pub struct StrWrapper { + s: str, +} + +fn str_wrapper_get(w: &StrWrapper) -> &str { + &w.s +} + +fn i16_as_i8(a: i16) -> i8 { + a as i8 +} + +struct Unsized(u8, str); + +fn get_sized_field_ref_from_unsized_type(u: &Unsized) -> &u8 { + &u.0 +} + +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 +} diff --git a/example/mini_core.rs b/example/mini_core.rs new file mode 100644 index 0000000000000..1067cee881487 --- /dev/null +++ b/example/mini_core.rs @@ -0,0 +1,585 @@ +#![feature( + no_core, lang_items, intrinsics, unboxed_closures, type_ascription, extern_types, + untagged_unions, decl_macro, rustc_attrs, transparent_unions, auto_traits, + thread_local +)] +#![no_core] +#![allow(dead_code)] + +#[no_mangle] +unsafe extern "C" fn _Unwind_Resume() { + intrinsics::unreachable(); +} + +#[lang = "sized"] +pub trait Sized {} + +#[lang = "unsize"] +pub trait Unsize {} + +#[lang = "coerce_unsized"] +pub trait CoerceUnsized {} + +impl<'a, 'b: 'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} +impl<'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {} +impl, U: ?Sized> CoerceUnsized<*const U> for *const T {} +impl, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} + +#[lang = "dispatch_from_dyn"] +pub trait DispatchFromDyn {} + +// &T -> &U +impl<'a, T: ?Sized+Unsize, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {} +// &mut T -> &mut U +impl<'a, T: ?Sized+Unsize, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {} +// *const T -> *const U +impl, U: ?Sized> DispatchFromDyn<*const U> for *const T {} +// *mut T -> *mut U +impl, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {} +impl, U: ?Sized> DispatchFromDyn> for Box {} + +#[lang = "receiver"] +pub trait Receiver {} + +impl Receiver for &T {} +impl Receiver for &mut T {} +impl Receiver for Box {} + +#[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 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 Copy for *const T {} +unsafe impl Copy for *mut 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 Freeze for PhantomData {} +unsafe impl Freeze for *const T {} +unsafe impl Freeze for *mut T {} +unsafe impl Freeze for &T {} +unsafe impl 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 { + 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 { + 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 { + 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 { + 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 { + 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 for &'a bool { + type Output = bool; + + fn bitor(self, rhs: bool) -> bool { + *self | rhs + } +} + +#[lang = "eq"] +pub trait PartialEq { + 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 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 PartialEq for *const T { + fn eq(&self, other: &*const T) -> bool { + *self == *other + } + fn ne(&self, other: &*const T) -> bool { + *self != *other + } +} + +#[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 { + Some(T), + None, +} + +pub use Option::*; + +#[lang = "phantom_data"] +pub struct PhantomData; + +#[lang = "fn_once"] +#[rustc_paren_sugar] +pub trait FnOnce { + #[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: FnOnce { + 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 u8); + 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(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(*mut T); + +impl, U: ?Sized> CoerceUnsized> for Box {} + +impl Drop for Box { + fn drop(&mut self) { + // drop is currently performed by compiler. + } +} + +impl Deref for Box { + 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(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 { + pub value: T, +} + +#[lang = "maybe_uninit"] +#[repr(transparent)] +pub union MaybeUninit { + pub uninit: (), + pub value: ManuallyDrop, +} + +pub mod intrinsics { + extern "rust-intrinsic" { + pub fn abort() -> !; + pub fn size_of() -> usize; + pub fn size_of_val(val: *const T) -> usize; + pub fn min_align_of() -> usize; + pub fn min_align_of_val(val: *const T) -> usize; + pub fn copy(src: *const T, dst: *mut T, count: usize); + pub fn transmute(e: T) -> U; + pub fn ctlz_nonzero(x: T) -> T; + pub fn needs_drop() -> bool; + pub fn bitreverse(x: T) -> T; + pub fn bswap(x: T) -> T; + pub fn write_bytes(dst: *mut T, val: u8, count: usize); + pub fn unreachable() -> !; + } +} + +pub mod libc { + #[link(name = "c")] + extern "C" { + pub fn puts(s: *const u8) -> 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 { + type Output: ?Sized; + fn index(&self, index: Idx) -> &Self::Output; +} + +impl Index for [T; 3] { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self[index] + } +} + +impl Index 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 */ } + +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 +} diff --git a/example/mini_core_hello_world.rs b/example/mini_core_hello_world.rs new file mode 100644 index 0000000000000..ccf4b53a702e5 --- /dev/null +++ b/example/mini_core_hello_world.rs @@ -0,0 +1,424 @@ +// Adapted from https://github.com/sunfishcode/mir2cranelift/blob/master/rust-examples/nocore-hello-world.rs + +#![feature( + no_core, unboxed_closures, 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 u8) { + 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 u8); + } + } +} + +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 u8); + } + } +} + +impl Drop for NoisyDropInner { + fn drop(&mut self) { + unsafe { + puts("Inner got dropped!\0" as *const str as *const u8); + } + } +} + +impl SomeTrait for NoisyDrop { + fn object_safe(&self) {} +} + +enum Ordering { + Less = -1, + Equal = 0, + Greater = 1, +} + +#[lang = "start"] +fn start( + main: fn() -> T, + argc: isize, + argv: *const *const u8, +) -> isize { + if argc == 3 { + unsafe { puts(*argv); } + unsafe { puts(*((argv as usize + intrinsics::size_of::<*const u8>()) as *const *const u8)); } + unsafe { puts(*((argv as usize + 2 * intrinsics::size_of::<*const u8>()) as *const *const u8)); } + } + + 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 { + pointer: *const T, + _marker: PhantomData, +} + +impl CoerceUnsized> for Unique where T: Unsize {} + +unsafe fn zeroed() -> 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 u8 = hello as *const [u8] as *const u8; + puts(ptr); + + let world: Box<&str> = box "World!\0"; + puts(*world as *const str as *const u8); + world as Box; + + 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::() as u8, 2); + assert_eq!(intrinsics::min_align_of_val(&a) as u8, intrinsics::min_align_of::<&str>() as u8); + + assert!(!intrinsics::needs_drop::()); + assert!(intrinsics::needs_drop::()); + + Unique { + pointer: 0 as *const &str, + _marker: PhantomData, + } as Unique; + + struct MyDst(T); + + intrinsics::size_of_val(&MyDst([0u8; 4]) as &MyDst<[u8]>); + + struct Foo { + x: u8, + y: !, + } + + unsafe fn uninitialized() -> T { + MaybeUninit { uninit: () }.value.value + } + + zeroed::<(u8, u8)>(); + #[allow(unreachable_code)] + { + if false { + zeroed::(); + zeroed::(); + uninitialized::(); + } + } + } + + let _ = box NoisyDrop { + text: "Boxed outer got dropped!\0", + inner: NoisyDropInner, + } as Box; + + const FUNC_REF: Option = 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); + + extern { + #[linkage = "weak"] + static ABC: *const u8; + } + + { + extern { + #[linkage = "weak"] + static ABC: *const u8; + } + } + + // TODO: not sure about this assert. ABC is not defined, so should it be really 0? + //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; + + #[cfg(not(jit))] + test_tls(); +} + +#[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 u8); + } +} + +// 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 { + 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:: { + intrinsics::abort(); + } +} diff --git a/example/mod_bench.rs b/example/mod_bench.rs new file mode 100644 index 0000000000000..2e2b0052dee8b --- /dev/null +++ b/example/mod_bench.rs @@ -0,0 +1,37 @@ +#![feature(start, box_syntax, core_intrinsics, lang_items)] +#![no_std] + +#[link(name = "c")] +extern {} + +#[panic_handler] +fn panic_handler(_: &core::panic::PanicInfo) -> ! { + unsafe { + 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..100_000_000 { + black_box((i + 1) % i); + } + + 0 +} + +#[inline(never)] +fn black_box(i: u32) { + if i != 1 { + unsafe { core::intrinsics::abort(); } + } +} diff --git a/example/std_example.rs b/example/std_example.rs new file mode 100644 index 0000000000000..d99d6bb4b855e --- /dev/null +++ b/example/std_example.rs @@ -0,0 +1,279 @@ +#![feature(core_intrinsics, generators, generator_trait, is_sorted)] + +use std::arch::x86_64::*; +use std::io::Write; +use std::ops::Generator; + +extern { + pub fn printf(format: *const i8, ...) -> i32; +} + +fn main() { + let mutex = std::sync::Mutex::new(()); + let _guard = mutex.lock().unwrap(); + + let _ = ::std::iter::repeat('a' as u8).take(10).collect::>(); + let stderr = ::std::io::stderr(); + let mut stderr = stderr.lock(); + + // FIXME: this thread panics. + std::thread::spawn(move || { + println!("Hello from another thread!"); + }); + + writeln!(stderr, "some {} text", "").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)>); + + 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!(-32768i16, (-32768i16).saturating_add(-32768)); + assert_eq!(32767i16, 32767i16.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);*/ + + let _a = 1u32 << 2u8; + + let empty: [i32; 0] = []; + assert!(empty.is_sorted()); + + println!("{:?}", std::intrinsics::caller_location()); + + /*unsafe { + test_simd(); + }*/ + + Box::pin(move |mut _task_context| { + yield (); + }).as_mut().resume(0); + + println!("End"); +} + +/*#[target_feature(enable = "sse2")] +unsafe fn test_simd() { + 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(); + + // FIXME(#666) implement `#[rustc_arg_required_const(..)]` support + //test_mm_extract_epi8(); + + 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);*/ +}*/ + +/*#[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)); +} + +#[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); +} + +#[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); +} + +#[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); +} + +#[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)); +} + +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)); + } +} + +#[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); + } +} + +#[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); +} + +#[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); +} + +#[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); +}*/ + +#[derive(PartialEq)] +enum LoopState { + Continue(()), + Break(()) +} + +pub enum Instruction { + Increment, + Loop, +} + +fn map(a: Option<(u8, Box)>) -> Option> { + match a { + None => None, + Some((_, instr)) => Some(instr), + } +} diff --git a/example/subslice-patterns-const-eval.rs b/example/subslice-patterns-const-eval.rs new file mode 100644 index 0000000000000..2cb84786f56d0 --- /dev/null +++ b/example/subslice-patterns-const-eval.rs @@ -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); +} diff --git a/example/track-caller-attribute.rs b/example/track-caller-attribute.rs new file mode 100644 index 0000000000000..93bab17e46b27 --- /dev/null +++ b/example/track-caller-attribute.rs @@ -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); +} diff --git a/gcc_path b/gcc_path new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/patches/0022-core-Disable-not-compiling-tests.patch b/patches/0022-core-Disable-not-compiling-tests.patch new file mode 100644 index 0000000000000..aae62a938b457 --- /dev/null +++ b/patches/0022-core-Disable-not-compiling-tests.patch @@ -0,0 +1,63 @@ +From f6befc4bb51d84f5f1cf35938a168c953d421350 Mon Sep 17 00:00:00 2001 +From: bjorn3 +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(v: T) -> Decoded { + match decode(v).1 { +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) diff --git a/patches/0023-core-Ignore-failing-tests.patch b/patches/0023-core-Ignore-failing-tests.patch new file mode 100644 index 0000000000000..ee5ba449fb8e6 --- /dev/null +++ b/patches/0023-core-Ignore-failing-tests.patch @@ -0,0 +1,49 @@ +From dd82e95c9de212524e14fc60155de1ae40156dfc Mon Sep 17 00:00:00 2001 +From: bjorn3 +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() { +-- 2.21.0 (Apple Git-122) diff --git a/prepare.sh b/prepare.sh new file mode 100755 index 0000000000000..503fa29b36269 --- /dev/null +++ b/prepare.sh @@ -0,0 +1,22 @@ +#!/bin/bash --verbose +set -e + +source prepare_build.sh + +cargo install hyperfine || echo "Skipping hyperfine install" + +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 diff --git a/prepare_build.sh b/prepare_build.sh new file mode 100755 index 0000000000000..ccf5350983015 --- /dev/null +++ b/prepare_build.sh @@ -0,0 +1,5 @@ +#!/bin/bash --verbose +set -e + +rustup component add rust-src rustc-dev llvm-tools-preview +./build_sysroot/prepare_sysroot_src.sh diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000000000..3a29ac5ddb120 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly-2021-07-21 diff --git a/rustup.sh b/rustup.sh new file mode 100755 index 0000000000000..01ce5bb78be0f --- /dev/null +++ b/rustup.sh @@ -0,0 +1,29 @@ +#!/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 + + 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 + ;; + "commit") + git add rust-toolchain + git commit -m "Rustup to $(rustc -V)" + ;; + *) + echo "Unknown command '$1'" + echo "Usage: ./rustup.sh prepare|commit" + ;; +esac diff --git a/src/abi.rs b/src/abi.rs new file mode 100644 index 0000000000000..95d536dc51c5f --- /dev/null +++ b/src/abi.rs @@ -0,0 +1,286 @@ +use gccjit::{ToRValue, Type}; +use rustc_codegen_ssa::traits::{AbiBuilderMethods, BaseTypeMethods}; +use rustc_middle::bug; +use rustc_middle::ty::Ty; +use rustc_target::abi::call::{CastTarget, FnAbi, PassMode, Reg, RegKind}; + +use crate::builder::Builder; +use crate::context::CodegenCx; +use crate::intrinsic::ArgAbiExt; +use crate::type_of::LayoutGccExt; + +impl<'a, 'gcc, 'tcx> AbiBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { + fn apply_attrs_callsite(&mut self, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _callsite: Self::Value) { + // TODO + //fn_abi.apply_attrs_callsite(self, callsite) + } + + fn get_param(&self, index: usize) -> Self::Value { + self.cx.current_func.borrow().expect("current func") + .get_param(index as i32) + .to_rvalue() + } +} + +impl GccType for CastTarget { + fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, '_>) -> Type<'gcc> { + let rest_gcc_unit = self.rest.unit.gcc_type(cx); + let (rest_count, rem_bytes) = + if self.rest.unit.size.bytes() == 0 { + (0, 0) + } + else { + (self.rest.total.bytes() / self.rest.unit.size.bytes(), self.rest.total.bytes() % self.rest.unit.size.bytes()) + }; + + if self.prefix.iter().all(|x| x.is_none()) { + // Simplify to a single unit when there is no prefix and size <= unit size + if self.rest.total <= self.rest.unit.size { + return rest_gcc_unit; + } + + // Simplify to array when all chunks are the same size and type + if rem_bytes == 0 { + return cx.type_array(rest_gcc_unit, rest_count); + } + } + + // Create list of fields in the main structure + let mut args: Vec<_> = self + .prefix + .iter() + .flat_map(|option_kind| { + option_kind.map(|kind| Reg { kind, size: self.prefix_chunk_size }.gcc_type(cx)) + }) + .chain((0..rest_count).map(|_| rest_gcc_unit)) + .collect(); + + // Append final integer + if rem_bytes != 0 { + // Only integers can be really split further. + assert_eq!(self.rest.unit.kind, RegKind::Integer); + args.push(cx.type_ix(rem_bytes * 8)); + } + + cx.type_struct(&args, false) + } +} + +pub trait GccType { + fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, '_>) -> Type<'gcc>; +} + +impl GccType for Reg { + fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, '_>) -> Type<'gcc> { + match self.kind { + RegKind::Integer => cx.type_ix(self.size.bits()), + RegKind::Float => { + match self.size.bits() { + 32 => cx.type_f32(), + 64 => cx.type_f64(), + _ => bug!("unsupported float: {:?}", self), + } + }, + RegKind::Vector => unimplemented!(), //cx.type_vector(cx.type_i8(), self.size.bytes()), + } + } +} + +pub trait FnAbiGccExt<'gcc, 'tcx> { + // TODO: return a function pointer type instead? + fn gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> (Type<'gcc>, Vec>, bool); + fn ptr_to_gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>; + /*fn llvm_cconv(&self) -> llvm::CallConv; + fn apply_attrs_llfn(&self, cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value); + fn apply_attrs_callsite(&self, bx: &mut Builder<'a, 'll, 'tcx>, callsite: &'ll Value);*/ +} + +impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { + fn gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> (Type<'gcc>, Vec>, bool) { + let args_capacity: usize = self.args.iter().map(|arg| + if arg.pad.is_some() { + 1 + } + else { + 0 + } + + if let PassMode::Pair(_, _) = arg.mode { + 2 + } else { + 1 + } + ).sum(); + let mut argument_tys = Vec::with_capacity( + if let PassMode::Indirect { .. } = self.ret.mode { + 1 + } + else { + 0 + } + args_capacity, + ); + + let return_ty = + match self.ret.mode { + PassMode::Ignore => cx.type_void(), + PassMode::Direct(_) | PassMode::Pair(..) => self.ret.layout.immediate_gcc_type(cx), + PassMode::Cast(cast) => cast.gcc_type(cx), + PassMode::Indirect { .. } => { + argument_tys.push(cx.type_ptr_to(self.ret.memory_ty(cx))); + cx.type_void() + } + }; + + for arg in &self.args { + // add padding + if let Some(ty) = arg.pad { + argument_tys.push(ty.gcc_type(cx)); + } + + let arg_ty = match arg.mode { + PassMode::Ignore => continue, + PassMode::Direct(_) => arg.layout.immediate_gcc_type(cx), + PassMode::Pair(..) => { + argument_tys.push(arg.layout.scalar_pair_element_gcc_type(cx, 0, true)); + argument_tys.push(arg.layout.scalar_pair_element_gcc_type(cx, 1, true)); + continue; + } + PassMode::Indirect { extra_attrs: Some(_), .. } => { + /*let ptr_ty = cx.tcx.mk_mut_ptr(arg.layout.ty); + let ptr_layout = cx.layout_of(ptr_ty); + argument_tys.push(ptr_layout.scalar_pair_element_gcc_type(cx, 0, true)); + argument_tys.push(ptr_layout.scalar_pair_element_gcc_type(cx, 1, true));*/ + unimplemented!(); + //continue; + } + PassMode::Cast(cast) => cast.gcc_type(cx), + PassMode::Indirect { extra_attrs: None, .. } => cx.type_ptr_to(arg.memory_ty(cx)), + }; + argument_tys.push(arg_ty); + } + + (return_ty, argument_tys, self.c_variadic) + } + + fn ptr_to_gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> { + let (return_type, params, variadic) = self.gcc_type(cx); + let pointer_type = cx.context.new_function_pointer_type(None, return_type, ¶ms, variadic); + pointer_type + } + + /*fn llvm_cconv(&self) -> llvm::CallConv { + match self.conv { + Conv::C | Conv::Rust => llvm::CCallConv, + Conv::AmdGpuKernel => llvm::AmdGpuKernel, + Conv::ArmAapcs => llvm::ArmAapcsCallConv, + Conv::Msp430Intr => llvm::Msp430Intr, + Conv::PtxKernel => llvm::PtxKernel, + Conv::X86Fastcall => llvm::X86FastcallCallConv, + Conv::X86Intr => llvm::X86_Intr, + Conv::X86Stdcall => llvm::X86StdcallCallConv, + Conv::X86ThisCall => llvm::X86_ThisCall, + Conv::X86VectorCall => llvm::X86_VectorCall, + Conv::X86_64SysV => llvm::X86_64_SysV, + Conv::X86_64Win64 => llvm::X86_64_Win64, + } + } + + fn apply_attrs_llfn(&self, cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value) { + // FIXME(eddyb) can this also be applied to callsites? + if self.ret.layout.abi.is_uninhabited() { + llvm::Attribute::NoReturn.apply_llfn(llvm::AttributePlace::Function, llfn); + } + + // FIXME(eddyb, wesleywiser): apply this to callsites as well? + if !self.can_unwind { + llvm::Attribute::NoUnwind.apply_llfn(llvm::AttributePlace::Function, llfn); + } + + let mut i = 0; + let mut apply = |attrs: &ArgAttributes, ty: Option<&Type>| { + attrs.apply_llfn(llvm::AttributePlace::Argument(i), llfn, ty); + i += 1; + }; + match self.ret.mode { + PassMode::Direct(ref attrs) => { + attrs.apply_llfn(llvm::AttributePlace::ReturnValue, llfn, None); + } + PassMode::Indirect(ref attrs, _) => apply(attrs, Some(self.ret.layout.gcc_type(cx))), + _ => {} + } + for arg in &self.args { + if arg.pad.is_some() { + apply(&ArgAttributes::new(), None); + } + match arg.mode { + PassMode::Ignore => {} + PassMode::Direct(ref attrs) | PassMode::Indirect(ref attrs, None) => { + apply(attrs, Some(arg.layout.gcc_type(cx))) + } + PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => { + apply(attrs, None); + apply(extra_attrs, None); + } + PassMode::Pair(ref a, ref b) => { + apply(a, None); + apply(b, None); + } + PassMode::Cast(_) => apply(&ArgAttributes::new(), None), + } + } + } + + fn apply_attrs_callsite(&self, bx: &mut Builder<'a, 'll, 'tcx>, callsite: &'ll Value) { + // FIXME(wesleywiser, eddyb): We should apply `nounwind` and `noreturn` as appropriate to this callsite. + + let mut i = 0; + let mut apply = |attrs: &ArgAttributes, ty: Option<&Type>| { + attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite, ty); + i += 1; + }; + match self.ret.mode { + PassMode::Direct(ref attrs) => { + attrs.apply_callsite(llvm::AttributePlace::ReturnValue, callsite, None); + } + PassMode::Indirect(ref attrs, _) => apply(attrs, Some(self.ret.layout.gcc_type(bx))), + _ => {} + } + if let abi::Abi::Scalar(ref scalar) = self.ret.layout.abi { + // If the value is a boolean, the range is 0..2 and that ultimately + // become 0..0 when the type becomes i1, which would be rejected + // by the LLVM verifier. + if let Int(..) = scalar.value { + if !scalar.is_bool() { + let range = scalar.valid_range_exclusive(bx); + if range.start != range.end { + bx.range_metadata(callsite, range); + } + } + } + } + for arg in &self.args { + if arg.pad.is_some() { + apply(&ArgAttributes::new(), None); + } + match arg.mode { + PassMode::Ignore => {} + PassMode::Direct(ref attrs) | PassMode::Indirect(ref attrs, None) => { + apply(attrs, Some(arg.layout.gcc_type(bx))) + } + PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => { + apply(attrs, None); + apply(extra_attrs, None); + } + PassMode::Pair(ref a, ref b) => { + apply(a, None); + apply(b, None); + } + PassMode::Cast(_) => apply(&ArgAttributes::new(), None), + } + } + + let cconv = self.llvm_cconv(); + if cconv != llvm::CCallConv { + llvm::SetInstructionCallConv(callsite, cconv); + } + }*/ +} diff --git a/src/allocator.rs b/src/allocator.rs new file mode 100644 index 0000000000000..11b2fad3e7eee --- /dev/null +++ b/src/allocator.rs @@ -0,0 +1,115 @@ +//use crate::attributes; +use gccjit::{FunctionType, ToRValue}; +use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS}; +use rustc_middle::bug; +use rustc_middle::ty::TyCtxt; +use rustc_span::symbol::sym; + +use crate::GccContext; + +pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, kind: AllocatorKind, has_alloc_error_handler: bool) { + let context = &mods.context; + let usize = + match tcx.sess.target.pointer_width { + 16 => context.new_type::(), + 32 => context.new_type::(), + 64 => context.new_type::(), + tws => bug!("Unsupported target word size for int: {}", tws), + }; + let i8 = context.new_type::(); + let i8p = i8.make_pointer(); + let void = context.new_type::<()>(); + + for method in ALLOCATOR_METHODS { + let mut types = Vec::with_capacity(method.inputs.len()); + for ty in method.inputs.iter() { + match *ty { + AllocatorTy::Layout => { + types.push(usize); + types.push(usize); + } + AllocatorTy::Ptr => types.push(i8p), + AllocatorTy::Usize => types.push(usize), + + AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"), + } + } + let output = match method.output { + AllocatorTy::ResultPtr => Some(i8p), + AllocatorTy::Unit => None, + + AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { + panic!("invalid allocator output") + } + }; + let name = format!("__rust_{}", method.name); + + let args: Vec<_> = types.iter().enumerate() + .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index))) + .collect(); + let func = context.new_function(None, FunctionType::Exported, output.unwrap_or(void), &args, name, false); + + if tcx.sess.target.options.default_hidden_visibility { + //llvm::LLVMRustSetVisibility(func, llvm::Visibility::Hidden); + } + if tcx.sess.must_emit_unwind_tables() { + // TODO + //attributes::emit_uwtable(func, true); + } + + let callee = kind.fn_name(method.name); + let args: Vec<_> = types.iter().enumerate() + .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index))) + .collect(); + let callee = context.new_function(None, FunctionType::Extern, output.unwrap_or(void), &args, callee, false); + //llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden); + + let block = func.new_block("entry"); + + let args = args + .iter() + .enumerate() + .map(|(i, _)| func.get_param(i as i32).to_rvalue()) + .collect::>(); + let ret = context.new_call(None, callee, &args); + //llvm::LLVMSetTailCall(ret, True); + if output.is_some() { + block.end_with_return(None, ret); + } + else { + block.end_with_void_return(None); + } + } + + let types = [usize, usize]; + let name = "__rust_alloc_error_handler".to_string(); + let args: Vec<_> = types.iter().enumerate() + .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index))) + .collect(); + let func = context.new_function(None, FunctionType::Exported, void, &args, name, false); + + let kind = + if has_alloc_error_handler { + AllocatorKind::Global + } + else { + AllocatorKind::Default + }; + let callee = kind.fn_name(sym::oom); + let args: Vec<_> = types.iter().enumerate() + .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index))) + .collect(); + let callee = context.new_function(None, FunctionType::Extern, void, &args, callee, false); + //llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden); + + let block = func.new_block("entry"); + + let args = args + .iter() + .enumerate() + .map(|(i, _)| func.get_param(i as i32).to_rvalue()) + .collect::>(); + let _ret = context.new_call(None, callee, &args); + //llvm::LLVMSetTailCall(ret, True); + block.end_with_void_return(None); +} diff --git a/src/archive.rs b/src/archive.rs new file mode 100644 index 0000000000000..52acf4f2d26e7 --- /dev/null +++ b/src/archive.rs @@ -0,0 +1,270 @@ +use std::fs::File; +use std::path::{Path, PathBuf}; + +use rustc_session::Session; +use rustc_codegen_ssa::back::archive::{find_library, ArchiveBuilder}; +use rustc_codegen_ssa::METADATA_FILENAME; +use rustc_data_structures::temp_dir::MaybeTempDir; +use rustc_middle::middle::cstore::DllImport; +use rustc_span::symbol::Symbol; + +struct ArchiveConfig<'a> { + sess: &'a Session, + dst: PathBuf, + lib_search_paths: Vec, + use_native_ar: bool, + use_gnu_style_archive: bool, +} + +#[derive(Debug)] +enum ArchiveEntry { + FromArchive { + archive_index: usize, + entry_index: usize, + }, + File(PathBuf), +} + +pub struct ArArchiveBuilder<'a> { + config: ArchiveConfig<'a>, + src_archives: Vec<(PathBuf, ar::Archive)>, + // 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)>, +} + +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 config = ArchiveConfig { + sess, + dst: output.to_path_buf(), + lib_search_paths: archive_search_paths(sess), + use_native_ar: false, + // FIXME test for linux and System V derivatives instead + use_gnu_style_archive: sess.target.options.archive_format == "gnu", + }; + + 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 { + config, + src_archives, + entries, + } + } + + fn src_files(&mut self) -> Vec { + 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: Symbol, verbatim: bool) { + let location = find_library(name, verbatim, &self.config.lib_search_paths, self.config.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 + return false; + }) + } + + fn update_symbols(&mut self) { + } + + fn build(mut self) { + use std::process::Command; + + fn add_file_using_ar(archive: &Path, file: &Path) { + Command::new("ar") + .arg("r") // add or replace file + .arg("-c") // silence created file message + .arg(archive) + .arg(&file) + .status() + .unwrap(); + } + + enum BuilderKind<'a> { + Bsd(ar::Builder), + Gnu(ar::GnuBuilder), + NativeAr(&'a Path), + } + + let mut builder = if self.config.use_native_ar { + BuilderKind::NativeAr(&self.config.dst) + } else if self.config.use_gnu_style_archive { + BuilderKind::Gnu(ar::GnuBuilder::new( + File::create(&self.config.dst).unwrap(), + self.entries + .iter() + .map(|(name, _)| name.as_bytes().to_vec()) + .collect(), + )) + } else { + BuilderKind::Bsd(ar::Builder::new(File::create(&self.config.dst).unwrap())) + }; + + // Add all files + for (entry_name, entry) in self.entries.into_iter() { + match entry { + ArchiveEntry::FromArchive { + archive_index, + entry_index, + } => { + let (ref src_archive_path, ref mut src_archive) = + self.src_archives[archive_index]; + let entry = src_archive.jump_to_entry(entry_index).unwrap(); + let header = entry.header().clone(); + + match builder { + BuilderKind::Bsd(ref mut builder) => { + builder.append(&header, entry).unwrap() + } + BuilderKind::Gnu(ref mut builder) => { + builder.append(&header, entry).unwrap() + } + BuilderKind::NativeAr(archive_file) => { + Command::new("ar") + .arg("x") + .arg(src_archive_path) + .arg(&entry_name) + .status() + .unwrap(); + add_file_using_ar(archive_file, Path::new(&entry_name)); + std::fs::remove_file(entry_name).unwrap(); + } + } + } + ArchiveEntry::File(file) => + match builder { + BuilderKind::Bsd(ref mut builder) => { + builder + .append_file(entry_name.as_bytes(), &mut File::open(file).expect("file for bsd builder")) + .unwrap() + }, + BuilderKind::Gnu(ref mut builder) => { + builder + .append_file(entry_name.as_bytes(), &mut File::open(&file).expect(&format!("file {:?} for gnu builder", file))) + .unwrap() + }, + BuilderKind::NativeAr(archive_file) => add_file_using_ar(archive_file, &file), + }, + } + } + + // Finalize archive + std::mem::drop(builder); + + // Run ranlib to be able to link the archive + let status = std::process::Command::new("ranlib") + .arg(self.config.dst) + .status() + .expect("Couldn't run ranlib"); + + if !status.success() { + self.config.sess.fatal(&format!("Ranlib exited with code {:?}", status.code())); + } + } + + fn inject_dll_import_lib(&mut self, _lib_name: &str, _dll_imports: &[DllImport], _tmpdir: &MaybeTempDir) { + unimplemented!(); + } +} + +impl<'a> ArArchiveBuilder<'a> { + fn add_archive(&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.unwrap(); + let file_name = String::from_utf8(entry.header().identifier().to_vec()).unwrap(); + 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(()) + } +} diff --git a/src/asm.rs b/src/asm.rs new file mode 100644 index 0000000000000..6616366235fcd --- /dev/null +++ b/src/asm.rs @@ -0,0 +1,632 @@ +use gccjit::{RValue, ToRValue, Type}; +use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; +use rustc_codegen_ssa::mir::operand::OperandValue; +use rustc_codegen_ssa::mir::place::PlaceRef; +use rustc_codegen_ssa::traits::{AsmBuilderMethods, AsmMethods, BaseTypeMethods, BuilderMethods, GlobalAsmOperandRef, InlineAsmOperandRef}; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::LlvmInlineAsmInner; +use rustc_middle::bug; +use rustc_span::Span; +use rustc_target::asm::*; + +use crate::builder::Builder; +use crate::context::CodegenCx; +use crate::type_of::LayoutGccExt; + +impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { + fn codegen_llvm_inline_asm(&mut self, _ia: &LlvmInlineAsmInner, _outputs: Vec>>, mut _inputs: Vec>, _span: Span) -> bool { + // TODO + return true; + + /*let mut ext_constraints = vec![]; + let mut output_types = vec![]; + + // Prepare the output operands + let mut indirect_outputs = vec![]; + for (i, (out, &place)) in ia.outputs.iter().zip(&outputs).enumerate() { + if out.is_rw { + let operand = self.load_operand(place); + if let OperandValue::Immediate(_) = operand.val { + inputs.push(operand.immediate()); + } + ext_constraints.push(i.to_string()); + } + if out.is_indirect { + let operand = self.load_operand(place); + if let OperandValue::Immediate(_) = operand.val { + indirect_outputs.push(operand.immediate()); + } + } else { + output_types.push(place.layout.gcc_type(self.cx())); + } + } + if !indirect_outputs.is_empty() { + indirect_outputs.extend_from_slice(&inputs); + inputs = indirect_outputs; + } + + let clobbers = ia.clobbers.iter().map(|s| format!("~{{{}}}", &s)); + + // Default per-arch clobbers + // Basically what clang does + let arch_clobbers = match &self.sess().target.target.arch[..] { + "x86" | "x86_64" => vec!["~{dirflag}", "~{fpsr}", "~{flags}"], + "mips" | "mips64" => vec!["~{$1}"], + _ => Vec::new(), + }; + + let all_constraints = ia + .outputs + .iter() + .map(|out| out.constraint.to_string()) + .chain(ia.inputs.iter().map(|s| s.to_string())) + .chain(ext_constraints) + .chain(clobbers) + .chain(arch_clobbers.iter().map(|s| (*s).to_string())) + .collect::>() + .join(","); + + debug!("Asm Constraints: {}", &all_constraints); + + // Depending on how many outputs we have, the return type is different + let num_outputs = output_types.len(); + let output_type = match num_outputs { + 0 => self.type_void(), + 1 => output_types[0], + _ => self.type_struct(&output_types, false), + }; + + let asm = ia.asm.as_str(); + let r = inline_asm_call( + self, + &asm, + &all_constraints, + &inputs, + output_type, + ia.volatile, + ia.alignstack, + ia.dialect, + ); + if r.is_none() { + return false; + } + let r = r.unwrap(); + + // Again, based on how many outputs we have + let outputs = ia.outputs.iter().zip(&outputs).filter(|&(ref o, _)| !o.is_indirect); + for (i, (_, &place)) in outputs.enumerate() { + let v = if num_outputs == 1 { r } else { self.extract_value(r, i as u64) }; + OperandValue::Immediate(v).store(self, place); + } + + // Store mark in a metadata node so we can map LLVM errors + // back to source locations. See #17552. + unsafe { + let key = "srcloc"; + let kind = llvm::LLVMGetMDKindIDInContext( + self.llcx, + key.as_ptr() as *const c_char, + key.len() as c_uint, + ); + + let val: &'ll Value = self.const_i32(span.ctxt().outer_expn().as_u32() as i32); + + llvm::LLVMSetMetadata(r, kind, llvm::LLVMMDNodeInContext(self.llcx, &val, 1)); + } + + true*/ + } + + fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, _span: &[Span]) { + let asm_arch = self.tcx.sess.asm_arch.unwrap(); + + let intel_dialect = + match asm_arch { + InlineAsmArch::X86 | InlineAsmArch::X86_64 if !options.contains(InlineAsmOptions::ATT_SYNTAX) => true, + _ => false, + }; + + // Collect the types of output operands + // FIXME: we do this here instead of later because of a bug in libgccjit where creating the + // variable after the extended asm expression causes a segfault: + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100380 + let mut output_vars = FxHashMap::default(); + let mut operand_numbers = FxHashMap::default(); + let mut current_number = 0; + for (idx, op) in operands.iter().enumerate() { + match *op { + InlineAsmOperandRef::Out { place, .. } => { + let ty = + match place { + Some(place) => place.layout.gcc_type(self.cx, false), + None => { + // If the output is discarded, we don't really care what + // type is used. We're just using this to tell GCC to + // reserve the register. + //dummy_output_type(self.cx, reg.reg_class()) + + // NOTE: if no output value, we should not create one (it will be a + // clobber). + continue; + }, + }; + let var = self.current_func().new_local(None, ty, "output_register"); + operand_numbers.insert(idx, current_number); + current_number += 1; + output_vars.insert(idx, var); + } + InlineAsmOperandRef::InOut { out_place, .. } => { + let ty = + match out_place { + Some(place) => place.layout.gcc_type(self.cx, false), + None => { + // If the output is discarded, we don't really care what + // type is used. We're just using this to tell GCC to + // reserve the register. + //dummy_output_type(self.cx, reg.reg_class()) + + // NOTE: if no output value, we should not create one. + continue; + }, + }; + operand_numbers.insert(idx, current_number); + current_number += 1; + let var = self.current_func().new_local(None, ty, "output_register"); + output_vars.insert(idx, var); + } + _ => {} + } + } + + // All output operands must come before the input operands, hence the 2 loops. + for (idx, op) in operands.iter().enumerate() { + match *op { + InlineAsmOperandRef::In { .. } | InlineAsmOperandRef::InOut { .. } => { + operand_numbers.insert(idx, current_number); + current_number += 1; + }, + _ => (), + } + } + + // Build the template string + let mut template_str = String::new(); + for piece in template { + match *piece { + InlineAsmTemplatePiece::String(ref string) => { + if string.contains('%') { + for c in string.chars() { + if c == '%' { + template_str.push_str("%%"); + } + else { + template_str.push(c); + } + } + } + else { + template_str.push_str(string) + } + } + InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span: _ } => { + match operands[operand_idx] { + InlineAsmOperandRef::Out { reg, place: Some(_), .. } => { + let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier); + if let Some(modifier) = modifier { + template_str.push_str(&format!("%{}{}", modifier, operand_numbers[&operand_idx])); + } else { + template_str.push_str(&format!("%{}", operand_numbers[&operand_idx])); + } + }, + InlineAsmOperandRef::Out { place: None, .. } => { + unimplemented!("Out None"); + }, + InlineAsmOperandRef::In { reg, .. } + | InlineAsmOperandRef::InOut { reg, .. } => { + let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier); + if let Some(modifier) = modifier { + template_str.push_str(&format!("%{}{}", modifier, operand_numbers[&operand_idx])); + } else { + template_str.push_str(&format!("%{}", operand_numbers[&operand_idx])); + } + } + InlineAsmOperandRef::Const { ref string } => { + // Const operands get injected directly into the template + template_str.push_str(string); + } + InlineAsmOperandRef::SymFn { .. } + | InlineAsmOperandRef::SymStatic { .. } => { + unimplemented!(); + // Only emit the raw symbol name + //template_str.push_str(&format!("${{{}:c}}", op_idx[&operand_idx])); + } + } + } + } + } + + let block = self.llbb(); + let template_str = + if intel_dialect { + template_str + } + else { + // FIXME: this might break the "m" memory constraint: + // https://stackoverflow.com/a/9347957/389119 + // TODO: only set on x86 platforms. + format!(".att_syntax noprefix\n\t{}\n\t.intel_syntax noprefix", template_str) + }; + let extended_asm = block.add_extended_asm(None, &template_str); + + // Collect the types of output operands + let mut output_types = vec![]; + for (idx, op) in operands.iter().enumerate() { + match *op { + InlineAsmOperandRef::Out { reg, late, place } => { + let ty = + match place { + Some(place) => place.layout.gcc_type(self.cx, false), + None => { + // If the output is discarded, we don't really care what + // type is used. We're just using this to tell GCC to + // reserve the register. + dummy_output_type(self.cx, reg.reg_class()) + }, + }; + output_types.push(ty); + //op_idx.insert(idx, constraints.len()); + let prefix = if late { "=" } else { "=&" }; + let constraint = format!("{}{}", prefix, reg_to_gcc(reg)); + + if place.is_some() { + let var = output_vars[&idx]; + extended_asm.add_output_operand(None, &constraint, var); + } + else { + // NOTE: reg.to_string() returns the register name with quotes around it so + // remove them. + extended_asm.add_clobber(reg.to_string().trim_matches('"')); + } + } + InlineAsmOperandRef::InOut { reg, late, in_value, out_place } => { + let ty = + match out_place { + Some(out_place) => out_place.layout.gcc_type(self.cx, false), + None => dummy_output_type(self.cx, reg.reg_class()) + }; + output_types.push(ty); + //op_idx.insert(idx, constraints.len()); + // TODO: prefix of "+" for reading and writing? + let prefix = if late { "=" } else { "=&" }; + let constraint = format!("{}{}", prefix, reg_to_gcc(reg)); + + if out_place.is_some() { + let var = output_vars[&idx]; + // TODO: also specify an output operand when out_place is none: that would + // be the clobber but clobbers do not support general constraint like reg; + // they only support named registers. + // Not sure how we can do this. And the LLVM backend does not seem to add a + // clobber. + extended_asm.add_output_operand(None, &constraint, var); + } + + let constraint = reg_to_gcc(reg); + extended_asm.add_input_operand(None, &constraint, in_value.immediate()); + } + InlineAsmOperandRef::In { reg, value } => { + let constraint = reg_to_gcc(reg); + extended_asm.add_input_operand(None, &constraint, value.immediate()); + } + _ => {} + } + } + + /*if !options.contains(InlineAsmOptions::PRESERVES_FLAGS) { + match asm_arch { + InlineAsmArch::AArch64 | InlineAsmArch::Arm => { + constraints.push("~{cc}".to_string()); + } + InlineAsmArch::X86 | InlineAsmArch::X86_64 => { + constraints.extend_from_slice(&[ + "~{dirflag}".to_string(), + "~{fpsr}".to_string(), + "~{flags}".to_string(), + ]); + } + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {} + } + } + if !options.contains(InlineAsmOptions::NOMEM) { + // This is actually ignored by LLVM, but it's probably best to keep + // it just in case. LLVM instead uses the ReadOnly/ReadNone + // attributes on the call instruction to optimize. + constraints.push("~{memory}".to_string()); + } + let volatile = !options.contains(InlineAsmOptions::PURE); + let alignstack = !options.contains(InlineAsmOptions::NOSTACK); + let output_type = match &output_types[..] { + [] => self.type_void(), + [ty] => ty, + tys => self.type_struct(&tys, false), + };*/ + + /*let result = inline_asm_call( + self, + &template_str, + &constraints.join(","), + &inputs, + output_type, + volatile, + alignstack, + dialect, + span, + ) + .unwrap_or_else(|| span_bug!(span, "LLVM asm constraint validation failed")); + + if options.contains(InlineAsmOptions::PURE) { + if options.contains(InlineAsmOptions::NOMEM) { + llvm::Attribute::ReadNone.apply_callsite(llvm::AttributePlace::Function, result); + } else if options.contains(InlineAsmOptions::READONLY) { + llvm::Attribute::ReadOnly.apply_callsite(llvm::AttributePlace::Function, result); + } + } else { + if options.contains(InlineAsmOptions::NOMEM) { + llvm::Attribute::InaccessibleMemOnly + .apply_callsite(llvm::AttributePlace::Function, result); + } else { + // LLVM doesn't have an attribute to represent ReadOnly + SideEffect + } + }*/ + + // Write results to outputs + for (idx, op) in operands.iter().enumerate() { + if let InlineAsmOperandRef::Out { place: Some(place), .. } + | InlineAsmOperandRef::InOut { out_place: Some(place), .. } = *op + { + OperandValue::Immediate(output_vars[&idx].to_rvalue()).store(self, place); + } + } + } +} + +/// Converts a register class to a GCC constraint code. +// TODO: return &'static str instead? +fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> String { + match reg { + // For vector registers LLVM wants the register name to match the type size. + InlineAsmRegOrRegClass::Reg(reg) => { + // TODO: add support for vector register. + let constraint = + match reg.name() { + "ax" => "a", + "bx" => "b", + "cx" => "c", + "dx" => "d", + "si" => "S", + "di" => "D", + // TODO: for registers like r11, we have to create a register variable: https://stackoverflow.com/a/31774784/389119 + // TODO: in this case though, it's a clobber, so it should work as r11. + // Recent nightly supports clobber() syntax, so update to it. It does not seem + // like it's implemented yet. + name => name, // FIXME: probably wrong. + }; + constraint.to_string() + }, + InlineAsmRegOrRegClass::RegClass(reg) => match reg { + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => unimplemented!(), + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => unimplemented!(), + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) => unimplemented!(), + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => unimplemented!(), + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => unimplemented!(), + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => unimplemented!(), + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8) => unimplemented!(), + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => unimplemented!(), + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) => unimplemented!(), + InlineAsmRegClass::Bpf(_) => unimplemented!(), + InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => unimplemented!(), + InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => unimplemented!(), + InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => unimplemented!(), + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => unimplemented!(), + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => unimplemented!(), + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => unimplemented!(), + InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => unimplemented!(), + InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => unimplemented!(), + InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => unimplemented!(), + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => unimplemented!(), + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => unimplemented!(), + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => unimplemented!(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::mmx_reg) => unimplemented!(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r", + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => unimplemented!(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => unimplemented!(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg) + | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => unimplemented!(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg) => unimplemented!(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => unimplemented!(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => unimplemented!(), + InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(), + InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { + bug!("GCC backend does not support SPIR-V") + } + InlineAsmRegClass::Err => unreachable!(), + } + .to_string(), + } +} + +/// Type to use for outputs that are discarded. It doesn't really matter what +/// the type is, as long as it is valid for the constraint code. +fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegClass) -> Type<'gcc> { + match reg { + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => cx.type_i32(), + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => unimplemented!(), + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) + | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => { + unimplemented!() + } + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => cx.type_i32(), + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => cx.type_f32(), + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) => cx.type_f64(), + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => { + unimplemented!() + } + InlineAsmRegClass::Bpf(_) => unimplemented!(), + InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => cx.type_i32(), + InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(), + InlineAsmRegClass::Mips(MipsInlineAsmRegClass::freg) => cx.type_f32(), + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg16) => cx.type_i16(), + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg32) => cx.type_i32(), + InlineAsmRegClass::Nvptx(NvptxInlineAsmRegClass::reg64) => cx.type_i64(), + InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg) => cx.type_i32(), + InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::reg_nonzero) => cx.type_i32(), + InlineAsmRegClass::PowerPC(PowerPCInlineAsmRegClass::freg) => cx.type_f64(), + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => cx.type_i32(), + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => cx.type_f32(), + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => cx.type_f32(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) + | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => cx.type_i32(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => cx.type_i8(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::mmx_reg) => unimplemented!(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg) + | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) + | InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => cx.type_f32(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg) => unimplemented!(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => cx.type_i16(), + InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(), + InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { + bug!("LLVM backend does not support SPIR-V") + }, + InlineAsmRegClass::Err => unreachable!(), + } +} + +impl<'gcc, 'tcx> AsmMethods for CodegenCx<'gcc, 'tcx> { + fn codegen_global_asm(&self, template: &[InlineAsmTemplatePiece], operands: &[GlobalAsmOperandRef], options: InlineAsmOptions, _line_spans: &[Span]) { + let asm_arch = self.tcx.sess.asm_arch.unwrap(); + + // Default to Intel syntax on x86 + let intel_syntax = matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64) + && !options.contains(InlineAsmOptions::ATT_SYNTAX); + + // Build the template string + let mut template_str = String::new(); + for piece in template { + match *piece { + InlineAsmTemplatePiece::String(ref string) => { + for line in string.lines() { + // NOTE: gcc does not allow inline comment, so remove them. + let line = + if let Some(index) = line.rfind("//") { + &line[..index] + } + else { + line + }; + template_str.push_str(line); + template_str.push('\n'); + } + }, + InlineAsmTemplatePiece::Placeholder { operand_idx, modifier: _, span: _ } => { + match operands[operand_idx] { + GlobalAsmOperandRef::Const { ref string } => { + // Const operands get injected directly into the + // template. Note that we don't need to escape $ + // here unlike normal inline assembly. + template_str.push_str(string); + } + } + } + } + } + + let template_str = + if intel_syntax { + format!("{}\n\t.intel_syntax noprefix", template_str) + } + else { + format!(".att_syntax\n\t{}\n\t.intel_syntax noprefix", template_str) + }; + // NOTE: seems like gcc will put the asm in the wrong section, so set it to .text manually. + let template_str = format!(".pushsection .text\n{}\n.popsection", template_str); + self.context.add_top_level_asm(None, &template_str); + } +} + +fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option) -> Option { + match reg { + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::reg) => modifier, + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => modifier, + InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) + | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => { + unimplemented!() + //if modifier == Some('v') { None } else { modifier } + } + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => unimplemented!(), + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => unimplemented!(), + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low8) => unimplemented!(), + InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8) + | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => { + unimplemented!() + /*if modifier.is_none() { + Some('q') + } else { + modifier + }*/ + } + InlineAsmRegClass::Bpf(_) => unimplemented!(), + InlineAsmRegClass::Hexagon(_) => unimplemented!(), + InlineAsmRegClass::Mips(_) => unimplemented!(), + InlineAsmRegClass::Nvptx(_) => unimplemented!(), + InlineAsmRegClass::PowerPC(_) => unimplemented!(), + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) + | InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => unimplemented!(), + InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => unimplemented!(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) + | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => match modifier { + None if arch == InlineAsmArch::X86_64 => Some('q'), + None => Some('k'), + Some('l') => Some('b'), + Some('h') => Some('h'), + Some('x') => Some('w'), + Some('e') => Some('k'), + Some('r') => Some('q'), + _ => unreachable!(), + }, + InlineAsmRegClass::X86(X86InlineAsmRegClass::mmx_reg) => unimplemented!(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => unimplemented!(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg) + | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) + | InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => unimplemented!() /*match (reg, modifier) { + (X86InlineAsmRegClass::xmm_reg, None) => Some('x'), + (X86InlineAsmRegClass::ymm_reg, None) => Some('t'), + (X86InlineAsmRegClass::zmm_reg, None) => Some('g'), + (_, Some('x')) => Some('x'), + (_, Some('y')) => Some('t'), + (_, Some('z')) => Some('g'), + _ => unreachable!(), + }*/, + InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg) => unimplemented!(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => unimplemented!(), + InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(), + InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { + bug!("LLVM backend does not support SPIR-V") + }, + InlineAsmRegClass::Err => unreachable!(), + } +} diff --git a/src/back/mod.rs b/src/back/mod.rs new file mode 100644 index 0000000000000..d692799d7642f --- /dev/null +++ b/src/back/mod.rs @@ -0,0 +1 @@ +pub mod write; diff --git a/src/back/write.rs b/src/back/write.rs new file mode 100644 index 0000000000000..5201e49427799 --- /dev/null +++ b/src/back/write.rs @@ -0,0 +1,234 @@ +use std::fs; + +use gccjit::OutputKind; +use rustc_codegen_ssa::{CompiledModule, ModuleCodegen}; +use rustc_codegen_ssa::back::write::{CodegenContext, EmitObj, ModuleConfig}; +use rustc_errors::Handler; +use rustc_session::config::OutputType; +use rustc_span::fatal_error::FatalError; +use rustc_target::spec::SplitDebuginfo; + +use crate::{GccCodegenBackend, GccContext}; + +pub(crate) unsafe fn codegen(cgcx: &CodegenContext, _diag_handler: &Handler, module: ModuleCodegen, config: &ModuleConfig) -> Result { + let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_codegen", &module.name[..]); + { + let context = &module.module_llvm.context; + + //let llcx = &*module.module_llvm.llcx; + //let tm = &*module.module_llvm.tm; + let module_name = module.name.clone(); + let module_name = Some(&module_name[..]); + //let handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx); + + /*if cgcx.msvc_imps_needed { + create_msvc_imps(cgcx, llcx, llmod); + }*/ + + // A codegen-specific pass manager is used to generate object + // files for an GCC module. + // + // Apparently each of these pass managers is a one-shot kind of + // thing, so we create a new one for each type of output. The + // pass manager passed to the closure should be ensured to not + // escape the closure itself, and the manager should only be + // used once. + /*unsafe fn with_codegen<'ll, F, R>(tm: &'ll llvm::TargetMachine, llmod: &'ll llvm::Module, no_builtins: bool, f: F) -> R + where F: FnOnce(&'ll mut PassManager<'ll>) -> R, + { + let cpm = llvm::LLVMCreatePassManager(); + llvm::LLVMAddAnalysisPasses(tm, cpm); + llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins); + f(cpm) + }*/ + + // Two things to note: + // - If object files are just LLVM bitcode we write bitcode, copy it to + // the .o file, and delete the bitcode if it wasn't otherwise + // requested. + // - If we don't have the integrated assembler then we need to emit + // asm from LLVM and use `gcc` to create the object file. + + let _bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name); + let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name); + + if config.bitcode_needed() { + // TODO + /*let _timer = cgcx + .prof + .generic_activity_with_arg("LLVM_module_codegen_make_bitcode", &module.name[..]); + let thin = ThinBuffer::new(llmod); + let data = thin.data(); + + if config.emit_bc || config.emit_obj == EmitObj::Bitcode { + let _timer = cgcx.prof.generic_activity_with_arg( + "LLVM_module_codegen_emit_bitcode", + &module.name[..], + ); + if let Err(e) = fs::write(&bc_out, data) { + let msg = format!("failed to write bytecode to {}: {}", bc_out.display(), e); + diag_handler.err(&msg); + } + } + + if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) { + let _timer = cgcx.prof.generic_activity_with_arg( + "LLVM_module_codegen_embed_bitcode", + &module.name[..], + ); + embed_bitcode(cgcx, llcx, llmod, Some(data)); + } + + if config.emit_bc_compressed { + let _timer = cgcx.prof.generic_activity_with_arg( + "LLVM_module_codegen_emit_compressed_bitcode", + &module.name[..], + ); + let dst = bc_out.with_extension(RLIB_BYTECODE_EXTENSION); + let data = bytecode::encode(&module.name, data); + if let Err(e) = fs::write(&dst, data) { + let msg = format!("failed to write bytecode to {}: {}", dst.display(), e); + diag_handler.err(&msg); + } + }*/ + } /*else if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Marker) { + unimplemented!(); + //embed_bitcode(cgcx, llcx, llmod, None); + }*/ + + if config.emit_ir { + unimplemented!(); + /*let _timer = cgcx + .prof + .generic_activity_with_arg("LLVM_module_codegen_emit_ir", &module.name[..]); + let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name); + let out_c = path_to_c_string(&out); + + extern "C" fn demangle_callback( + input_ptr: *const c_char, + input_len: size_t, + output_ptr: *mut c_char, + output_len: size_t, + ) -> size_t { + let input = + unsafe { slice::from_raw_parts(input_ptr as *const u8, input_len as usize) }; + + let input = match str::from_utf8(input) { + Ok(s) => s, + Err(_) => return 0, + }; + + let output = unsafe { + slice::from_raw_parts_mut(output_ptr as *mut u8, output_len as usize) + }; + let mut cursor = io::Cursor::new(output); + + let demangled = match rustc_demangle::try_demangle(input) { + Ok(d) => d, + Err(_) => return 0, + }; + + if write!(cursor, "{:#}", demangled).is_err() { + // Possible only if provided buffer is not big enough + return 0; + } + + cursor.position() as size_t + } + + let result = llvm::LLVMRustPrintModule(llmod, out_c.as_ptr(), demangle_callback); + result.into_result().map_err(|()| { + let msg = format!("failed to write LLVM IR to {}", out.display()); + llvm_err(diag_handler, &msg) + })?;*/ + } + + if config.emit_asm { + let _timer = cgcx + .prof + .generic_activity_with_arg("LLVM_module_codegen_emit_asm", &module.name[..]); + let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); + context.compile_to_file(OutputKind::Assembler, path.to_str().expect("path to str")); + + /*with_codegen(tm, llmod, config.no_builtins, |cpm| { + write_output_file(diag_handler, tm, cpm, llmod, &path, llvm::FileType::AssemblyFile) + })?;*/ + } + + match config.emit_obj { + EmitObj::ObjectCode(_) => { + let _timer = cgcx + .prof + .generic_activity_with_arg("LLVM_module_codegen_emit_obj", &module.name[..]); + //with_codegen(tm, llmod, config.no_builtins, |cpm| { + //println!("1: {}", module.name); + match &*module.name { + "std_example.7rcbfp3g-cgu.15" => { + println!("Dumping reproducer {}", module.name); + let _ = fs::create_dir("/tmp/reproducers"); + // FIXME: segfault in dump_reproducer_to_file() might be caused by + // transmuting an rvalue to an lvalue. + // Segfault is actually in gcc::jit::reproducer::get_identifier_as_lvalue + context.dump_reproducer_to_file(&format!("/tmp/reproducers/{}.c", module.name)); + println!("Dumped reproducer {}", module.name); + }, + _ => (), + } + /*let _ = fs::create_dir("/tmp/dumps"); + context.dump_to_file(&format!("/tmp/dumps/{}.c", module.name), true); + println!("Dumped {}", module.name);*/ + //println!("Compile module {}", module.name); + context.compile_to_file(OutputKind::ObjectFile, obj_out.to_str().expect("path to str")); + //})?; + } + + EmitObj::Bitcode => { + //unimplemented!(); + /*debug!("copying bitcode {:?} to obj {:?}", bc_out, obj_out); + if let Err(e) = link_or_copy(&bc_out, &obj_out) { + diag_handler.err(&format!("failed to copy bitcode to object file: {}", e)); + } + + if !config.emit_bc { + debug!("removing_bitcode {:?}", bc_out); + if let Err(e) = fs::remove_file(&bc_out) { + diag_handler.err(&format!("failed to remove bitcode: {}", e)); + } + }*/ + } + + EmitObj::None => {} + } + + //drop(handlers); + } + + Ok(module.into_compiled_module( + config.emit_obj != EmitObj::None, + cgcx.target_can_use_split_dwarf && cgcx.split_debuginfo == SplitDebuginfo::Unpacked, + config.emit_bc, + &cgcx.output_filenames, + )) +} + +pub(crate) fn link(_cgcx: &CodegenContext, _diag_handler: &Handler, mut _modules: Vec>) -> Result, FatalError> { + unimplemented!(); + /*use super::lto::{Linker, ModuleBuffer}; + // Sort the modules by name to ensure to ensure deterministic behavior. + modules.sort_by(|a, b| a.name.cmp(&b.name)); + let (first, elements) = + modules.split_first().expect("Bug! modules must contain at least one module."); + + let mut linker = Linker::new(first.module_llvm.llmod()); + for module in elements { + let _timer = + cgcx.prof.generic_activity_with_arg("LLVM_link_module", format!("{:?}", module.name)); + let buffer = ModuleBuffer::new(module.module_llvm.llmod()); + linker.add(&buffer.data()).map_err(|()| { + let msg = format!("failed to serialize module {:?}", module.name); + llvm_err(&diag_handler, &msg) + })?; + } + drop(linker); + Ok(modules.remove(0))*/ +} diff --git a/src/base.rs b/src/base.rs new file mode 100644 index 0000000000000..7050f122a1765 --- /dev/null +++ b/src/base.rs @@ -0,0 +1,173 @@ +use std::env; +use std::sync::Once; +use std::time::Instant; + +use gccjit::{ + Context, + FunctionType, + GlobalKind, +}; +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_middle::dep_graph; +use rustc_middle::middle::cstore::EncodedMetadata; +use rustc_middle::middle::exported_symbols; +use rustc_middle::ty::TyCtxt; +use rustc_middle::mir::mono::Linkage; +use rustc_codegen_ssa::{ModuleCodegen, ModuleKind}; +use rustc_codegen_ssa::base::maybe_create_entry_wrapper; +use rustc_codegen_ssa::mono_item::MonoItemExt; +use rustc_codegen_ssa::traits::DebugInfoMethods; +use rustc_session::config::DebugInfo; +use rustc_span::Symbol; + +use crate::{GccContext, create_function_calling_initializers}; +use crate::builder::Builder; +use crate::context::CodegenCx; + +pub fn global_linkage_to_gcc(linkage: Linkage) -> GlobalKind { + match linkage { + Linkage::External => GlobalKind::Imported, + Linkage::AvailableExternally => GlobalKind::Imported, + Linkage::LinkOnceAny => unimplemented!(), + Linkage::LinkOnceODR => unimplemented!(), + Linkage::WeakAny => unimplemented!(), + Linkage::WeakODR => unimplemented!(), + Linkage::Appending => unimplemented!(), + Linkage::Internal => GlobalKind::Internal, + Linkage::Private => GlobalKind::Internal, + Linkage::ExternalWeak => GlobalKind::Imported, // TODO: should be weak linkage. + Linkage::Common => unimplemented!(), + } +} + +pub fn linkage_to_gcc(linkage: Linkage) -> FunctionType { + match linkage { + Linkage::External => FunctionType::Exported, + Linkage::AvailableExternally => FunctionType::Extern, + Linkage::LinkOnceAny => unimplemented!(), + Linkage::LinkOnceODR => unimplemented!(), + Linkage::WeakAny => FunctionType::Exported, // FIXME: should be similar to linkonce. + Linkage::WeakODR => unimplemented!(), + Linkage::Appending => unimplemented!(), + Linkage::Internal => FunctionType::Internal, + Linkage::Private => FunctionType::Internal, + Linkage::ExternalWeak => unimplemented!(), + Linkage::Common => unimplemented!(), + } +} + +pub fn compile_codegen_unit<'tcx>(tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (ModuleCodegen, u64) { + let prof_timer = tcx.prof.generic_activity("codegen_module"); + let start_time = Instant::now(); + + let dep_node = tcx.codegen_unit(cgu_name).codegen_dep_node(tcx); + let (module, _) = tcx.dep_graph.with_task(dep_node, tcx, cgu_name, module_codegen, dep_graph::hash_result); + let time_to_codegen = start_time.elapsed(); + drop(prof_timer); + + // We assume that the cost to run GCC on a CGU is proportional to + // the time we needed for codegenning it. + let cost = time_to_codegen.as_secs() * 1_000_000_000 + time_to_codegen.subsec_nanos() as u64; + + fn module_codegen(tcx: TyCtxt<'_>, cgu_name: Symbol) -> ModuleCodegen { + let cgu = tcx.codegen_unit(cgu_name); + // Instantiate monomorphizations without filling out definitions yet... + //let llvm_module = ModuleLlvm::new(tcx, &cgu_name.as_str()); + let context = Context::default(); + // TODO: only set on x86 platforms. + context.add_command_line_option("-masm=intel"); + for arg in &tcx.sess.opts.cg.llvm_args { + context.add_command_line_option(arg); + } + context.add_command_line_option("-fno-semantic-interposition"); + //context.set_dump_code_on_compile(true); + if env::var("CG_GCCJIT_DUMP_GIMPLE").as_deref() == Ok("1") { + context.set_dump_initial_gimple(true); + } + context.set_debug_info(true); + //context.set_dump_everything(true); + //context.set_keep_intermediates(true); + + { + let cx = CodegenCx::new(&context, cgu, tcx); + + static START: Once = Once::new(); + START.call_once(|| { + let initializer_name = format!("__gccGlobalCrateInit{}", tcx.crate_name(LOCAL_CRATE)); + let func = context.new_function(None, FunctionType::Exported, context.new_type::<()>(), &[], initializer_name, false); + let block = func.new_block("initial"); + create_function_calling_initializers(tcx, &context, block); + block.end_with_void_return(None); + }); + + //println!("module_codegen: {:?} {:?}", cgu_name, &cx.context as *const _); + let mono_items = cgu.items_in_deterministic_order(tcx); + for &(mono_item, (linkage, visibility)) in &mono_items { + mono_item.predefine::>(&cx, linkage, visibility); + } + + // ... and now that we have everything pre-defined, fill out those definitions. + for &(mono_item, _) in &mono_items { + mono_item.define::>(&cx); + } + + // If this codegen unit contains the main function, also create the + // wrapper here + maybe_create_entry_wrapper::>(&cx); + + // Finalize debuginfo + if cx.sess().opts.debuginfo != DebugInfo::None { + cx.debuginfo_finalize(); + } + + cx.global_init_block.end_with_void_return(None); + } + + ModuleCodegen { + name: cgu_name.to_string(), + module_llvm: GccContext { + context + }, + kind: ModuleKind::Regular, + } + } + + (module, cost) +} + +pub fn write_compressed_metadata<'tcx>(tcx: TyCtxt<'tcx>, metadata: &EncodedMetadata, gcc_module: &mut GccContext) { + use snap::write::FrameEncoder; + use std::io::Write; + + // Historical note: + // + // When using link.exe it was seen that the section name `.note.rustc` + // was getting shortened to `.note.ru`, and according to the PE and COFF + // specification: + // + // > Executable images do not use a string table and do not support + // > section names longer than 8 characters + // + // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format + // + // As a result, we choose a slightly shorter name! As to why + // `.note.rustc` works on MinGW, see + // https://github.com/llvm/llvm-project/blob/llvmorg-12.0.0/lld/COFF/Writer.cpp#L1190-L1197 + let section_name = if tcx.sess.target.is_like_osx { "__DATA,.rustc" } else { ".rustc" }; + + let context = &gcc_module.context; + let mut compressed = rustc_metadata::METADATA_HEADER.to_vec(); + FrameEncoder::new(&mut compressed).write_all(&metadata.raw_data).unwrap(); + + let name = exported_symbols::metadata_symbol_name(tcx); + let typ = context.new_array_type(None, context.new_type::(), compressed.len() as i32); + let global = context.new_global(None, GlobalKind::Exported, typ, name); + global.global_set_initializer(&compressed); + global.set_link_section(section_name); + + // Also generate a .section directive to force no + // flags, at least for ELF outputs, so that the + // metadata doesn't get loaded into memory. + let directive = format!(".section {}", section_name); + context.add_top_level_asm(None, &directive); +} diff --git a/src/builder.rs b/src/builder.rs new file mode 100644 index 0000000000000..8bdcb08bd3d6b --- /dev/null +++ b/src/builder.rs @@ -0,0 +1,1812 @@ +use std::borrow::Cow; +use std::cell::Cell; +use std::convert::TryFrom; +use std::ops::{Deref, Range}; + +use gccjit::FunctionType; +use gccjit::{ + BinaryOp, + Block, + ComparisonOp, + Function, + LValue, + RValue, + ToRValue, + Type, + UnaryOp, +}; +use rustc_codegen_ssa::MemFlags; +use rustc_codegen_ssa::common::{AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope}; +use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; +use rustc_codegen_ssa::mir::place::PlaceRef; +use rustc_codegen_ssa::traits::{ + BackendTypes, + BaseTypeMethods, + BuilderMethods, + ConstMethods, + DerivedTypeMethods, + HasCodegen, + OverflowOp, + StaticBuilderMethods, +}; +use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; +use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, TyAndLayout}; +use rustc_span::Span; +use rustc_span::def_id::DefId; +use rustc_target::abi::{ + self, + Align, + HasDataLayout, + LayoutOf, + Size, + TargetDataLayout, +}; +use rustc_target::spec::{HasTargetSpec, Target}; + +use crate::common::{SignType, TypeReflection, type_is_pointer}; +use crate::context::CodegenCx; +use crate::type_of::LayoutGccExt; + +// TODO +type Funclet = (); + +// TODO: remove this variable. +static mut RETURN_VALUE_COUNT: usize = 0; + +enum ExtremumOperation { + Max, + Min, +} + +trait EnumClone { + fn clone(&self) -> Self; +} + +impl EnumClone for AtomicOrdering { + fn clone(&self) -> Self { + match *self { + AtomicOrdering::NotAtomic => AtomicOrdering::NotAtomic, + AtomicOrdering::Unordered => AtomicOrdering::Unordered, + AtomicOrdering::Monotonic => AtomicOrdering::Monotonic, + AtomicOrdering::Acquire => AtomicOrdering::Acquire, + AtomicOrdering::Release => AtomicOrdering::Release, + AtomicOrdering::AcquireRelease => AtomicOrdering::AcquireRelease, + AtomicOrdering::SequentiallyConsistent => AtomicOrdering::SequentiallyConsistent, + } + } +} + +pub struct Builder<'a: 'gcc, 'gcc, 'tcx> { + pub cx: &'a CodegenCx<'gcc, 'tcx>, + pub block: Option>, + stack_var_count: Cell, +} + +impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { + fn with_cx(cx: &'a CodegenCx<'gcc, 'tcx>) -> Self { + Builder { + cx, + block: None, + stack_var_count: Cell::new(0), + } + } + + fn atomic_extremum(&mut self, operation: ExtremumOperation, dst: RValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering) -> RValue<'gcc> { + let size = self.cx.int_width(src.get_type()) / 8; + + let func = self.current_func(); + + let load_ordering = + match order { + // TODO: does this make sense? + AtomicOrdering::AcquireRelease | AtomicOrdering::Release => AtomicOrdering::Acquire, + _ => order.clone(), + }; + let previous_value = self.atomic_load(dst.get_type(), dst, load_ordering.clone(), Size::from_bytes(size)); + let previous_var = func.new_local(None, previous_value.get_type(), "previous_value"); + let return_value = func.new_local(None, previous_value.get_type(), "return_value"); + self.llbb().add_assignment(None, previous_var, previous_value); + self.llbb().add_assignment(None, return_value, previous_var.to_rvalue()); + + let while_block = func.new_block("while"); + let after_block = func.new_block("after_while"); + self.llbb().end_with_jump(None, while_block); + + // NOTE: since jumps were added and compare_exchange doesn't expect this, the current blocks in the + // state need to be updated. + self.block = Some(while_block); + *self.cx.current_block.borrow_mut() = Some(while_block); + + let comparison_operator = + match operation { + ExtremumOperation::Max => ComparisonOp::LessThan, + ExtremumOperation::Min => ComparisonOp::GreaterThan, + }; + + let cond1 = self.context.new_comparison(None, comparison_operator, previous_var.to_rvalue(), self.context.new_cast(None, src, previous_value.get_type())); + let compare_exchange = self.compare_exchange(dst, previous_var, src, order, load_ordering, false); + let cond2 = self.cx.context.new_unary_op(None, UnaryOp::LogicalNegate, compare_exchange.get_type(), compare_exchange); + let cond = self.cx.context.new_binary_op(None, BinaryOp::LogicalAnd, self.cx.bool_type, cond1, cond2); + + while_block.end_with_conditional(None, cond, while_block, after_block); + + // NOTE: since jumps were added in a place rustc does not expect, the current blocks in the + // state need to be updated. + self.block = Some(after_block); + *self.cx.current_block.borrow_mut() = Some(after_block); + + return_value.to_rvalue() + } + + fn compare_exchange(&self, dst: RValue<'gcc>, cmp: LValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering, failure_order: AtomicOrdering, weak: bool) -> RValue<'gcc> { + let size = self.cx.int_width(src.get_type()); + let compare_exchange = self.context.get_builtin_function(&format!("__atomic_compare_exchange_{}", size / 8)); + let order = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc()); + let failure_order = self.context.new_rvalue_from_int(self.i32_type, failure_order.to_gcc()); + let weak = self.context.new_rvalue_from_int(self.bool_type, weak as i32); + + let void_ptr_type = self.context.new_type::<*mut ()>(); + let volatile_void_ptr_type = void_ptr_type.make_volatile(); + let dst = self.context.new_cast(None, dst, volatile_void_ptr_type); + let expected = self.context.new_cast(None, cmp.get_address(None), void_ptr_type); + + // NOTE: not sure why, but we have the wrong type here. + let int_type = compare_exchange.get_param(2).to_rvalue().get_type(); + let src = self.context.new_cast(None, src, int_type); + self.context.new_call(None, compare_exchange, &[dst, expected, src, weak, order, failure_order]) + } + + pub fn assign(&self, lvalue: LValue<'gcc>, value: RValue<'gcc>) { + self.llbb().add_assignment(None, lvalue, value); + } + + fn check_call<'b>(&mut self, _typ: &str, func: Function<'gcc>, args: &'b [RValue<'gcc>]) -> Cow<'b, [RValue<'gcc>]> { + //let mut fn_ty = self.cx.val_ty(func); + // Strip off pointers + /*while self.cx.type_kind(fn_ty) == TypeKind::Pointer { + fn_ty = self.cx.element_type(fn_ty); + }*/ + + /*assert!( + self.cx.type_kind(fn_ty) == TypeKind::Function, + "builder::{} not passed a function, but {:?}", + typ, + fn_ty + ); + + let param_tys = self.cx.func_params_types(fn_ty); + + let all_args_match = param_tys + .iter() + .zip(args.iter().map(|&v| self.val_ty(v))) + .all(|(expected_ty, actual_ty)| *expected_ty == actual_ty);*/ + + let mut all_args_match = true; + let mut param_types = vec![]; + let param_count = func.get_param_count(); + for (index, arg) in args.iter().enumerate().take(param_count) { + let param = func.get_param(index as i32); + let param = param.to_rvalue().get_type(); + if param != arg.get_type() { + all_args_match = false; + } + param_types.push(param); + } + + if all_args_match { + return Cow::Borrowed(args); + } + + let casted_args: Vec<_> = param_types + .into_iter() + .zip(args.iter()) + .enumerate() + .map(|(_i, (expected_ty, &actual_val))| { + let actual_ty = actual_val.get_type(); + if expected_ty != actual_ty { + /*debug!( + "type mismatch in function call of {:?}. \ + Expected {:?} for param {}, got {:?}; injecting bitcast", + func, expected_ty, i, actual_ty + );*/ + /*println!( + "type mismatch in function call of {:?}. \ + Expected {:?} for param {}, got {:?}; injecting bitcast", + func, expected_ty, i, actual_ty + );*/ + self.bitcast(actual_val, expected_ty) + } + else { + actual_val + } + }) + .collect(); + + Cow::Owned(casted_args) + } + + fn check_ptr_call<'b>(&mut self, _typ: &str, func_ptr: RValue<'gcc>, args: &'b [RValue<'gcc>]) -> Cow<'b, [RValue<'gcc>]> { + //let mut fn_ty = self.cx.val_ty(func); + // Strip off pointers + /*while self.cx.type_kind(fn_ty) == TypeKind::Pointer { + fn_ty = self.cx.element_type(fn_ty); + }*/ + + /*assert!( + self.cx.type_kind(fn_ty) == TypeKind::Function, + "builder::{} not passed a function, but {:?}", + typ, + fn_ty + ); + + let param_tys = self.cx.func_params_types(fn_ty); + + let all_args_match = param_tys + .iter() + .zip(args.iter().map(|&v| self.val_ty(v))) + .all(|(expected_ty, actual_ty)| *expected_ty == actual_ty);*/ + + let mut all_args_match = true; + let mut param_types = vec![]; + let gcc_func = func_ptr.get_type().is_function_ptr_type().expect("function ptr"); + for (index, arg) in args.iter().enumerate().take(gcc_func.get_param_count()) { + let param = gcc_func.get_param_type(index); + if param != arg.get_type() { + all_args_match = false; + } + param_types.push(param); + } + + if all_args_match { + return Cow::Borrowed(args); + } + + let casted_args: Vec<_> = param_types + .into_iter() + .zip(args.iter()) + .enumerate() + .map(|(_i, (expected_ty, &actual_val))| { + let actual_ty = actual_val.get_type(); + if expected_ty != actual_ty { + /*debug!( + "type mismatch in function call of {:?}. \ + Expected {:?} for param {}, got {:?}; injecting bitcast", + func, expected_ty, i, actual_ty + );*/ + /*println!( + "type mismatch in function call of {:?}. \ + Expected {:?} for param {}, got {:?}; injecting bitcast", + func, expected_ty, i, actual_ty + );*/ + self.bitcast(actual_val, expected_ty) + } + else { + actual_val + } + }) + .collect(); + + Cow::Owned(casted_args) + } + + fn check_store(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>) -> RValue<'gcc> { + let dest_ptr_ty = self.cx.val_ty(ptr).make_pointer(); // TODO: make sure make_pointer() is okay here. + let stored_ty = self.cx.val_ty(val); + let stored_ptr_ty = self.cx.type_ptr_to(stored_ty); + + //assert_eq!(self.cx.type_kind(dest_ptr_ty), TypeKind::Pointer); + + if dest_ptr_ty == stored_ptr_ty { + ptr + } + else { + /*debug!( + "type mismatch in store. \ + Expected {:?}, got {:?}; inserting bitcast", + dest_ptr_ty, stored_ptr_ty + );*/ + /*println!( + "type mismatch in store. \ + Expected {:?}, got {:?}; inserting bitcast", + dest_ptr_ty, stored_ptr_ty + );*/ + //ptr + self.bitcast(ptr, stored_ptr_ty) + } + } + + pub fn current_func(&self) -> Function<'gcc> { + self.block.expect("block").get_function() + } + + fn function_call(&mut self, func: RValue<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>) -> RValue<'gcc> { + //debug!("call {:?} with args ({:?})", func, args); + + // TODO: remove when the API supports a different type for functions. + let func: Function<'gcc> = self.cx.rvalue_as_function(func); + let args = self.check_call("call", func, args); + //let bundle = funclet.map(|funclet| funclet.bundle()); + //let bundle = bundle.as_ref().map(|b| &*b.raw); + + // gccjit requires to use the result of functions, even when it's not used. + // That's why we assign the result to a local or call add_eval(). + let return_type = func.get_return_type(); + let current_block = self.current_block.borrow().expect("block"); + let void_type = self.context.new_type::<()>(); + let current_func = current_block.get_function(); + if return_type != void_type { + unsafe { RETURN_VALUE_COUNT += 1 }; + let result = current_func.new_local(None, return_type, &format!("returnValue{}", unsafe { RETURN_VALUE_COUNT })); + current_block.add_assignment(None, result, self.cx.context.new_call(None, func, &args)); + result.to_rvalue() + } + else { + current_block.add_eval(None, self.cx.context.new_call(None, func, &args)); + // Return dummy value when not having return value. + self.context.new_rvalue_from_long(self.isize_type, 0) + } + } + + fn function_ptr_call(&mut self, func_ptr: RValue<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>) -> RValue<'gcc> { + //debug!("func ptr call {:?} with args ({:?})", func, args); + + let args = self.check_ptr_call("call", func_ptr, args); + //let bundle = funclet.map(|funclet| funclet.bundle()); + //let bundle = bundle.as_ref().map(|b| &*b.raw); + + // gccjit requires to use the result of functions, even when it's not used. + // That's why we assign the result to a local or call add_eval(). + let gcc_func = func_ptr.get_type().is_function_ptr_type().expect("function ptr"); + let mut return_type = gcc_func.get_return_type(); + let current_block = self.current_block.borrow().expect("block"); + let void_type = self.context.new_type::<()>(); + let current_func = current_block.get_function(); + + // FIXME: As a temporary workaround for unsupported LLVM intrinsics. + if gcc_func.get_param_count() == 0 && format!("{:?}", func_ptr) == "__builtin_ia32_pmovmskb128" { + return_type = self.int_type; + } + + if return_type != void_type { + unsafe { RETURN_VALUE_COUNT += 1 }; + let result = current_func.new_local(None, return_type, &format!("returnValue{}", unsafe { RETURN_VALUE_COUNT })); + current_block.add_assignment(None, result, self.cx.context.new_call_through_ptr(None, func_ptr, &args)); + result.to_rvalue() + } + else { + if gcc_func.get_param_count() == 0 { + // FIXME: As a temporary workaround for unsupported LLVM intrinsics. + current_block.add_eval(None, self.cx.context.new_call_through_ptr(None, func_ptr, &[])); + } + else { + current_block.add_eval(None, self.cx.context.new_call_through_ptr(None, func_ptr, &args)); + } + // Return dummy value when not having return value. + let result = current_func.new_local(None, self.isize_type, "dummyValueThatShouldNeverBeUsed"); + current_block.add_assignment(None, result, self.context.new_rvalue_from_long(self.isize_type, 0)); + result.to_rvalue() + } + } + + pub fn overflow_call(&mut self, func: Function<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>) -> RValue<'gcc> { + //debug!("overflow_call {:?} with args ({:?})", func, args); + + //let bundle = funclet.map(|funclet| funclet.bundle()); + //let bundle = bundle.as_ref().map(|b| &*b.raw); + + // gccjit requires to use the result of functions, even when it's not used. + // That's why we assign the result to a local. + let return_type = self.context.new_type::(); + let current_block = self.current_block.borrow().expect("block"); + let current_func = current_block.get_function(); + // TODO: return the new_call() directly? Since the overflow function has no side-effects. + unsafe { RETURN_VALUE_COUNT += 1 }; + let result = current_func.new_local(None, return_type, &format!("returnValue{}", unsafe { RETURN_VALUE_COUNT })); + current_block.add_assignment(None, result, self.cx.context.new_call(None, func, &args)); + result.to_rvalue() + } +} + +impl<'gcc, 'tcx> HasCodegen<'tcx> for Builder<'_, 'gcc, 'tcx> { + type CodegenCx = CodegenCx<'gcc, 'tcx>; +} + +impl<'tcx> HasTyCtxt<'tcx> for Builder<'_, '_, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.cx.tcx() + } +} + +impl HasDataLayout for Builder<'_, '_, '_> { + fn data_layout(&self) -> &TargetDataLayout { + self.cx.data_layout() + } +} + +impl<'tcx> LayoutOf for Builder<'_, '_, 'tcx> { + type Ty = Ty<'tcx>; + type TyAndLayout = TyAndLayout<'tcx>; + + fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { + self.cx.layout_of(ty) + } +} + +impl<'gcc, 'tcx> Deref for Builder<'_, 'gcc, 'tcx> { + type Target = CodegenCx<'gcc, 'tcx>; + + fn deref(&self) -> &Self::Target { + self.cx + } +} + +impl<'gcc, 'tcx> BackendTypes for Builder<'_, 'gcc, 'tcx> { + type Value = as BackendTypes>::Value; + type Function = as BackendTypes>::Function; + type BasicBlock = as BackendTypes>::BasicBlock; + type Type = as BackendTypes>::Type; + type Funclet = as BackendTypes>::Funclet; + + type DIScope = as BackendTypes>::DIScope; + type DILocation = as BackendTypes>::DILocation; + type DIVariable = as BackendTypes>::DIVariable; +} + +impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { + fn build(cx: &'a CodegenCx<'gcc, 'tcx>, block: Block<'gcc>) -> Self { + let mut bx = Builder::with_cx(cx); + *cx.current_block.borrow_mut() = Some(block); + bx.block = Some(block); + bx + } + + fn build_sibling_block(&mut self, name: &str) -> Self { + let block = self.append_sibling_block(name); + Self::build(self.cx, block) + } + + fn llbb(&self) -> Block<'gcc> { + self.block.expect("block") + } + + fn append_block(cx: &'a CodegenCx<'gcc, 'tcx>, func: RValue<'gcc>, name: &str) -> Block<'gcc> { + let func = cx.rvalue_as_function(func); + func.new_block(name) + } + + fn append_sibling_block(&mut self, name: &str) -> Block<'gcc> { + let func = self.current_func(); + func.new_block(name) + } + + fn ret_void(&mut self) { + self.llbb().end_with_void_return(None) + } + + fn ret(&mut self, value: RValue<'gcc>) { + let value = + if self.structs_as_pointer.borrow().contains(&value) { + // NOTE: hack to workaround a limitation of the rustc API: see comment on + // CodegenCx.structs_as_pointer + value.dereference(None).to_rvalue() + } + else { + value + }; + self.llbb().end_with_return(None, value); + } + + fn br(&mut self, dest: Block<'gcc>) { + self.llbb().end_with_jump(None, dest) + } + + fn cond_br(&mut self, cond: RValue<'gcc>, then_block: Block<'gcc>, else_block: Block<'gcc>) { + self.llbb().end_with_conditional(None, cond, then_block, else_block) + } + + fn switch(&mut self, value: RValue<'gcc>, default_block: Block<'gcc>, cases: impl ExactSizeIterator)>) { + let mut gcc_cases = vec![]; + let typ = self.val_ty(value); + for (on_val, dest) in cases { + let on_val = self.const_uint_big(typ, on_val); + gcc_cases.push(self.context.new_case(on_val, on_val, dest)); + } + self.block.expect("block").end_with_switch(None, value, default_block, &gcc_cases); + } + + fn invoke(&mut self, _func: RValue<'gcc>, _args: &[RValue<'gcc>], _then: Block<'gcc>, _catch: Block<'gcc>, _funclet: Option<&Funclet>) -> RValue<'gcc> { + unimplemented!(); + /*debug!("invoke {:?} with args ({:?})", func, args); + + let args = self.check_call("invoke", func, args); + let bundle = funclet.map(|funclet| funclet.bundle()); + let bundle = bundle.as_ref().map(|b| &*b.raw); + + unsafe { + llvm::LLVMRustBuildInvoke( + self.llbuilder, + func, + args.as_ptr(), + args.len() as c_uint, + then, + catch, + bundle, + UNNAMED, + ) + }*/ + } + + fn unreachable(&mut self) { + let func = self.context.get_builtin_function("__builtin_unreachable"); + let block = self.block.expect("block"); + block.add_eval(None, self.context.new_call(None, func, &[])); + let return_type = block.get_function().get_return_type(); + let void_type = self.context.new_type::<()>(); + if return_type == void_type { + block.end_with_void_return(None) + } + else { + let return_value = self.current_func() + .new_local(None, return_type, "unreachableReturn"); + block.end_with_return(None, return_value) + } + } + + fn add(&mut self, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> { + // FIXME: this should not be required. + if format!("{:?}", a.get_type()) != format!("{:?}", b.get_type()) { + b = self.context.new_cast(None, b, a.get_type()); + } + a + b + } + + fn fadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + a + b + } + + fn sub(&mut self, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> { + if a.get_type() != b.get_type() { + b = self.context.new_cast(None, b, a.get_type()); + } + a - b + } + + fn fsub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + a - b + } + + fn mul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + a * b + } + + fn fmul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + a * b + } + + fn udiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + // TODO: convert the arguments to unsigned? + a / b + } + + fn exactudiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + // TODO: convert the arguments to unsigned? + // TODO: poison if not exact. + a / b + } + + fn sdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + // TODO: convert the arguments to signed? + a / b + } + + fn exactsdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + // TODO: posion if not exact. + // FIXME: rustc_codegen_ssa::mir::intrinsic uses different types for a and b but they + // should be the same. + let typ = a.get_type().to_signed(self); + let a = self.context.new_cast(None, a, typ); + let b = self.context.new_cast(None, b, typ); + a / b + } + + fn fdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + a / b + } + + fn urem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + a % b + } + + fn srem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + a % b + } + + fn frem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + if a.get_type() == self.cx.float_type { + let fmodf = self.context.get_builtin_function("fmodf"); + // FIXME: this seems to produce the wrong result. + return self.context.new_call(None, fmodf, &[a, b]); + } + assert_eq!(a.get_type(), self.cx.double_type); + + let fmod = self.context.get_builtin_function("fmod"); + return self.context.new_call(None, fmod, &[a, b]); + } + + fn shl(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + // FIXME: remove the casts when libgccjit can shift an unsigned number by an unsigned number. + let a_type = a.get_type(); + let b_type = b.get_type(); + if a_type.is_unsigned(self) && b_type.is_signed(self) { + //println!("shl: {:?} -> {:?}", a, b_type); + let a = self.context.new_cast(None, a, b_type); + let result = a << b; + //println!("shl: {:?} -> {:?}", result, a_type); + self.context.new_cast(None, result, a_type) + } + else if a_type.is_signed(self) && b_type.is_unsigned(self) { + //println!("shl: {:?} -> {:?}", b, a_type); + let b = self.context.new_cast(None, b, a_type); + a << b + } + else { + a << b + } + } + + fn lshr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + // FIXME: remove the casts when libgccjit can shift an unsigned number by an unsigned number. + // TODO: cast to unsigned to do a logical shift if that does not work. + let a_type = a.get_type(); + let b_type = b.get_type(); + if a_type.is_unsigned(self) && b_type.is_signed(self) { + //println!("lshl: {:?} -> {:?}", a, b_type); + let a = self.context.new_cast(None, a, b_type); + let result = a >> b; + //println!("lshl: {:?} -> {:?}", result, a_type); + self.context.new_cast(None, result, a_type) + } + else if a_type.is_signed(self) && b_type.is_unsigned(self) { + //println!("lshl: {:?} -> {:?}", b, a_type); + let b = self.context.new_cast(None, b, a_type); + a >> b + } + else { + a >> b + } + } + + fn ashr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + // TODO: check whether behavior is an arithmetic shift for >> . + // FIXME: remove the casts when libgccjit can shift an unsigned number by an unsigned number. + let a_type = a.get_type(); + let b_type = b.get_type(); + if a_type.is_unsigned(self) && b_type.is_signed(self) { + //println!("ashl: {:?} -> {:?}", a, b_type); + let a = self.context.new_cast(None, a, b_type); + let result = a >> b; + //println!("ashl: {:?} -> {:?}", result, a_type); + self.context.new_cast(None, result, a_type) + } + else if a_type.is_signed(self) && b_type.is_unsigned(self) { + //println!("ashl: {:?} -> {:?}", b, a_type); + let b = self.context.new_cast(None, b, a_type); + a >> b + } + else { + a >> b + } + } + + fn and(&mut self, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> { + // FIXME: hack by putting the result in a variable to workaround this bug: + // https://gcc.gnu.org/bugzilla//show_bug.cgi?id=95498 + if a.get_type() != b.get_type() { + b = self.context.new_cast(None, b, a.get_type()); + } + let res = self.current_func().new_local(None, b.get_type(), "andResult"); + self.llbb().add_assignment(None, res, a & b); + res.to_rvalue() + } + + fn or(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + // FIXME: hack by putting the result in a variable to workaround this bug: + // https://gcc.gnu.org/bugzilla//show_bug.cgi?id=95498 + let res = self.current_func().new_local(None, b.get_type(), "orResult"); + self.llbb().add_assignment(None, res, a | b); + res.to_rvalue() + } + + fn xor(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + a ^ b + } + + fn neg(&mut self, a: RValue<'gcc>) -> RValue<'gcc> { + // TODO: use new_unary_op()? + self.cx.context.new_rvalue_from_long(a.get_type(), 0) - a + } + + fn fneg(&mut self, a: RValue<'gcc>) -> RValue<'gcc> { + self.cx.context.new_unary_op(None, UnaryOp::Minus, a.get_type(), a) + } + + fn not(&mut self, a: RValue<'gcc>) -> RValue<'gcc> { + let operation = + if a.get_type().is_bool() { + UnaryOp::LogicalNegate + } + else { + UnaryOp::BitwiseNegate + }; + self.cx.context.new_unary_op(None, operation, a.get_type(), a) + } + + fn unchecked_sadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + a + b + } + + fn unchecked_uadd(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + a + b + } + + fn unchecked_ssub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + a - b + } + + fn unchecked_usub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + // TODO: should generate poison value? + a - b + } + + fn unchecked_smul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + a * b + } + + fn unchecked_umul(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { + a * b + } + + fn fadd_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> { + unimplemented!(); + /*unsafe { + let instr = llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, UNNAMED); + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + }*/ + } + + fn fsub_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> { + unimplemented!(); + /*unsafe { + let instr = llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, UNNAMED); + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + }*/ + } + + fn fmul_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> { + unimplemented!(); + /*unsafe { + let instr = llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, UNNAMED); + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + }*/ + } + + fn fdiv_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> { + unimplemented!(); + /*unsafe { + let instr = llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, UNNAMED); + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + }*/ + } + + fn frem_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> { + unimplemented!(); + /*unsafe { + let instr = llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, UNNAMED); + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + }*/ + } + + fn checked_binop(&mut self, oop: OverflowOp, typ: Ty<'_>, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value) { + use rustc_middle::ty::{Int, IntTy::*, Uint, UintTy::*}; + + let new_kind = + match typ.kind() { + Int(t @ Isize) => Int(t.normalize(self.tcx.sess.target.pointer_width)), + Uint(t @ Usize) => Uint(t.normalize(self.tcx.sess.target.pointer_width)), + t @ (Uint(_) | Int(_)) => t.clone(), + _ => panic!("tried to get overflow intrinsic for op applied to non-int type"), + }; + + // TODO: remove duplication with intrinsic? + let name = + match oop { + OverflowOp::Add => + match new_kind { + Int(I8) => "__builtin_add_overflow", + Int(I16) => "__builtin_add_overflow", + Int(I32) => "__builtin_sadd_overflow", + Int(I64) => "__builtin_saddll_overflow", + Int(I128) => "__builtin_add_overflow", + + Uint(U8) => "__builtin_add_overflow", + Uint(U16) => "__builtin_add_overflow", + Uint(U32) => "__builtin_uadd_overflow", + Uint(U64) => "__builtin_uaddll_overflow", + Uint(U128) => "__builtin_add_overflow", + + _ => unreachable!(), + }, + OverflowOp::Sub => + match new_kind { + Int(I8) => "__builtin_sub_overflow", + Int(I16) => "__builtin_sub_overflow", + Int(I32) => "__builtin_ssub_overflow", + Int(I64) => "__builtin_ssubll_overflow", + Int(I128) => "__builtin_sub_overflow", + + Uint(U8) => "__builtin_sub_overflow", + Uint(U16) => "__builtin_sub_overflow", + Uint(U32) => "__builtin_usub_overflow", + Uint(U64) => "__builtin_usubll_overflow", + Uint(U128) => "__builtin_sub_overflow", + + _ => unreachable!(), + }, + OverflowOp::Mul => + match new_kind { + Int(I8) => "__builtin_mul_overflow", + Int(I16) => "__builtin_mul_overflow", + Int(I32) => "__builtin_smul_overflow", + Int(I64) => "__builtin_smulll_overflow", + Int(I128) => "__builtin_mul_overflow", + + Uint(U8) => "__builtin_mul_overflow", + Uint(U16) => "__builtin_mul_overflow", + Uint(U32) => "__builtin_umul_overflow", + Uint(U64) => "__builtin_umulll_overflow", + Uint(U128) => "__builtin_mul_overflow", + + _ => unreachable!(), + }, + }; + + let intrinsic = self.context.get_builtin_function(&name); + let res = self.current_func() + // TODO: is it correct to use rhs type instead of the parameter typ? + .new_local(None, rhs.get_type(), "binopResult") + .get_address(None); + let overflow = self.overflow_call(intrinsic, &[lhs, rhs, res], None); + (res.dereference(None).to_rvalue(), overflow) + } + + fn alloca(&mut self, ty: Type<'gcc>, align: Align) -> RValue<'gcc> { + // FIXME: this check that we don't call get_aligned() a second time on a time. + // Ideally, we shouldn't need to do this check. + let aligned_type = + if ty == self.cx.u128_type || ty == self.cx.i128_type { + ty + } + else { + ty.get_aligned(align.bytes()) + }; + // TODO: It might be better to return a LValue, but fixing the rustc API is non-trivial. + self.stack_var_count.set(self.stack_var_count.get() + 1); + self.current_func().new_local(None, aligned_type, &format!("stack_var_{}", self.stack_var_count.get())).get_address(None) + } + + fn dynamic_alloca(&mut self, _ty: Type<'gcc>, _align: Align) -> RValue<'gcc> { + unimplemented!(); + /*unsafe { + let alloca = llvm::LLVMBuildAlloca(self.llbuilder, ty, UNNAMED); + llvm::LLVMSetAlignment(alloca, align.bytes() as c_uint); + alloca + }*/ + } + + fn array_alloca(&mut self, _ty: Type<'gcc>, _len: RValue<'gcc>, _align: Align) -> RValue<'gcc> { + unimplemented!(); + /*unsafe { + let alloca = llvm::LLVMBuildArrayAlloca(self.llbuilder, ty, len, UNNAMED); + llvm::LLVMSetAlignment(alloca, align.bytes() as c_uint); + alloca + }*/ + } + + fn load(&mut self, _ty: Type<'gcc>, ptr: RValue<'gcc>, _align: Align) -> RValue<'gcc> { + // TODO: use ty. + let block = self.llbb(); + let function = block.get_function(); + // NOTE: instead of returning the dereference here, we have to assign it to a variable in + // the current basic block. Otherwise, it could be used in another basic block, causing a + // dereference after a drop, for instance. + // TODO: handle align. + let deref = ptr.dereference(None).to_rvalue(); + let value_type = deref.get_type(); + unsafe { RETURN_VALUE_COUNT += 1 }; + let loaded_value = function.new_local(None, value_type, &format!("loadedValue{}", unsafe { RETURN_VALUE_COUNT })); + block.add_assignment(None, loaded_value, deref); + loaded_value.to_rvalue() + } + + fn volatile_load(&mut self, _ty: Type<'gcc>, ptr: RValue<'gcc>) -> RValue<'gcc> { + // TODO: use ty. + //println!("5: volatile load: {:?} to {:?}", ptr, ptr.get_type().make_volatile()); + let ptr = self.context.new_cast(None, ptr, ptr.get_type().make_volatile()); + //println!("6"); + ptr.dereference(None).to_rvalue() + } + + fn atomic_load(&mut self, _ty: Type<'gcc>, ptr: RValue<'gcc>, order: AtomicOrdering, size: Size) -> RValue<'gcc> { + // TODO: use ty. + // TODO: handle alignment. + let atomic_load = self.context.get_builtin_function(&format!("__atomic_load_{}", size.bytes())); + let ordering = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc()); + + let volatile_const_void_ptr_type = self.context.new_type::<*mut ()>().make_const().make_volatile(); + let ptr = self.context.new_cast(None, ptr, volatile_const_void_ptr_type); + self.context.new_call(None, atomic_load, &[ptr, ordering]) + } + + fn load_operand(&mut self, place: PlaceRef<'tcx, RValue<'gcc>>) -> OperandRef<'tcx, RValue<'gcc>> { + //debug!("PlaceRef::load: {:?}", place); + + assert_eq!(place.llextra.is_some(), place.layout.is_unsized()); + + if place.layout.is_zst() { + return OperandRef::new_zst(self, place.layout); + } + + fn scalar_load_metadata<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, load: RValue<'gcc>, scalar: &abi::Scalar) { + let vr = scalar.valid_range.clone(); + match scalar.value { + abi::Int(..) => { + let range = scalar.valid_range_exclusive(bx); + if range.start != range.end { + bx.range_metadata(load, range); + } + } + abi::Pointer if vr.start() < vr.end() && !vr.contains(&0) => { + bx.nonnull_metadata(load); + } + _ => {} + } + } + + let val = + if let Some(llextra) = place.llextra { + OperandValue::Ref(place.llval, Some(llextra), place.align) + } + else if place.layout.is_gcc_immediate() { + let const_llval = None; + /*unsafe { + if let Some(global) = llvm::LLVMIsAGlobalVariable(place.llval) { + if llvm::LLVMIsGlobalConstant(global) == llvm::True { + const_llval = llvm::LLVMGetInitializer(global); + } + } + }*/ + let llval = const_llval.unwrap_or_else(|| { + let load = self.load(place.llval.get_type(), place.llval, place.align); + if let abi::Abi::Scalar(ref scalar) = place.layout.abi { + scalar_load_metadata(self, load, scalar); + } + load + }); + OperandValue::Immediate(self.to_immediate(llval, place.layout)) + } + else if let abi::Abi::ScalarPair(ref a, ref b) = place.layout.abi { + let b_offset = a.value.size(self).align_to(b.value.align(self).abi); + + let mut load = |i, scalar: &abi::Scalar, align| { + let llptr = self.struct_gep(place.llval, i as u64); + let load = self.load(llptr.get_type(), llptr, align); + scalar_load_metadata(self, load, scalar); + if scalar.is_bool() { self.trunc(load, self.type_i1()) } else { load } + }; + + OperandValue::Pair( + load(0, a, place.align), + load(1, b, place.align.restrict_for_offset(b_offset)), + ) + } + else { + OperandValue::Ref(place.llval, None, place.align) + }; + + OperandRef { val, layout: place.layout } + } + + fn write_operand_repeatedly(mut self, cg_elem: OperandRef<'tcx, RValue<'gcc>>, count: u64, dest: PlaceRef<'tcx, RValue<'gcc>>) -> Self { + let zero = self.const_usize(0); + let count = self.const_usize(count); + let start = dest.project_index(&mut self, zero).llval; + let end = dest.project_index(&mut self, count).llval; + + let mut header_bx = self.build_sibling_block("repeat_loop_header"); + let mut body_bx = self.build_sibling_block("repeat_loop_body"); + let next_bx = self.build_sibling_block("repeat_loop_next"); + + let ptr_type = start.get_type(); + let current = self.llbb().get_function().new_local(None, ptr_type, "loop_var"); + let current_val = current.to_rvalue(); + self.assign(current, start); + + self.br(header_bx.llbb()); + + let keep_going = header_bx.icmp(IntPredicate::IntNE, current_val, end); + header_bx.cond_br(keep_going, body_bx.llbb(), next_bx.llbb()); + + let align = dest.align.restrict_for_offset(dest.layout.field(self.cx(), 0).size); + cg_elem.val.store(&mut body_bx, PlaceRef::new_sized_aligned(current_val, cg_elem.layout, align)); + + let next = body_bx.inbounds_gep(current.to_rvalue(), &[self.const_usize(1)]); + body_bx.llbb().add_assignment(None, current, next); + body_bx.br(header_bx.llbb()); + + next_bx + } + + fn range_metadata(&mut self, _load: RValue<'gcc>, _range: Range) { + // TODO + /*if self.sess().target.target.arch == "amdgpu" { + // amdgpu/LLVM does something weird and thinks a i64 value is + // split into a v2i32, halving the bitwidth LLVM expects, + // tripping an assertion. So, for now, just disable this + // optimization. + return; + } + + unsafe { + let llty = self.cx.val_ty(load); + let v = [ + self.cx.const_uint_big(llty, range.start), + self.cx.const_uint_big(llty, range.end), + ]; + + llvm::LLVMSetMetadata( + load, + llvm::MD_range as c_uint, + llvm::LLVMMDNodeInContext(self.cx.llcx, v.as_ptr(), v.len() as c_uint), + ); + }*/ + } + + fn nonnull_metadata(&mut self, _load: RValue<'gcc>) { + // TODO + /*unsafe { + llvm::LLVMSetMetadata( + load, + llvm::MD_nonnull as c_uint, + llvm::LLVMMDNodeInContext(self.cx.llcx, ptr::null(), 0), + ); + }*/ + } + + fn store(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>, align: Align) -> RValue<'gcc> { + self.store_with_flags(val, ptr, align, MemFlags::empty()) + } + + fn store_with_flags(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>, _align: Align, _flags: MemFlags) -> RValue<'gcc> { + //debug!("Store {:?} -> {:?} ({:?})", val, ptr, flags); + let ptr = self.check_store(val, ptr); + self.llbb().add_assignment(None, ptr.dereference(None), val); + /*let align = + if flags.contains(MemFlags::UNALIGNED) { 1 } else { align.bytes() as c_uint }; + llvm::LLVMSetAlignment(store, align); + if flags.contains(MemFlags::VOLATILE) { + llvm::LLVMSetVolatile(store, llvm::True); + } + if flags.contains(MemFlags::NONTEMPORAL) { + // According to LLVM [1] building a nontemporal store must + // *always* point to a metadata value of the integer 1. + // + // [1]: http://llvm.org/docs/LangRef.html#store-instruction + let one = self.cx.const_i32(1); + let node = llvm::LLVMMDNodeInContext(self.cx.llcx, &one, 1); + llvm::LLVMSetMetadata(store, llvm::MD_nontemporal as c_uint, node); + }*/ + // NOTE: dummy value here since it's never used. FIXME: API should not return a value here? + self.cx.context.new_rvalue_zero(self.type_i32()) + } + + fn atomic_store(&mut self, value: RValue<'gcc>, ptr: RValue<'gcc>, order: AtomicOrdering, size: Size) { + // TODO: handle alignment. + let atomic_store = self.context.get_builtin_function(&format!("__atomic_store_{}", size.bytes())); + let ordering = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc()); + let volatile_const_void_ptr_type = self.context.new_type::<*mut ()>().make_const().make_volatile(); + let ptr = self.context.new_cast(None, ptr, volatile_const_void_ptr_type); + + // FIXME: fix libgccjit to allow comparing an integer type with an aligned integer type because + // the following cast is required to avoid this error: + // gcc_jit_context_new_call: mismatching types for argument 2 of function "__atomic_store_4": assignment to param arg1 (type: int) from loadedValue3577 (type: unsigned int __attribute__((aligned(4)))) + let int_type = atomic_store.get_param(1).to_rvalue().get_type(); + let value = self.context.new_cast(None, value, int_type); + self.llbb() + .add_eval(None, self.context.new_call(None, atomic_store, &[ptr, value, ordering])); + } + + fn gep(&mut self, ptr: RValue<'gcc>, indices: &[RValue<'gcc>]) -> RValue<'gcc> { + let mut result = ptr; + for index in indices { + result = self.context.new_array_access(None, result, *index).get_address(None).to_rvalue(); + } + result + } + + fn inbounds_gep(&mut self, ptr: RValue<'gcc>, indices: &[RValue<'gcc>]) -> RValue<'gcc> { + // FIXME: would be safer if doing the same thing (loop) as gep. + // TODO: specify inbounds somehow. + match indices.len() { + 1 => { + self.context.new_array_access(None, ptr, indices[0]).get_address(None) + }, + 2 => { + let array = ptr.dereference(None); // TODO: assert that first index is 0? + self.context.new_array_access(None, array, indices[1]).get_address(None) + }, + _ => unimplemented!(), + } + } + + fn struct_gep(&mut self, ptr: RValue<'gcc>, idx: u64) -> RValue<'gcc> { + // FIXME: it would be better if the API only called this on struct, not on arrays. + assert_eq!(idx as usize as u64, idx); + let value = ptr.dereference(None).to_rvalue(); + let value_type = value.get_type(); + + if value_type.is_array().is_some() { + let index = self.context.new_rvalue_from_long(self.u64_type, i64::try_from(idx).expect("i64::try_from")); + let element = self.context.new_array_access(None, value, index); + element.get_address(None) + } + else if let Some(vector_type) = value_type.is_vector() { + let array_type = vector_type.get_element_type().make_pointer(); + let array = self.bitcast(ptr, array_type); + let index = self.context.new_rvalue_from_long(self.u64_type, i64::try_from(idx).expect("i64::try_from")); + let element = self.context.new_array_access(None, array, index); + element.get_address(None) + } + else if let Some(struct_type) = value_type.is_struct() { + ptr.dereference_field(None, struct_type.get_field(idx as i32)).get_address(None) + } + else { + panic!("Unexpected type {:?}", value_type); + } + } + + /* Casts */ + fn trunc(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { + // TODO: check that it indeed truncate the value. + //println!("trunc: {:?} -> {:?}", value, dest_ty); + self.context.new_cast(None, value, dest_ty) + } + + fn sext(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { + // TODO: check that it indeed sign extend the value. + //println!("Sext {:?} to {:?}", value, dest_ty); + //if let Some(vector_type) = value.get_type().is_vector() { + if dest_ty.is_vector().is_some() { + // TODO: nothing to do as it is only for LLVM? + return value; + /*let dest_type = self.context.new_vector_type(dest_ty, vector_type.get_num_units() as u64); + println!("Casting {:?} to {:?}", value, dest_type); + return self.context.new_cast(None, value, dest_type);*/ + } + self.context.new_cast(None, value, dest_ty) + } + + fn fptoui(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { + //println!("7: fptoui: {:?} to {:?}", value, dest_ty); + let ret = self.context.new_cast(None, value, dest_ty); + //println!("8"); + ret + //unsafe { llvm::LLVMBuildFPToUI(self.llbuilder, val, dest_ty, UNNAMED) } + } + + fn fptosi(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { + self.context.new_cast(None, value, dest_ty) + } + + fn uitofp(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { + //println!("1: uitofp: {:?} -> {:?}", value, dest_ty); + let ret = self.context.new_cast(None, value, dest_ty); + //println!("2"); + ret + } + + fn sitofp(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { + //println!("3: sitofp: {:?} -> {:?}", value, dest_ty); + let ret = self.context.new_cast(None, value, dest_ty); + //println!("4"); + ret + } + + fn fptrunc(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { + // TODO: make sure it trancates. + self.context.new_cast(None, value, dest_ty) + } + + fn fpext(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { + self.context.new_cast(None, value, dest_ty) + } + + fn ptrtoint(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { + self.cx.ptrtoint(self.block.expect("block"), value, dest_ty) + } + + fn inttoptr(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { + self.cx.inttoptr(self.block.expect("block"), value, dest_ty) + } + + fn bitcast(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { + self.cx.const_bitcast(value, dest_ty) + } + + fn intcast(&mut self, value: RValue<'gcc>, dest_typ: Type<'gcc>, _is_signed: bool) -> RValue<'gcc> { + // NOTE: is_signed is for value, not dest_typ. + //println!("intcast: {:?} ({:?}) -> {:?}", value, value.get_type(), dest_typ); + self.cx.context.new_cast(None, value, dest_typ) + } + + fn pointercast(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { + //println!("pointercast: {:?} ({:?}) -> {:?}", value, value.get_type(), dest_ty); + let val_type = value.get_type(); + match (type_is_pointer(val_type), type_is_pointer(dest_ty)) { + (false, true) => { + // NOTE: Projecting a field of a pointer type will attemp a cast from a signed char to + // a pointer, which is not supported by gccjit. + return self.cx.context.new_cast(None, self.inttoptr(value, val_type.make_pointer()), dest_ty); + }, + (false, false) => { + // When they are not pointers, we want a transmute (or reinterpret_cast). + //self.cx.context.new_cast(None, value, dest_ty) + self.bitcast(value, dest_ty) + }, + (true, true) => self.cx.context.new_cast(None, value, dest_ty), + (true, false) => unimplemented!(), + } + } + + /* Comparisons */ + fn icmp(&mut self, op: IntPredicate, lhs: RValue<'gcc>, mut rhs: RValue<'gcc>) -> RValue<'gcc> { + if lhs.get_type() != rhs.get_type() { + // NOTE: hack because we try to cast a vector type to the same vector type. + if format!("{:?}", lhs.get_type()) != format!("{:?}", rhs.get_type()) { + rhs = self.context.new_cast(None, rhs, lhs.get_type()); + } + } + self.context.new_comparison(None, op.to_gcc_comparison(), lhs, rhs) + } + + fn fcmp(&mut self, op: RealPredicate, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> { + self.context.new_comparison(None, op.to_gcc_comparison(), lhs, rhs) + } + + /* Miscellaneous instructions */ + fn memcpy(&mut self, dst: RValue<'gcc>, dst_align: Align, src: RValue<'gcc>, src_align: Align, size: RValue<'gcc>, flags: MemFlags) { + if flags.contains(MemFlags::NONTEMPORAL) { + // HACK(nox): This is inefficient but there is no nontemporal memcpy. + let val = self.load(src.get_type(), src, src_align); + let ptr = self.pointercast(dst, self.type_ptr_to(self.val_ty(val))); + self.store_with_flags(val, ptr, dst_align, flags); + return; + } + let size = self.intcast(size, self.type_size_t(), false); + let _is_volatile = flags.contains(MemFlags::VOLATILE); + let dst = self.pointercast(dst, self.type_i8p()); + let src = self.pointercast(src, self.type_ptr_to(self.type_void())); + let memcpy = self.context.get_builtin_function("memcpy"); + let block = self.block.expect("block"); + // TODO: handle aligns and is_volatile. + block.add_eval(None, self.context.new_call(None, memcpy, &[dst, src, size])); + } + + fn memmove(&mut self, dst: RValue<'gcc>, dst_align: Align, src: RValue<'gcc>, src_align: Align, size: RValue<'gcc>, flags: MemFlags) { + if flags.contains(MemFlags::NONTEMPORAL) { + // HACK(nox): This is inefficient but there is no nontemporal memmove. + let val = self.load(src.get_type(), src, src_align); + let ptr = self.pointercast(dst, self.type_ptr_to(self.val_ty(val))); + self.store_with_flags(val, ptr, dst_align, flags); + return; + } + let size = self.intcast(size, self.type_size_t(), false); + let _is_volatile = flags.contains(MemFlags::VOLATILE); + let dst = self.pointercast(dst, self.type_i8p()); + let src = self.pointercast(src, self.type_ptr_to(self.type_void())); + + let memmove = self.context.get_builtin_function("memmove"); + let block = self.block.expect("block"); + // TODO: handle is_volatile. + block.add_eval(None, self.context.new_call(None, memmove, &[dst, src, size])); + } + + fn memset(&mut self, ptr: RValue<'gcc>, fill_byte: RValue<'gcc>, size: RValue<'gcc>, _align: Align, flags: MemFlags) { + let _is_volatile = flags.contains(MemFlags::VOLATILE); + let ptr = self.pointercast(ptr, self.type_i8p()); + let memset = self.context.get_builtin_function("memset"); + let block = self.block.expect("block"); + // TODO: handle aligns and is_volatile. + //println!("memset: {:?} -> {:?}", fill_byte, self.i32_type); + let fill_byte = self.context.new_cast(None, fill_byte, self.i32_type); + let size = self.intcast(size, self.type_size_t(), false); + block.add_eval(None, self.context.new_call(None, memset, &[ptr, fill_byte, size])); + } + + fn select(&mut self, cond: RValue<'gcc>, then_val: RValue<'gcc>, mut else_val: RValue<'gcc>) -> RValue<'gcc> { + let func = self.current_func(); + let variable = func.new_local(None, then_val.get_type(), "selectVar"); + let then_block = func.new_block("then"); + let else_block = func.new_block("else"); + let after_block = func.new_block("after"); + self.llbb().end_with_conditional(None, cond, then_block, else_block); + + then_block.add_assignment(None, variable, then_val); + then_block.end_with_jump(None, after_block); + + if then_val.get_type() != else_val.get_type() { + else_val = self.context.new_cast(None, else_val, then_val.get_type()); + } + else_block.add_assignment(None, variable, else_val); + else_block.end_with_jump(None, after_block); + + // NOTE: since jumps were added in a place rustc does not expect, the current blocks in the + // state need to be updated. + self.block = Some(after_block); + *self.cx.current_block.borrow_mut() = Some(after_block); + + variable.to_rvalue() + } + + #[allow(dead_code)] + fn va_arg(&mut self, _list: RValue<'gcc>, _ty: Type<'gcc>) -> RValue<'gcc> { + unimplemented!(); + //unsafe { llvm::LLVMBuildVAArg(self.llbuilder, list, ty, UNNAMED) } + } + + fn extract_element(&mut self, _vec: RValue<'gcc>, _idx: RValue<'gcc>) -> RValue<'gcc> { + unimplemented!(); + //unsafe { llvm::LLVMBuildExtractElement(self.llbuilder, vec, idx, UNNAMED) } + } + + fn vector_splat(&mut self, _num_elts: usize, _elt: RValue<'gcc>) -> RValue<'gcc> { + unimplemented!(); + /*unsafe { + let elt_ty = self.cx.val_ty(elt); + let undef = llvm::LLVMGetUndef(self.type_vector(elt_ty, num_elts as u64)); + let vec = self.insert_element(undef, elt, self.cx.const_i32(0)); + let vec_i32_ty = self.type_vector(self.type_i32(), num_elts as u64); + self.shuffle_vector(vec, undef, self.const_null(vec_i32_ty)) + }*/ + } + + fn extract_value(&mut self, aggregate_value: RValue<'gcc>, idx: u64) -> RValue<'gcc> { + // FIXME: it would be better if the API only called this on struct, not on arrays. + assert_eq!(idx as usize as u64, idx); + let value_type = aggregate_value.get_type(); + + if value_type.is_array().is_some() { + let index = self.context.new_rvalue_from_long(self.u64_type, i64::try_from(idx).expect("i64::try_from")); + let element = self.context.new_array_access(None, aggregate_value, index); + element.get_address(None) + } + else if value_type.is_vector().is_some() { + panic!(); + } + else if let Some(pointer_type) = value_type.get_pointee() { + if let Some(struct_type) = pointer_type.is_struct() { + // NOTE: hack to workaround a limitation of the rustc API: see comment on + // CodegenCx.structs_as_pointer + aggregate_value.dereference_field(None, struct_type.get_field(idx as i32)).to_rvalue() + } + else { + panic!("Unexpected type {:?}", value_type); + } + } + else if let Some(struct_type) = value_type.is_struct() { + aggregate_value.access_field(None, struct_type.get_field(idx as i32)).to_rvalue() + } + else { + panic!("Unexpected type {:?}", value_type); + } + /*assert_eq!(idx as c_uint as u64, idx); + unsafe { llvm::LLVMBuildExtractValue(self.llbuilder, agg_val, idx as c_uint, UNNAMED) }*/ + } + + fn insert_value(&mut self, aggregate_value: RValue<'gcc>, value: RValue<'gcc>, idx: u64) -> RValue<'gcc> { + // FIXME: it would be better if the API only called this on struct, not on arrays. + assert_eq!(idx as usize as u64, idx); + let value_type = aggregate_value.get_type(); + + let lvalue = + if value_type.is_array().is_some() { + let index = self.context.new_rvalue_from_long(self.u64_type, i64::try_from(idx).expect("i64::try_from")); + self.context.new_array_access(None, aggregate_value, index) + } + else if value_type.is_vector().is_some() { + panic!(); + } + else if let Some(pointer_type) = value_type.get_pointee() { + if let Some(struct_type) = pointer_type.is_struct() { + // NOTE: hack to workaround a limitation of the rustc API: see comment on + // CodegenCx.structs_as_pointer + aggregate_value.dereference_field(None, struct_type.get_field(idx as i32)) + } + else { + panic!("Unexpected type {:?}", value_type); + } + } + else { + panic!("Unexpected type {:?}", value_type); + }; + self.llbb().add_assignment(None, lvalue, value); + + aggregate_value + } + + fn landing_pad(&mut self, _ty: Type<'gcc>, _pers_fn: RValue<'gcc>, _num_clauses: usize) -> RValue<'gcc> { + unimplemented!(); + /*unsafe { + llvm::LLVMBuildLandingPad(self.llbuilder, ty, pers_fn, num_clauses as c_uint, UNNAMED) + }*/ + } + + fn set_cleanup(&mut self, _landing_pad: RValue<'gcc>) { + unimplemented!(); + /*unsafe { + llvm::LLVMSetCleanup(landing_pad, llvm::True); + }*/ + } + + fn resume(&mut self, _exn: RValue<'gcc>) -> RValue<'gcc> { + unimplemented!(); + //unsafe { llvm::LLVMBuildResume(self.llbuilder, exn) } + } + + fn cleanup_pad(&mut self, _parent: Option>, _args: &[RValue<'gcc>]) -> Funclet { + unimplemented!(); + /*let name = const_cstr!("cleanuppad"); + let ret = unsafe { + llvm::LLVMRustBuildCleanupPad( + self.llbuilder, + parent, + args.len() as c_uint, + args.as_ptr(), + name.as_ptr(), + ) + }; + Funclet::new(ret.expect("LLVM does not have support for cleanuppad"))*/ + } + + fn cleanup_ret(&mut self, _funclet: &Funclet, _unwind: Option>) -> RValue<'gcc> { + unimplemented!(); + /*let ret = + unsafe { llvm::LLVMRustBuildCleanupRet(self.llbuilder, funclet.cleanuppad(), unwind) }; + ret.expect("LLVM does not have support for cleanupret")*/ + } + + fn catch_pad(&mut self, _parent: RValue<'gcc>, _args: &[RValue<'gcc>]) -> Funclet { + unimplemented!(); + /*let name = const_cstr!("catchpad"); + let ret = unsafe { + llvm::LLVMRustBuildCatchPad( + self.llbuilder, + parent, + args.len() as c_uint, + args.as_ptr(), + name.as_ptr(), + ) + }; + Funclet::new(ret.expect("LLVM does not have support for catchpad"))*/ + } + + fn catch_switch(&mut self, _parent: Option>, _unwind: Option>, _num_handlers: usize) -> RValue<'gcc> { + unimplemented!(); + /*let name = const_cstr!("catchswitch"); + let ret = unsafe { + llvm::LLVMRustBuildCatchSwitch( + self.llbuilder, + parent, + unwind, + num_handlers as c_uint, + name.as_ptr(), + ) + }; + ret.expect("LLVM does not have support for catchswitch")*/ + } + + fn add_handler(&mut self, _catch_switch: RValue<'gcc>, _handler: Block<'gcc>) { + unimplemented!(); + /*unsafe { + llvm::LLVMRustAddHandler(catch_switch, handler); + }*/ + } + + fn set_personality_fn(&mut self, _personality: RValue<'gcc>) { + unimplemented!(); + /*unsafe { + llvm::LLVMSetPersonalityFn(self.llfn(), personality); + }*/ + } + + // Atomic Operations + fn atomic_cmpxchg(&mut self, dst: RValue<'gcc>, cmp: RValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering, failure_order: AtomicOrdering, weak: bool) -> RValue<'gcc> { + let expected = self.current_func().new_local(None, cmp.get_type(), "expected"); + self.llbb().add_assignment(None, expected, cmp); + let success = self.compare_exchange(dst, expected, src, order, failure_order, weak); + + let pair_type = self.cx.type_struct(&[src.get_type(), self.bool_type], false); + let result = self.current_func().new_local(None, pair_type, "atomic_cmpxchg_result"); + let align = Align::from_bits(64).expect("align"); // TODO: use good align. + + let value_type = result.to_rvalue().get_type(); + if let Some(struct_type) = value_type.is_struct() { + self.store(success, result.access_field(None, struct_type.get_field(1)).get_address(None), align); + // NOTE: since success contains the call to the intrinsic, it must be stored before + // expected so that we store expected after the call. + self.store(expected.to_rvalue(), result.access_field(None, struct_type.get_field(0)).get_address(None), align); + } + // TODO: handle when value is not a struct. + + result.to_rvalue() + } + + fn atomic_rmw(&mut self, op: AtomicRmwBinOp, dst: RValue<'gcc>, src: RValue<'gcc>, order: AtomicOrdering) -> RValue<'gcc> { + let size = self.cx.int_width(src.get_type()) / 8; + let name = + match op { + AtomicRmwBinOp::AtomicXchg => format!("__atomic_exchange_{}", size), + AtomicRmwBinOp::AtomicAdd => format!("__atomic_fetch_add_{}", size), + AtomicRmwBinOp::AtomicSub => format!("__atomic_fetch_sub_{}", size), + AtomicRmwBinOp::AtomicAnd => format!("__atomic_fetch_and_{}", size), + AtomicRmwBinOp::AtomicNand => format!("__atomic_fetch_nand_{}", size), + AtomicRmwBinOp::AtomicOr => format!("__atomic_fetch_or_{}", size), + AtomicRmwBinOp::AtomicXor => format!("__atomic_fetch_xor_{}", size), + AtomicRmwBinOp::AtomicMax => return self.atomic_extremum(ExtremumOperation::Max, dst, src, order), + AtomicRmwBinOp::AtomicMin => return self.atomic_extremum(ExtremumOperation::Min, dst, src, order), + AtomicRmwBinOp::AtomicUMax => return self.atomic_extremum(ExtremumOperation::Max, dst, src, order), + AtomicRmwBinOp::AtomicUMin => return self.atomic_extremum(ExtremumOperation::Min, dst, src, order), + }; + + + let atomic_function = self.context.get_builtin_function(name); + let order = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc()); + + let void_ptr_type = self.context.new_type::<*mut ()>(); + let volatile_void_ptr_type = void_ptr_type.make_volatile(); + let dst = self.context.new_cast(None, dst, volatile_void_ptr_type); + // NOTE: not sure why, but we have the wrong type here. + let new_src_type = atomic_function.get_param(1).to_rvalue().get_type(); + let src = self.context.new_cast(None, src, new_src_type); + let res = self.context.new_call(None, atomic_function, &[dst, src, order]); + self.context.new_cast(None, res, src.get_type()) + } + + fn atomic_fence(&mut self, order: AtomicOrdering, scope: SynchronizationScope) { + let name = + match scope { + SynchronizationScope::SingleThread => "__atomic_signal_fence", + SynchronizationScope::CrossThread => "__atomic_thread_fence", + }; + let thread_fence = self.context.get_builtin_function(name); + let order = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc()); + self.llbb().add_eval(None, self.context.new_call(None, thread_fence, &[order])); + } + + fn set_invariant_load(&mut self, load: RValue<'gcc>) { + // NOTE: Hack to consider vtable function pointer as non-global-variable function pointer. + self.normal_function_addresses.borrow_mut().insert(load); + // TODO + /*unsafe { + llvm::LLVMSetMetadata( + load, + llvm::MD_invariant_load as c_uint, + llvm::LLVMMDNodeInContext(self.cx.llcx, ptr::null(), 0), + ); + }*/ + } + + fn lifetime_start(&mut self, _ptr: RValue<'gcc>, _size: Size) { + // TODO + //self.call_lifetime_intrinsic("llvm.lifetime.start.p0i8", ptr, size); + } + + fn lifetime_end(&mut self, _ptr: RValue<'gcc>, _size: Size) { + // TODO + //self.call_lifetime_intrinsic("llvm.lifetime.end.p0i8", ptr, size); + } + + fn call(&mut self, func: RValue<'gcc>, args: &[RValue<'gcc>], funclet: Option<&Funclet>) -> RValue<'gcc> { + // FIXME: remove when having a proper API. + let gcc_func = unsafe { std::mem::transmute(func) }; + if self.functions.borrow().values().find(|value| **value == gcc_func).is_some() { + self.function_call(func, args, funclet) + } + else { + // If it's a not function that was defined, it's a function pointer. + self.function_ptr_call(func, args, funclet) + } + } + + fn zext(&mut self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> { + // FIXME: this does not zero-extend. + if value.get_type().is_bool() && dest_typ.is_i8(&self.cx) { + // FIXME: hack because base::from_immediate converts i1 to i8. + // Fix the code in codegen_ssa::base::from_immediate. + return value; + } + //println!("zext: {:?} -> {:?}", value, dest_typ); + self.context.new_cast(None, value, dest_typ) + } + + fn cx(&self) -> &CodegenCx<'gcc, 'tcx> { + self.cx + } + + fn do_not_inline(&mut self, _llret: RValue<'gcc>) { + unimplemented!(); + //llvm::Attribute::NoInline.apply_callsite(llvm::AttributePlace::Function, llret); + } + + fn set_span(&mut self, _span: Span) {} + + fn from_immediate(&mut self, val: Self::Value) -> Self::Value { + if self.cx().val_ty(val) == self.cx().type_i1() { + self.zext(val, self.cx().type_i8()) + } + else { + val + } + } + + fn to_immediate_scalar(&mut self, val: Self::Value, scalar: &abi::Scalar) -> Self::Value { + if scalar.is_bool() { + return self.trunc(val, self.cx().type_i1()); + } + val + } + + fn fptoui_sat(&mut self, _val: RValue<'gcc>, _dest_ty: Type<'gcc>) -> Option> { + None + } + + fn fptosi_sat(&mut self, _val: RValue<'gcc>, _dest_ty: Type<'gcc>) -> Option> { + None + } + + fn instrprof_increment(&mut self, _fn_name: RValue<'gcc>, _hash: RValue<'gcc>, _num_counters: RValue<'gcc>, _index: RValue<'gcc>) { + unimplemented!(); + /*debug!( + "instrprof_increment() with args ({:?}, {:?}, {:?}, {:?})", + fn_name, hash, num_counters, index + ); + + let llfn = unsafe { llvm::LLVMRustGetInstrProfIncrementIntrinsic(self.cx().llmod) }; + let args = &[fn_name, hash, num_counters, index]; + let args = self.check_call("call", llfn, args); + + unsafe { + let _ = llvm::LLVMRustBuildCall( + self.llbuilder, + llfn, + args.as_ptr() as *const &llvm::Value, + args.len() as c_uint, + None, + ); + }*/ + } +} + +impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { + pub fn shuffle_vector(&mut self, v1: RValue<'gcc>, v2: RValue<'gcc>, mask: RValue<'gcc>) -> RValue<'gcc> { + let return_type = v1.get_type(); + let params = [ + self.context.new_parameter(None, return_type, "v1"), + self.context.new_parameter(None, return_type, "v2"), + self.context.new_parameter(None, mask.get_type(), "mask"), + ]; + let shuffle = self.context.new_function(None, FunctionType::Extern, return_type, ¶ms, "_mm_shuffle_epi8", false); + self.context.new_call(None, shuffle, &[v1, v2, mask]) + } +} + +impl<'a, 'gcc, 'tcx> StaticBuilderMethods for Builder<'a, 'gcc, 'tcx> { + fn get_static(&mut self, def_id: DefId) -> RValue<'gcc> { + // Forward to the `get_static` method of `CodegenCx` + self.cx().get_static(def_id) + } +} + +impl<'tcx> HasParamEnv<'tcx> for Builder<'_, '_, 'tcx> { + fn param_env(&self) -> ParamEnv<'tcx> { + self.cx.param_env() + } +} + +impl<'tcx> HasTargetSpec for Builder<'_, '_, 'tcx> { + fn target_spec(&self) -> &Target { + &self.cx.target_spec() + } +} + +trait ToGccComp { + fn to_gcc_comparison(&self) -> ComparisonOp; +} + +impl ToGccComp for IntPredicate { + fn to_gcc_comparison(&self) -> ComparisonOp { + match *self { + IntPredicate::IntEQ => ComparisonOp::Equals, + IntPredicate::IntNE => ComparisonOp::NotEquals, + IntPredicate::IntUGT => ComparisonOp::GreaterThan, + IntPredicate::IntUGE => ComparisonOp::GreaterThanEquals, + IntPredicate::IntULT => ComparisonOp::LessThan, + IntPredicate::IntULE => ComparisonOp::LessThanEquals, + IntPredicate::IntSGT => ComparisonOp::GreaterThan, + IntPredicate::IntSGE => ComparisonOp::GreaterThanEquals, + IntPredicate::IntSLT => ComparisonOp::LessThan, + IntPredicate::IntSLE => ComparisonOp::LessThanEquals, + } + } +} + +impl ToGccComp for RealPredicate { + fn to_gcc_comparison(&self) -> ComparisonOp { + // TODO: check that ordered vs non-ordered is respected. + match *self { + RealPredicate::RealPredicateFalse => unreachable!(), + RealPredicate::RealOEQ => ComparisonOp::Equals, + RealPredicate::RealOGT => ComparisonOp::GreaterThan, + RealPredicate::RealOGE => ComparisonOp::GreaterThanEquals, + RealPredicate::RealOLT => ComparisonOp::LessThan, + RealPredicate::RealOLE => ComparisonOp::LessThanEquals, + RealPredicate::RealONE => ComparisonOp::NotEquals, + RealPredicate::RealORD => unreachable!(), + RealPredicate::RealUNO => unreachable!(), + RealPredicate::RealUEQ => ComparisonOp::Equals, + RealPredicate::RealUGT => ComparisonOp::GreaterThan, + RealPredicate::RealUGE => ComparisonOp::GreaterThan, + RealPredicate::RealULT => ComparisonOp::LessThan, + RealPredicate::RealULE => ComparisonOp::LessThan, + RealPredicate::RealUNE => ComparisonOp::NotEquals, + RealPredicate::RealPredicateTrue => unreachable!(), + } + } +} + +#[repr(C)] +#[allow(non_camel_case_types)] +enum MemOrdering { + __ATOMIC_RELAXED, + __ATOMIC_CONSUME, + __ATOMIC_ACQUIRE, + __ATOMIC_RELEASE, + __ATOMIC_ACQ_REL, + __ATOMIC_SEQ_CST, +} + +trait ToGccOrdering { + fn to_gcc(self) -> i32; +} + +impl ToGccOrdering for AtomicOrdering { + fn to_gcc(self) -> i32 { + use MemOrdering::*; + + let ordering = + match self { + AtomicOrdering::NotAtomic => __ATOMIC_RELAXED, // TODO: check if that's the same. + AtomicOrdering::Unordered => __ATOMIC_RELAXED, + AtomicOrdering::Monotonic => __ATOMIC_RELAXED, // TODO: check if that's the same. + AtomicOrdering::Acquire => __ATOMIC_ACQUIRE, + AtomicOrdering::Release => __ATOMIC_RELEASE, + AtomicOrdering::AcquireRelease => __ATOMIC_ACQ_REL, + AtomicOrdering::SequentiallyConsistent => __ATOMIC_SEQ_CST, + }; + ordering as i32 + } +} diff --git a/src/callee.rs b/src/callee.rs new file mode 100644 index 0000000000000..d0ae96adcedaf --- /dev/null +++ b/src/callee.rs @@ -0,0 +1,99 @@ +use gccjit::{FunctionType, RValue}; +use rustc_codegen_ssa::traits::BaseTypeMethods; +use rustc_middle::ty::{Instance, TypeFoldable}; +use rustc_middle::ty::layout::{FnAbiExt, HasTyCtxt}; +use rustc_target::abi::call::FnAbi; + +use crate::abi::FnAbiGccExt; +use crate::context::CodegenCx; + +/// Codegens a reference to a fn/method item, monomorphizing and +/// inlining as it goes. +/// +/// # Parameters +/// +/// - `cx`: the crate context +/// - `instance`: the instance to be instantiated +pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>) -> RValue<'gcc> { + let tcx = cx.tcx(); + + //debug!("get_fn(instance={:?})", instance); + + assert!(!instance.substs.needs_infer()); + assert!(!instance.substs.has_escaping_bound_vars()); + assert!(!instance.substs.has_param_types_or_consts()); + + if let Some(&func) = cx.instances.borrow().get(&instance) { + return func; + } + + let sym = tcx.symbol_name(instance).name; + //debug!("get_fn({:?}: {:?}) => {}", instance, instance.monomorphic_ty(cx.tcx()), sym); + + let fn_abi = FnAbi::of_instance(cx, instance, &[]); + + // TODO + let func = + if let Some(func) = cx.get_declared_value(&sym) { + // Create a fn pointer with the new signature. + let ptrty = fn_abi.ptr_to_gcc_type(cx); + + // This is subtle and surprising, but sometimes we have to bitcast + // the resulting fn pointer. The reason has to do with external + // functions. If you have two crates that both bind the same C + // library, they may not use precisely the same types: for + // example, they will probably each declare their own structs, + // which are distinct types from LLVM's point of view (nominal + // types). + // + // Now, if those two crates are linked into an application, and + // they contain inlined code, you can wind up with a situation + // where both of those functions wind up being loaded into this + // application simultaneously. In that case, the same function + // (from LLVM's point of view) requires two types. But of course + // LLVM won't allow one function to have two types. + // + // What we currently do, therefore, is declare the function with + // one of the two types (whichever happens to come first) and then + // bitcast as needed when the function is referenced to make sure + // it has the type we expect. + // + // This can occur on either a crate-local or crate-external + // reference. It also occurs when testing libcore and in some + // other weird situations. Annoying. + if cx.val_ty(func) != ptrty { + //debug!("get_fn: casting {:?} to {:?}", func, ptrty); + // TODO + //cx.const_ptrcast(func, ptrty) + func + } + else { + //debug!("get_fn: not casting pointer!"); + func + } + } + else { + cx.linkage.set(FunctionType::Extern); + let func = cx.declare_fn(&sym, &fn_abi); + //cx.linkage.set(FunctionType::Internal); + //debug!("get_fn: not casting pointer!"); + + // TODO + //attributes::from_fn_attrs(cx, func, instance); + + //let instance_def_id = instance.def_id(); + + // TODO + /*if cx.use_dll_storage_attrs && tcx.is_dllimport_foreign_item(instance_def_id) { + unsafe { + llvm::LLVMSetDLLStorageClass(func, llvm::DLLStorageClass::DllImport); + } + }*/ + + func + }; + + cx.instances.borrow_mut().insert(instance, func); + + func +} diff --git a/src/common.rs b/src/common.rs new file mode 100644 index 0000000000000..3178ada9ec3e2 --- /dev/null +++ b/src/common.rs @@ -0,0 +1,448 @@ +use std::convert::TryFrom; +use std::convert::TryInto; + +use gccjit::{Block, CType, RValue, Type, ToRValue}; +use rustc_codegen_ssa::mir::place::PlaceRef; +use rustc_codegen_ssa::traits::{ + BaseTypeMethods, + ConstMethods, + DerivedTypeMethods, + MiscMethods, + StaticMethods, +}; +use rustc_middle::bug; +use rustc_middle::mir::Mutability; +use rustc_middle::ty::{layout::TyAndLayout, ScalarInt}; +use rustc_mir::interpret::{Allocation, GlobalAlloc, Scalar}; +use rustc_span::Symbol; +use rustc_target::abi::{self, HasDataLayout, LayoutOf, Pointer, Size}; + +use crate::consts::const_alloc_to_gcc; +use crate::context::CodegenCx; +use crate::type_of::LayoutGccExt; + +impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { + pub fn const_bytes(&self, bytes: &[u8]) -> RValue<'gcc> { + bytes_in_context(self, bytes) + } + + fn const_cstr(&self, symbol: Symbol, _null_terminated: bool) -> RValue<'gcc> { + // TODO: handle null_terminated. + if let Some(&value) = self.const_cstr_cache.borrow().get(&symbol) { + return value.to_rvalue(); + } + + let global = self.global_string(&*symbol.as_str()); + + self.const_cstr_cache.borrow_mut().insert(symbol, global.dereference(None)); + global + } + + fn global_string(&self, string: &str) -> RValue<'gcc> { + // TODO: handle non-null-terminated strings. + let string = self.context.new_string_literal(&*string); + let sym = self.generate_local_symbol_name("str"); + // NOTE: TLS is always off for a string litteral. + // NOTE: string litterals do not have a link section. + let global = self.define_global(&sym, self.val_ty(string), false, None) + .unwrap_or_else(|| bug!("symbol `{}` is already defined", sym)); + self.global_init_block.add_assignment(None, global.dereference(None), string); + global.to_rvalue() + //llvm::LLVMRustSetLinkage(global, llvm::Linkage::InternalLinkage); + } + + pub fn inttoptr(&self, block: Block<'gcc>, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { + let func = block.get_function(); + let local = func.new_local(None, value.get_type(), "intLocal"); + block.add_assignment(None, local, value); + let value_address = local.get_address(None); + + let ptr = self.context.new_cast(None, value_address, dest_ty.make_pointer()); + ptr.dereference(None).to_rvalue() + } + + pub fn ptrtoint(&self, block: Block<'gcc>, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { + // TODO: when libgccjit allow casting from pointer to int, remove this. + let func = block.get_function(); + let local = func.new_local(None, value.get_type(), "ptrLocal"); + block.add_assignment(None, local, value); + let ptr_address = local.get_address(None); + + let ptr = self.context.new_cast(None, ptr_address, dest_ty.make_pointer()); + ptr.dereference(None).to_rvalue() + } + + /*pub fn const_vector(&self, elements: &[RValue<'gcc>]) -> RValue<'gcc> { + self.context.new_rvalue_from_vector(None, elements[0].get_type(), elements) + }*/ +} + +pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> RValue<'gcc> { + let context = &cx.context; + let typ = context.new_array_type(None, context.new_type::(), bytes.len() as i32); + let global = cx.declare_unnamed_global(typ); + global.global_set_initializer(bytes); + global.to_rvalue() +} + +pub fn type_is_pointer<'gcc>(typ: Type<'gcc>) -> bool { + typ.get_pointee().is_some() +} + +impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { + fn const_null(&self, typ: Type<'gcc>) -> RValue<'gcc> { + if type_is_pointer(typ) { + self.context.new_null(typ) + } + else { + self.const_int(typ, 0) + } + } + + fn const_undef(&self, typ: Type<'gcc>) -> RValue<'gcc> { + let local = self.current_func.borrow().expect("func") + .new_local(None, typ, "undefined"); + if typ.is_struct().is_some() { + // NOTE: hack to workaround a limitation of the rustc API: see comment on + // CodegenCx.structs_as_pointer + let pointer = local.get_address(None); + self.structs_as_pointer.borrow_mut().insert(pointer); + pointer + } + else { + local.to_rvalue() + } + } + + fn const_int(&self, typ: Type<'gcc>, int: i64) -> RValue<'gcc> { + self.context.new_rvalue_from_long(typ, i64::try_from(int).expect("i64::try_from")) + } + + fn const_uint(&self, typ: Type<'gcc>, int: u64) -> RValue<'gcc> { + self.context.new_rvalue_from_long(typ, u64::try_from(int).expect("u64::try_from") as i64) + } + + fn const_uint_big(&self, typ: Type<'gcc>, num: u128) -> RValue<'gcc> { + let num64: Result = num.try_into(); + if let Ok(num) = num64 { + // FIXME: workaround for a bug where libgccjit is expecting a constant. + // The operations >> 64 and | low are making the normal case a non-constant. + return self.context.new_rvalue_from_long(typ, num as i64); + } + + if num >> 64 != 0 { + // FIXME: use a new function new_rvalue_from_unsigned_long()? + let low = self.context.new_rvalue_from_long(self.u64_type, num as u64 as i64); + let high = self.context.new_rvalue_from_long(typ, (num >> 64) as u64 as i64); + + let sixty_four = self.context.new_rvalue_from_long(typ, 64); + (high << sixty_four) | self.context.new_cast(None, low, typ) + } + else if typ.is_i128(self) { + let num = self.context.new_rvalue_from_long(self.u64_type, num as u64 as i64); + self.context.new_cast(None, num, typ) + } + else { + self.context.new_rvalue_from_long(typ, num as u64 as i64) + } + } + + fn const_bool(&self, val: bool) -> RValue<'gcc> { + self.const_uint(self.type_i1(), val as u64) + } + + fn const_i32(&self, i: i32) -> RValue<'gcc> { + self.const_int(self.type_i32(), i as i64) + } + + fn const_u32(&self, i: u32) -> RValue<'gcc> { + self.const_uint(self.type_u32(), i as u64) + } + + fn const_u64(&self, i: u64) -> RValue<'gcc> { + self.const_uint(self.type_u64(), i) + } + + fn const_usize(&self, i: u64) -> RValue<'gcc> { + let bit_size = self.data_layout().pointer_size.bits(); + if bit_size < 64 { + // make sure it doesn't overflow + assert!(i < (1 << bit_size)); + } + + self.const_uint(self.usize_type, i) + } + + fn const_u8(&self, _i: u8) -> RValue<'gcc> { + unimplemented!(); + //self.const_uint(self.type_i8(), i as u64) + } + + fn const_real(&self, _t: Type<'gcc>, _val: f64) -> RValue<'gcc> { + unimplemented!(); + //unsafe { llvm::LLVMConstReal(t, val) } + } + + fn const_str(&self, s: Symbol) -> (RValue<'gcc>, RValue<'gcc>) { + let len = s.as_str().len(); + let cs = self.const_ptrcast(self.const_cstr(s, false), + self.type_ptr_to(self.layout_of(self.tcx.types.str_).gcc_type(self, true)), + ); + (cs, self.const_usize(len as u64)) + } + + fn const_struct(&self, values: &[RValue<'gcc>], packed: bool) -> RValue<'gcc> { + let fields: Vec<_> = values.iter() + .map(|value| value.get_type()) + .collect(); + // TODO: cache the type? It's anonymous, so probably not. + let name = fields.iter().map(|typ| format!("{:?}", typ)).collect::>().join("_"); + let typ = self.type_struct(&fields, packed); + let structure = self.global_init_func.new_local(None, typ, &name); + let struct_type = typ.is_struct().expect("struct type"); + for (index, value) in values.iter().enumerate() { + let field = struct_type.get_field(index as i32); + let field_lvalue = structure.access_field(None, field); + self.global_init_block.add_assignment(None, field_lvalue, *value); + } + self.lvalue_to_rvalue(structure) + } + + fn const_to_opt_uint(&self, _v: RValue<'gcc>) -> Option { + // TODO + None + //try_as_const_integral(v).map(|v| unsafe { llvm::LLVMConstIntGetZExtValue(v) }) + } + + fn const_to_opt_u128(&self, _v: RValue<'gcc>, _sign_ext: bool) -> Option { + // TODO + None + /*try_as_const_integral(v).and_then(|v| unsafe { + let (mut lo, mut hi) = (0u64, 0u64); + let success = llvm::LLVMRustConstInt128Get(v, sign_ext, &mut hi, &mut lo); + success.then_some(hi_lo_to_u128(lo, hi)) + })*/ + } + + fn scalar_to_backend(&self, cv: Scalar, layout: &abi::Scalar, ty: Type<'gcc>) -> RValue<'gcc> { + let bitsize = if layout.is_bool() { 1 } else { layout.value.size(self).bits() }; + match cv { + Scalar::Int(ScalarInt::ZST) => { + assert_eq!(0, layout.value.size(self).bytes()); + self.const_undef(self.type_ix(0)) + } + Scalar::Int(int) => { + let data = int.assert_bits(layout.value.size(self)); + + // FIXME: there's some issues with using the u128 code that follows, so hard-code + // the paths for floating-point values. + if ty == self.float_type { + return self.context.new_rvalue_from_double(ty, f32::from_bits(data as u32) as f64); + } + else if ty == self.double_type { + return self.context.new_rvalue_from_double(ty, f64::from_bits(data as u64)); + } + + let value = self.const_uint_big(self.type_ix(bitsize), data); + if layout.value == Pointer { + self.inttoptr(self.current_block.borrow().expect("block"), value, ty) + } else { + self.const_bitcast(value, ty) + } + } + Scalar::Ptr(ptr, _size) => { + let (alloc_id, offset) = ptr.into_parts(); + let base_addr = + match self.tcx.global_alloc(alloc_id) { + GlobalAlloc::Memory(alloc) => { + let init = const_alloc_to_gcc(self, alloc); + let value = + match alloc.mutability { + Mutability::Mut => self.static_addr_of_mut(init, alloc.align, None), + _ => self.static_addr_of(init, alloc.align, None), + }; + if !self.sess().fewer_names() { + // TODO + //llvm::set_value_name(value, format!("{:?}", ptr.alloc_id).as_bytes()); + } + value + }, + GlobalAlloc::Function(fn_instance) => { + self.get_fn_addr(fn_instance) + }, + GlobalAlloc::Static(def_id) => { + assert!(self.tcx.is_static(def_id)); + self.get_static(def_id) + }, + }; + let ptr_type = base_addr.get_type(); + let base_addr = self.const_bitcast(base_addr, self.usize_type); + let offset = self.context.new_rvalue_from_long(self.usize_type, offset.bytes() as i64); + let ptr = self.const_bitcast(base_addr + offset, ptr_type); + let value = ptr.dereference(None); + if layout.value != Pointer { + self.const_bitcast(value.to_rvalue(), ty) + } + else { + self.const_bitcast(value.get_address(None), ty) + } + } + } + } + + fn const_data_from_alloc(&self, alloc: &Allocation) -> Self::Value { + const_alloc_to_gcc(self, alloc) + } + + fn from_const_alloc(&self, layout: TyAndLayout<'tcx>, alloc: &Allocation, offset: Size) -> PlaceRef<'tcx, RValue<'gcc>> { + assert_eq!(alloc.align, layout.align.abi); + let ty = self.type_ptr_to(layout.gcc_type(self, true)); + let value = + if layout.size == Size::ZERO { + let value = self.const_usize(alloc.align.bytes()); + self.context.new_cast(None, value, ty) + } + else { + let init = const_alloc_to_gcc(self, alloc); + let base_addr = self.static_addr_of(init, alloc.align, None); + + let array = self.const_bitcast(base_addr, self.type_i8p()); + let value = self.context.new_array_access(None, array, self.const_usize(offset.bytes())).get_address(None); + self.const_bitcast(value, ty) + }; + PlaceRef::new_sized(value, layout) + } + + fn const_ptrcast(&self, val: RValue<'gcc>, ty: Type<'gcc>) -> RValue<'gcc> { + self.context.new_cast(None, val, ty) + } +} + +pub trait SignType<'gcc, 'tcx> { + fn is_signed(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool; + fn is_unsigned(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool; + fn to_signed(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>; +} + +impl<'gcc, 'tcx> SignType<'gcc, 'tcx> for Type<'gcc> { + fn is_signed(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool { + self.is_i8(cx) || self.is_i16(cx) || self.is_i32(cx) || self.is_i64(cx) || self.is_i128(cx) + } + + fn is_unsigned(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool { + self.is_u8(cx) || self.is_u16(cx) || self.is_u32(cx) || self.is_u64(cx) || self.is_u128(cx) + } + + fn to_signed(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> { + if self.is_u8(cx) { + cx.i8_type + } + else if self.is_u16(cx) { + cx.i16_type + } + else if self.is_u32(cx) { + cx.i32_type + } + else if self.is_u64(cx) { + cx.i64_type + } + else if self.is_u128(cx) { + cx.i128_type + } + else { + self.clone() + } + } +} + +pub trait TypeReflection<'gcc, 'tcx> { + fn is_uchar(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool; + fn is_ushort(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool; + fn is_uint(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool; + fn is_ulong(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool; + fn is_ulonglong(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool; + + fn is_i8(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool; + fn is_u8(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool; + fn is_i16(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool; + fn is_u16(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool; + fn is_i32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool; + fn is_u32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool; + fn is_i64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool; + fn is_u64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool; + fn is_i128(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool; + fn is_u128(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool; + + fn is_f32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool; + fn is_f64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool; +} + +impl<'gcc, 'tcx> TypeReflection<'gcc, 'tcx> for Type<'gcc> { + fn is_uchar(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool { + self.unqualified() == cx.u8_type + } + + fn is_ushort(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool { + self.unqualified() == cx.u16_type + } + + fn is_uint(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool { + self.unqualified() == cx.uint_type + } + + fn is_ulong(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool { + self.unqualified() == cx.ulong_type + } + + fn is_ulonglong(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool { + self.unqualified() == cx.ulonglong_type + } + + fn is_i8(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool { + self.unqualified() == cx.i8_type + } + + fn is_u8(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool { + self.unqualified() == cx.u8_type + } + + fn is_i16(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool { + self.unqualified() == cx.i16_type + } + + fn is_u16(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool { + self.unqualified() == cx.u16_type + } + + fn is_i32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool { + self.unqualified() == cx.i32_type + } + + fn is_u32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool { + self.unqualified() == cx.u32_type + } + + fn is_i64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool { + self.unqualified() == cx.i64_type + } + + fn is_u64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool { + self.unqualified() == cx.u64_type + } + + fn is_i128(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool { + self.unqualified() == cx.context.new_c_type(CType::Int128t) + } + + fn is_u128(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool { + self.unqualified() == cx.context.new_c_type(CType::UInt128t) + } + + fn is_f32(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool { + self.unqualified() == cx.context.new_type::() + } + + fn is_f64(&self, cx: &CodegenCx<'gcc, 'tcx>) -> bool { + self.unqualified() == cx.context.new_type::() + } +} diff --git a/src/consts.rs b/src/consts.rs new file mode 100644 index 0000000000000..2cdd7fcab8b46 --- /dev/null +++ b/src/consts.rs @@ -0,0 +1,527 @@ +use gccjit::{RValue, Type}; +use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods, DerivedTypeMethods, StaticMethods}; +use rustc_hir as hir; +use rustc_hir::Node; +use rustc_middle::{bug, span_bug}; +use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; +use rustc_middle::mir::mono::MonoItem; +use rustc_middle::ty::{self, Instance, Ty}; +use rustc_mir::interpret::{self, Allocation, ErrorHandled, Scalar as InterpScalar, read_target_uint}; +use rustc_span::Span; +use rustc_span::def_id::DefId; +use rustc_target::abi::{self, Align, HasDataLayout, LayoutOf, Primitive, Size}; + +use crate::base; +use crate::context::CodegenCx; +use crate::mangled_std_symbols::{ARGC, ARGV, ARGV_INIT_ARRAY}; +use crate::type_of::LayoutGccExt; + +impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { + pub fn const_bitcast(&self, value: RValue<'gcc>, typ: Type<'gcc>) -> RValue<'gcc> { + if value.get_type() == self.bool_type.make_pointer() { + if let Some(pointee) = typ.get_pointee() { + if pointee.is_vector().is_some() { + panic!() + } + } + } + self.context.new_bitcast(None, value, typ) + } +} + +impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> { + fn static_addr_of(&self, cv: RValue<'gcc>, align: Align, kind: Option<&str>) -> RValue<'gcc> { + if let Some(global_value) = self.const_globals.borrow().get(&cv) { + // TODO + /*unsafe { + // Upgrade the alignment in cases where the same constant is used with different + // alignment requirements + let llalign = align.bytes() as u32; + if llalign > llvm::LLVMGetAlignment(gv) { + llvm::LLVMSetAlignment(gv, llalign); + } + }*/ + return *global_value; + } + let global_value = self.static_addr_of_mut(cv, align, kind); + // TODO + /*unsafe { + llvm::LLVMSetGlobalConstant(global_value, True); + }*/ + self.const_globals.borrow_mut().insert(cv, global_value); + global_value + } + + fn codegen_static(&self, def_id: DefId, is_mutable: bool) { + let attrs = self.tcx.codegen_fn_attrs(def_id); + + let instance = Instance::mono(self.tcx, def_id); + let name = &*self.tcx.symbol_name(instance).name; + + let (value, alloc) = + match codegen_static_initializer(&self, def_id) { + Ok(value) => value, + // Error has already been reported + Err(_) => return, + }; + + let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL); + let global = self.get_static(def_id); + + // boolean SSA values are i1, but they have to be stored in i8 slots, + // otherwise some LLVM optimization passes don't work as expected + let val_llty = self.val_ty(value); + let value = + if val_llty == self.type_i1() { + //val_llty = self.type_i8(); + unimplemented!(); + //llvm::LLVMConstZExt(value, val_llty) + } + else { + value + }; + + let instance = Instance::mono(self.tcx, def_id); + let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all()); + let gcc_type = self.layout_of(ty).gcc_type(self, true); + + let global = + if val_llty == gcc_type { + global + } + else { + // If we created the global with the wrong type, + // correct the type. + /*let name = llvm::get_value_name(global).to_vec(); + llvm::set_value_name(global, b""); + + let linkage = llvm::LLVMRustGetLinkage(global); + let visibility = llvm::LLVMRustGetVisibility(global);*/ + + let new_global = self.get_or_insert_global(&name, val_llty, is_tls, attrs.link_section); + + /*llvm::LLVMRustSetLinkage(new_global, linkage); + llvm::LLVMRustSetVisibility(new_global, visibility);*/ + + // To avoid breaking any invariants, we leave around the old + // global for the moment; we'll replace all references to it + // with the new global later. (See base::codegen_backend.) + //self.statics_to_rauw.borrow_mut().push((global, new_global)); + new_global + }; + // TODO + //set_global_alignment(&self, global, self.align_of(ty)); + //llvm::LLVMSetInitializer(global, value); + let value = self.rvalue_as_lvalue(value); + let value = value.get_address(None); + let dest_typ = global.get_type(); + let value = self.context.new_cast(None, value, dest_typ); + + // NOTE: do not init the variables related to argc/argv because it seems we cannot + // overwrite those variables. + // FIXME: correctly support global variable initialization. + let skip_init = [ + ARGV_INIT_ARRAY, + ARGC, + ARGV, + ]; + if !skip_init.iter().any(|symbol_name| name.starts_with(symbol_name)) { + // TODO: switch to set_initializer when libgccjit supports that. + let memcpy = self.context.get_builtin_function("memcpy"); + let dst = self.context.new_cast(None, global, self.type_i8p()); + let src = self.context.new_cast(None, value, self.type_ptr_to(self.type_void())); + let size = self.context.new_rvalue_from_long(self.sizet_type, alloc.size().bytes() as i64); + self.global_init_block.add_eval(None, self.context.new_call(None, memcpy, &[dst, src, size])); + } + + // As an optimization, all shared statics which do not have interior + // mutability are placed into read-only memory. + if !is_mutable { + if self.type_is_freeze(ty) { + // TODO + //llvm::LLVMSetGlobalConstant(global, llvm::True); + } + } + + //debuginfo::create_global_var_metadata(&self, def_id, global); + + if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) { + // Do not allow LLVM to change the alignment of a TLS on macOS. + // + // By default a global's alignment can be freely increased. + // This allows LLVM to generate more performant instructions + // e.g., using load-aligned into a SIMD register. + // + // However, on macOS 10.10 or below, the dynamic linker does not + // respect any alignment given on the TLS (radar 24221680). + // This will violate the alignment assumption, and causing segfault at runtime. + // + // This bug is very easy to trigger. In `println!` and `panic!`, + // the `LOCAL_STDOUT`/`LOCAL_STDERR` handles are stored in a TLS, + // which the values would be `mem::replace`d on initialization. + // The implementation of `mem::replace` will use SIMD + // whenever the size is 32 bytes or higher. LLVM notices SIMD is used + // and tries to align `LOCAL_STDOUT`/`LOCAL_STDERR` to a 32-byte boundary, + // which macOS's dyld disregarded and causing crashes + // (see issues #51794, #51758, #50867, #48866 and #44056). + // + // To workaround the bug, we trick LLVM into not increasing + // the global's alignment by explicitly assigning a section to it + // (equivalent to automatically generating a `#[link_section]` attribute). + // See the comment in the `GlobalValue::canIncreaseAlignment()` function + // of `lib/IR/Globals.cpp` for why this works. + // + // When the alignment is not increased, the optimized `mem::replace` + // will use load-unaligned instructions instead, and thus avoiding the crash. + // + // We could remove this hack whenever we decide to drop macOS 10.10 support. + if self.tcx.sess.target.options.is_like_osx { + // The `inspect` method is okay here because we checked relocations, and + // because we are doing this access to inspect the final interpreter state + // (not as part of the interpreter execution). + // + // FIXME: This check requires that the (arbitrary) value of undefined bytes + // happens to be zero. Instead, we should only check the value of defined bytes + // and set all undefined bytes to zero if this allocation is headed for the + // BSS. + /*let all_bytes_are_zero = alloc.relocations().is_empty() + && alloc + .inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()) + .iter() + .all(|&byte| byte == 0); + + let sect_name = if all_bytes_are_zero { + CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_bss\0") + } else { + CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_data\0") + };*/ + unimplemented!(); + //llvm::LLVMSetSection(global, sect_name.as_ptr()); + } + } + + // Wasm statics with custom link sections get special treatment as they + // go into custom sections of the wasm executable. + if self.tcx.sess.opts.target_triple.triple().starts_with("wasm32") { + if let Some(_section) = attrs.link_section { + unimplemented!(); + /*let section = llvm::LLVMMDStringInContext( + self.llcx, + section.as_str().as_ptr().cast(), + section.as_str().len() as c_uint, + ); + assert!(alloc.relocations().is_empty()); + + // The `inspect` method is okay here because we checked relocations, and + // because we are doing this access to inspect the final interpreter state (not + // as part of the interpreter execution). + let bytes = + alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()); + let alloc = llvm::LLVMMDStringInContext( + self.llcx, + bytes.as_ptr().cast(), + bytes.len() as c_uint, + ); + let data = [section, alloc]; + let meta = llvm::LLVMMDNodeInContext(self.llcx, data.as_ptr(), 2); + llvm::LLVMAddNamedMetadataOperand( + self.llmod, + "wasm.custom_sections\0".as_ptr().cast(), + meta, + );*/ + } + } else { + // TODO + //base::set_link_section(global, &attrs); + } + + if attrs.flags.contains(CodegenFnAttrFlags::USED) { + self.add_used_global(global); + } + } + + /// Add a global value to a list to be stored in the `llvm.used` variable, an array of i8*. + fn add_used_global(&self, _global: RValue<'gcc>) { + // TODO + //let cast = self.context.new_cast(None, global, self.type_i8p()); + //self.used_statics.borrow_mut().push(cast); + } +} + +impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { + pub fn static_addr_of_mut(&self, cv: RValue<'gcc>, align: Align, kind: Option<&str>) -> RValue<'gcc> { + let (name, gv) = + match kind { + Some(kind) if !self.tcx.sess.fewer_names() => { + let name = self.generate_local_symbol_name(kind); + // TODO: check if it's okay that TLS is off here. + // TODO: check if it's okay that link_section is None here. + // TODO: set alignment here as well. + let gv = self.define_global(&name[..], self.val_ty(cv), false, None).unwrap_or_else(|| { + bug!("symbol `{}` is already defined", name); + }); + //llvm::LLVMRustSetLinkage(gv, llvm::Linkage::PrivateLinkage); + (name, gv) + } + _ => { + let index = self.global_gen_sym_counter.get(); + let name = format!("global_{}_{}", index, self.codegen_unit.name()); + let typ = self.val_ty(cv).get_aligned(align.bytes()); + let global = self.define_private_global(typ); + (name, global) + }, + }; + // FIXME: I think the name coming from generate_local_symbol_name() above cannot be used + // globally. + // NOTE: global seems to only be global in a module. So save the name instead of the value + // to import it later. + self.global_names.borrow_mut().insert(cv, name); + self.global_init_block.add_assignment(None, gv.dereference(None), cv); + //llvm::SetUnnamedAddress(gv, llvm::UnnamedAddr::Global); + gv + } + + pub fn get_static(&self, def_id: DefId) -> RValue<'gcc> { + let instance = Instance::mono(self.tcx, def_id); + let fn_attrs = self.tcx.codegen_fn_attrs(def_id); + if let Some(&global) = self.instances.borrow().get(&instance) { + /*let attrs = self.tcx.codegen_fn_attrs(def_id); + let name = &*self.tcx.symbol_name(instance).name; + let name = + if let Some(linkage) = attrs.linkage { + // This is to match what happens in check_and_apply_linkage. + Cow::from(format!("_rust_extern_with_linkage_{}", name)) + } + else { + Cow::from(name) + }; + let global = self.context.new_global(None, GlobalKind::Imported, global.get_type(), &name) + .get_address(None); + self.global_names.borrow_mut().insert(global, name.to_string());*/ + return global; + } + + let defined_in_current_codegen_unit = + self.codegen_unit.items().contains_key(&MonoItem::Static(def_id)); + assert!( + !defined_in_current_codegen_unit, + "consts::get_static() should always hit the cache for \ + statics defined in the same CGU, but did not for `{:?}`", + def_id + ); + + let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all()); + let sym = self.tcx.symbol_name(instance).name; + + //debug!("get_static: sym={} instance={:?}", sym, instance); + + let global = + if let Some(def_id) = def_id.as_local() { + let id = self.tcx.hir().local_def_id_to_hir_id(def_id); + let llty = self.layout_of(ty).gcc_type(self, true); + // FIXME: refactor this to work without accessing the HIR + let global = match self.tcx.hir().get(id) { + Node::Item(&hir::Item { span, kind: hir::ItemKind::Static(..), .. }) => { + if let Some(global) = self.get_declared_value(&sym) { + if self.val_ty(global) != self.type_ptr_to(llty) { + span_bug!(span, "Conflicting types for static"); + } + } + + let is_tls = fn_attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL); + let global = self.declare_global(&sym, llty, is_tls, fn_attrs.link_section); + + if !self.tcx.is_reachable_non_generic(def_id) { + /*unsafe { + llvm::LLVMRustSetVisibility(global, llvm::Visibility::Hidden); + }*/ + } + + global + } + + Node::ForeignItem(&hir::ForeignItem { + span, + kind: hir::ForeignItemKind::Static(..), + .. + }) => { + let fn_attrs = self.tcx.codegen_fn_attrs(def_id); + check_and_apply_linkage(&self, &fn_attrs, ty, sym, span) + } + + item => bug!("get_static: expected static, found {:?}", item), + }; + + //debug!("get_static: sym={} attrs={:?}", sym, attrs); + + global + } + else { + // FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow? + //debug!("get_static: sym={} item_attr={:?}", sym, self.tcx.item_attrs(def_id)); + + let attrs = self.tcx.codegen_fn_attrs(def_id); + let span = self.tcx.def_span(def_id); + let global = check_and_apply_linkage(&self, &attrs, ty, sym, span); + + let needs_dll_storage_attr = false; /*self.use_dll_storage_attrs && !self.tcx.is_foreign_item(def_id) && + // ThinLTO can't handle this workaround in all cases, so we don't + // emit the attrs. Instead we make them unnecessary by disallowing + // dynamic linking when linker plugin based LTO is enabled. + !self.tcx.sess.opts.cg.linker_plugin_lto.enabled();*/ + + // If this assertion triggers, there's something wrong with commandline + // argument validation. + debug_assert!( + !(self.tcx.sess.opts.cg.linker_plugin_lto.enabled() + && self.tcx.sess.target.options.is_like_msvc + && self.tcx.sess.opts.cg.prefer_dynamic) + ); + + if needs_dll_storage_attr { + // This item is external but not foreign, i.e., it originates from an external Rust + // crate. Since we don't know whether this crate will be linked dynamically or + // statically in the final application, we always mark such symbols as 'dllimport'. + // If final linkage happens to be static, we rely on compiler-emitted __imp_ stubs + // to make things work. + // + // However, in some scenarios we defer emission of statics to downstream + // crates, so there are cases where a static with an upstream DefId + // is actually present in the current crate. We can find out via the + // is_codegened_item query. + if !self.tcx.is_codegened_item(def_id) { + unimplemented!(); + /*unsafe { + llvm::LLVMSetDLLStorageClass(global, llvm::DLLStorageClass::DllImport); + }*/ + } + } + global + }; + + /*if self.use_dll_storage_attrs && self.tcx.is_dllimport_foreign_item(def_id) { + // For foreign (native) libs we know the exact storage type to use. + unsafe { + llvm::LLVMSetDLLStorageClass(global, llvm::DLLStorageClass::DllImport); + } + }*/ + + self.instances.borrow_mut().insert(instance, global); + global + } +} + +pub fn const_alloc_to_gcc<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, alloc: &Allocation) -> RValue<'gcc> { + let mut llvals = Vec::with_capacity(alloc.relocations().len() + 1); + let dl = cx.data_layout(); + let pointer_size = dl.pointer_size.bytes() as usize; + + let mut next_offset = 0; + for &(offset, alloc_id) in alloc.relocations().iter() { + let offset = offset.bytes(); + assert_eq!(offset as usize as u64, offset); + let offset = offset as usize; + if offset > next_offset { + // This `inspect` is okay since we have checked that it is not within a relocation, it + // is within the bounds of the allocation, and it doesn't affect interpreter execution + // (we inspect the result after interpreter execution). Any undef byte is replaced with + // some arbitrary byte value. + // + // FIXME: relay undef bytes to codegen as undef const bytes + let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(next_offset..offset); + llvals.push(cx.const_bytes(bytes)); + } + let ptr_offset = + read_target_uint( dl.endian, + // This `inspect` is okay since it is within the bounds of the allocation, it doesn't + // affect interpreter execution (we inspect the result after interpreter execution), + // and we properly interpret the relocation as a relocation pointer offset. + alloc.inspect_with_uninit_and_ptr_outside_interpreter(offset..(offset + pointer_size)), + ) + .expect("const_alloc_to_llvm: could not read relocation pointer") + as u64; + llvals.push(cx.scalar_to_backend( + InterpScalar::from_pointer( + interpret::Pointer::new(alloc_id, Size::from_bytes(ptr_offset)), + &cx.tcx, + ), + &abi::Scalar { value: Primitive::Pointer, valid_range: 0..=!0 }, + cx.type_i8p(), + )); + next_offset = offset + pointer_size; + } + if alloc.len() >= next_offset { + let range = next_offset..alloc.len(); + // This `inspect` is okay since we have check that it is after all relocations, it is + // within the bounds of the allocation, and it doesn't affect interpreter execution (we + // inspect the result after interpreter execution). Any undef byte is replaced with some + // arbitrary byte value. + // + // FIXME: relay undef bytes to codegen as undef const bytes + let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(range); + llvals.push(cx.const_bytes(bytes)); + } + + cx.const_struct(&llvals, true) +} + +pub fn codegen_static_initializer<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, def_id: DefId) -> Result<(RValue<'gcc>, &'tcx Allocation), ErrorHandled> { + let alloc = cx.tcx.eval_static_initializer(def_id)?; + Ok((const_alloc_to_gcc(cx, alloc), alloc)) +} + +fn check_and_apply_linkage<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, attrs: &CodegenFnAttrs, ty: Ty<'tcx>, sym: &str, span: Span) -> RValue<'gcc> { + let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL); + let llty = cx.layout_of(ty).gcc_type(cx, true); + if let Some(linkage) = attrs.linkage { + //debug!("get_static: sym={} linkage={:?}", sym, linkage); + + // If this is a static with a linkage specified, then we need to handle + // it a little specially. The typesystem prevents things like &T and + // extern "C" fn() from being non-null, so we can't just declare a + // static and call it a day. Some linkages (like weak) will make it such + // that the static actually has a null value. + let llty2 = + if let ty::RawPtr(ref mt) = ty.kind() { + cx.layout_of(mt.ty).gcc_type(cx, true) + } + else { + cx.sess().span_fatal( + span, + "must have type `*const T` or `*mut T` due to `#[linkage]` attribute", + ) + }; + // Declare a symbol `foo` with the desired linkage. + let global1 = cx.declare_global_with_linkage(&sym, llty2, base::global_linkage_to_gcc(linkage)); + + // 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 mut real_name = "_rust_extern_with_linkage_".to_string(); + real_name.push_str(&sym); + let global2 = + cx.define_global(&real_name, llty, is_tls, attrs.link_section).unwrap_or_else(|| { + cx.sess().span_fatal(span, &format!("symbol `{}` is already defined", &sym)) + }); + //llvm::LLVMRustSetLinkage(global2, llvm::Linkage::InternalLinkage); + let lvalue = global2.dereference(None); + cx.global_init_block.add_assignment(None, lvalue, global1); + //llvm::LLVMSetInitializer(global2, global1); + global2 + } + else { + // Generate an external declaration. + // FIXME(nagisa): investigate whether it can be changed into define_global + + // Thread-local statics in some other crate need to *always* be linked + // against in a thread-local fashion, so we need to be sure to apply the + // thread-local attribute locally if it was present remotely. If we + // don't do this then linker errors can be generated where the linker + // complains that one object files has a thread local version of the + // symbol and another one doesn't. + cx.declare_global(&sym, llty, is_tls, attrs.link_section) + } +} diff --git a/src/context.rs b/src/context.rs new file mode 100644 index 0000000000000..9cbbee772c5fb --- /dev/null +++ b/src/context.rs @@ -0,0 +1,491 @@ +use std::cell::{Cell, RefCell}; + +use gccjit::{ + Block, + Context, + CType, + Function, + FunctionType, + LValue, + RValue, + Struct, + Type, +}; +use rustc_codegen_ssa::base::wants_msvc_seh; +use rustc_codegen_ssa::traits::{ + BackendTypes, + BaseTypeMethods, + MiscMethods, +}; +use rustc_data_structures::base_n; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_middle::bug; +use rustc_middle::mir::mono::CodegenUnit; +use rustc_middle::ty::{self, Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt}; +use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutError, TyAndLayout}; +use rustc_session::Session; +use rustc_span::{Span, Symbol, DUMMY_SP}; +use rustc_target::abi::{HasDataLayout, LayoutOf, PointeeInfo, Size, TargetDataLayout, VariantIdx}; +use rustc_target::spec::{HasTargetSpec, Target, TlsModel}; + +use crate::callee::get_fn; +use crate::declare::mangle_name; + +#[derive(Clone)] +pub struct FuncSig<'gcc> { + pub params: Vec>, + pub return_type: Type<'gcc>, +} + +pub struct CodegenCx<'gcc, 'tcx> { + pub check_overflow: bool, + pub codegen_unit: &'tcx CodegenUnit<'tcx>, + pub context: &'gcc Context<'gcc>, + + // TODO: First set it to a dummy block to avoid using Option? + pub current_block: RefCell>>, + pub current_func: RefCell>>, + pub normal_function_addresses: RefCell>>, + + /// The function where globals are initialized. + pub global_init_func: Function<'gcc>, + pub global_init_block: Block<'gcc>, + + pub functions: RefCell>>, + + pub tls_model: gccjit::TlsModel, + + pub bool_type: Type<'gcc>, + pub i8_type: Type<'gcc>, + pub i16_type: Type<'gcc>, + pub i32_type: Type<'gcc>, + pub i64_type: Type<'gcc>, + pub i128_type: Type<'gcc>, + pub isize_type: Type<'gcc>, + + pub u8_type: Type<'gcc>, + pub u16_type: Type<'gcc>, + pub u32_type: Type<'gcc>, + pub u64_type: Type<'gcc>, + pub u128_type: Type<'gcc>, + pub usize_type: Type<'gcc>, + + pub int_type: Type<'gcc>, + pub uint_type: Type<'gcc>, + pub long_type: Type<'gcc>, + pub ulong_type: Type<'gcc>, + pub ulonglong_type: Type<'gcc>, + pub sizet_type: Type<'gcc>, + + pub float_type: Type<'gcc>, + pub double_type: Type<'gcc>, + + pub linkage: Cell, + pub scalar_types: RefCell, Type<'gcc>>>, + pub types: RefCell, Option), Type<'gcc>>>, + pub tcx: TyCtxt<'tcx>, + + pub struct_types: RefCell>, Type<'gcc>>>, + + pub types_with_fields_to_set: RefCell, (Struct<'gcc>, TyAndLayout<'tcx>)>>, + + /// Cache instances of monomorphic and polymorphic items + pub instances: RefCell, RValue<'gcc>>>, + /// Cache generated vtables + pub vtables: RefCell, Option>), RValue<'gcc>>>, + + /// Cache of emitted const globals (value -> global) + pub const_globals: RefCell, RValue<'gcc>>>, + + pub init_argv_var: RefCell, + pub argv_initialized: Cell, + + /// Cache of constant strings, + pub const_cstr_cache: RefCell>>, + + /// Cache of globals. + pub globals: RefCell>>, + // TODO: remove global_names. + pub global_names: RefCell, String>>, + + /// A counter that is used for generating local symbol names + local_gen_sym_counter: Cell, + pub global_gen_sym_counter: Cell, + + eh_personality: Cell>>, + + pub pointee_infos: RefCell, Size), Option>>, + + /// NOTE: a hack is used because the rustc API is not suitable to libgccjit and as such, + /// `const_undef()` returns struct as pointer so that they can later be assigned a value. + /// As such, this set remembers which of these pointers were returned by this function so that + /// they can be derefered later. + /// FIXME: fix the rustc API to avoid having this hack. + pub structs_as_pointer: RefCell>>, + + /// Store the pointer of different types for safety. + /// When casting the values back to their original types, check that they are indeed that type + /// with these sets. + /// FIXME: remove when the API supports more types. + #[cfg(debug_assertions)] + lvalues: RefCell>>, +} + +impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { + pub fn new(context: &'gcc Context<'gcc>, codegen_unit: &'tcx CodegenUnit<'tcx>, tcx: TyCtxt<'tcx>) -> Self { + let check_overflow = tcx.sess.overflow_checks(); + // TODO: fix this mess. libgccjit seems to return random type when using new_int_type(). + //let isize_type = context.new_int_type((tcx.data_layout.pointer_size.bits() / 8) as i32, true); + let isize_type = context.new_c_type(CType::LongLong); + //let usize_type = context.new_int_type((tcx.data_layout.pointer_size.bits() / 8) as i32, false); + let usize_type = context.new_c_type(CType::ULongLong); + let bool_type = context.new_type::(); + let i8_type = context.new_type::(); + let i16_type = context.new_type::(); + let i32_type = context.new_type::(); + let i64_type = context.new_c_type(CType::LongLong); + let i128_type = context.new_c_type(CType::Int128t).get_aligned(8); // TODO: should this be hard-coded? + let u8_type = context.new_type::(); + let u16_type = context.new_type::(); + let u32_type = context.new_type::(); + let u64_type = context.new_c_type(CType::ULongLong); + let u128_type = context.new_c_type(CType::UInt128t).get_aligned(8); // TODO: should this be hard-coded? + + let tls_model = to_gcc_tls_mode(tcx.sess.tls_model()); + + let float_type = context.new_type::(); + let double_type = context.new_type::(); + + let int_type = context.new_c_type(CType::Int); + let uint_type = context.new_c_type(CType::UInt); + let long_type = context.new_c_type(CType::Long); + let ulong_type = context.new_c_type(CType::ULong); + let ulonglong_type = context.new_c_type(CType::ULongLong); + let sizet_type = context.new_c_type(CType::SizeT); + + assert_eq!(isize_type, i64_type); + assert_eq!(usize_type, u64_type); + + let mut functions = FxHashMap::default(); + let builtins = [ + "__builtin_unreachable", "abort", "__builtin_expect", "__builtin_add_overflow", "__builtin_mul_overflow", + "__builtin_saddll_overflow", /*"__builtin_sadd_overflow",*/ "__builtin_smulll_overflow", /*"__builtin_smul_overflow",*/ + "__builtin_ssubll_overflow", /*"__builtin_ssub_overflow",*/ "__builtin_sub_overflow", "__builtin_uaddll_overflow", + "__builtin_uadd_overflow", "__builtin_umulll_overflow", "__builtin_umul_overflow", "__builtin_usubll_overflow", + "__builtin_usub_overflow", "sqrtf", "sqrt", "__builtin_powif", "__builtin_powi", "sinf", "sin", "cosf", "cos", + "powf", "pow", "expf", "exp", "exp2f", "exp2", "logf", "log", "log10f", "log10", "log2f", "log2", "fmaf", + "fma", "fabsf", "fabs", "fminf", "fmin", "fmaxf", "fmax", "copysignf", "copysign", "floorf", "floor", "ceilf", + "ceil", "truncf", "trunc", "rintf", "rint", "nearbyintf", "nearbyint", "roundf", "round", + "__builtin_expect_with_probability", + ]; + + for builtin in builtins.iter() { + functions.insert(builtin.to_string(), context.get_builtin_function(builtin)); + } + + let global_init_func = context.new_function(None, FunctionType::Exported, context.new_type::<()>(), &[], + &format!("__gccGlobalInit{}", unit_name(&codegen_unit)), false); + let global_init_block = global_init_func.new_block("initial"); + + Self { + check_overflow, + codegen_unit, + context, + current_block: RefCell::new(None), + current_func: RefCell::new(None), + normal_function_addresses: Default::default(), + functions: RefCell::new(functions), + global_init_func, + global_init_block, + + tls_model, + + bool_type, + i8_type, + i16_type, + i32_type, + i64_type, + i128_type, + isize_type, + usize_type, + u8_type, + u16_type, + u32_type, + u64_type, + u128_type, + int_type, + uint_type, + long_type, + ulong_type, + ulonglong_type, + sizet_type, + + float_type, + double_type, + + linkage: Cell::new(FunctionType::Internal), + #[cfg(debug_assertions)] + lvalues: Default::default(), + instances: Default::default(), + vtables: Default::default(), + const_globals: Default::default(), + init_argv_var: RefCell::new(String::new()), + argv_initialized: Cell::new(false), + const_cstr_cache: Default::default(), + global_names: Default::default(), + globals: Default::default(), + scalar_types: Default::default(), + types: Default::default(), + tcx, + struct_types: Default::default(), + types_with_fields_to_set: Default::default(), + local_gen_sym_counter: Cell::new(0), + global_gen_sym_counter: Cell::new(0), + eh_personality: Cell::new(None), + pointee_infos: Default::default(), + structs_as_pointer: Default::default(), + } + } + + pub fn lvalue_to_rvalue(&self, value: LValue<'gcc>) -> RValue<'gcc> { + #[cfg(debug_assertions)] + self.lvalues.borrow_mut().insert(value); + unsafe { std::mem::transmute(value) } + } + + pub fn rvalue_as_function(&self, value: RValue<'gcc>) -> Function<'gcc> { + let function: Function<'gcc> = unsafe { std::mem::transmute(value) }; + debug_assert!(self.functions.borrow().values().find(|value| **value == function).is_some(), + "{:?} ({:?}) is not a function", value, value.get_type()); + function + } + + pub fn rvalue_as_lvalue(&self, value: RValue<'gcc>) -> LValue<'gcc> { + let lvalue: LValue<'gcc> = unsafe { std::mem::transmute(value) }; + //debug_assert!(self.lvalues.borrow().contains(&lvalue), "{:?} is not an lvalue", value); + lvalue + } + + pub fn sess(&self) -> &Session { + &self.tcx.sess + } +} + +impl<'gcc, 'tcx> BackendTypes for CodegenCx<'gcc, 'tcx> { + type Value = RValue<'gcc>; + type Function = RValue<'gcc>; + + type BasicBlock = Block<'gcc>; + type Type = Type<'gcc>; + type Funclet = (); // TODO + + type DIScope = (); // TODO + type DILocation = (); // TODO + type DIVariable = (); // TODO +} + +impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> { + fn vtables(&self) -> &RefCell, Option>), RValue<'gcc>>> { + &self.vtables + } + + fn get_fn(&self, instance: Instance<'tcx>) -> RValue<'gcc> { + let func = get_fn(self, instance); + *self.current_func.borrow_mut() = Some(self.rvalue_as_function(func)); + func + } + + fn get_fn_addr(&self, instance: Instance<'tcx>) -> RValue<'gcc> { + //let symbol = self.tcx.symbol_name(instance).name; + + let func = get_fn(self, instance); + let func = self.rvalue_as_function(func); + let ptr = func.get_address(None); + + // TODO: don't do this twice: i.e. in declare_fn and here. + //let fn_abi = FnAbi::of_instance(self, instance, &[]); + //let (return_type, params, _) = fn_abi.gcc_type(self); + // FIXME: the rustc API seems to call get_fn_addr() when not needed (e.g. for FFI). + //let pointer_type = ptr.get_type(); + + self.normal_function_addresses.borrow_mut().insert(ptr); + + ptr + } + + fn eh_personality(&self) -> RValue<'gcc> { + // The exception handling personality function. + // + // If our compilation unit has the `eh_personality` lang item somewhere + // within it, then we just need to codegen that. Otherwise, we're + // building an rlib which will depend on some upstream implementation of + // this function, so we just codegen a generic reference to it. We don't + // specify any of the types for the function, we just make it a symbol + // that LLVM can later use. + // + // Note that MSVC is a little special here in that we don't use the + // `eh_personality` lang item at all. Currently LLVM has support for + // both Dwarf and SEH unwind mechanisms for MSVC targets and uses the + // *name of the personality function* to decide what kind of unwind side + // tables/landing pads to emit. It looks like Dwarf is used by default, + // injecting a dependency on the `_Unwind_Resume` symbol for resuming + // an "exception", but for MSVC we want to force SEH. This means that we + // can't actually have the personality function be our standard + // `rust_eh_personality` function, but rather we wired it up to the + // CRT's custom personality function, which forces LLVM to consider + // landing pads as "landing pads for SEH". + if let Some(llpersonality) = self.eh_personality.get() { + return llpersonality; + } + let tcx = self.tcx; + let llfn = match tcx.lang_items().eh_personality() { + Some(def_id) if !wants_msvc_seh(self.sess()) => self.get_fn_addr( + ty::Instance::resolve( + tcx, + ty::ParamEnv::reveal_all(), + def_id, + tcx.intern_substs(&[]), + ) + .unwrap().unwrap(), + ), + _ => { + let name = if wants_msvc_seh(self.sess()) { + "__CxxFrameHandler3" + } else { + "rust_eh_personality" + }; + self.declare_func(name, self.type_i32(), &[], true) + } + }; + //attributes::apply_target_cpu_attr(self, llfn); + self.eh_personality.set(Some(llfn)); + llfn + } + + fn sess(&self) -> &Session { + &self.tcx.sess + } + + fn check_overflow(&self) -> bool { + self.check_overflow + } + + fn codegen_unit(&self) -> &'tcx CodegenUnit<'tcx> { + self.codegen_unit + } + + fn used_statics(&self) -> &RefCell>> { + unimplemented!(); + //&self.used_statics + } + + fn set_frame_pointer_type(&self, _llfn: RValue<'gcc>) { + // TODO + //attributes::set_frame_pointer_type(self, llfn) + } + + fn apply_target_cpu_attr(&self, _llfn: RValue<'gcc>) { + // TODO + //attributes::apply_target_cpu_attr(self, llfn) + } + + fn create_used_variable(&self) { + unimplemented!(); + /*let name = const_cstr!("llvm.used"); + let section = const_cstr!("llvm.metadata"); + let array = + self.const_array(&self.type_ptr_to(self.type_i8()), &*self.used_statics.borrow()); + + unsafe { + let g = llvm::LLVMAddGlobal(self.llmod, self.val_ty(array), name.as_ptr()); + llvm::LLVMSetInitializer(g, array); + llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage); + llvm::LLVMSetSection(g, section.as_ptr()); + }*/ + } + + fn declare_c_main(&self, fn_type: Self::Type) -> Option { + if self.get_declared_value("main").is_none() { + Some(self.declare_cfn("main", fn_type)) + } + else { + // If the symbol already exists, it is an error: for example, the user wrote + // #[no_mangle] extern "C" fn main(..) {..} + // instead of #[start] + None + } + } +} + +impl<'gcc, 'tcx> HasTyCtxt<'tcx> for CodegenCx<'gcc, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } +} + +impl<'gcc, 'tcx> HasDataLayout for CodegenCx<'gcc, 'tcx> { + fn data_layout(&self) -> &TargetDataLayout { + &self.tcx.data_layout + } +} + +impl<'gcc, 'tcx> HasTargetSpec for CodegenCx<'gcc, 'tcx> { + fn target_spec(&self) -> &Target { + &self.tcx.sess.target + } +} + +impl<'gcc, 'tcx> LayoutOf for CodegenCx<'gcc, 'tcx> { + type Ty = Ty<'tcx>; + type TyAndLayout = TyAndLayout<'tcx>; + + fn layout_of(&self, ty: Ty<'tcx>) -> Self::TyAndLayout { + self.spanned_layout_of(ty, DUMMY_SP) + } + + fn spanned_layout_of(&self, ty: Ty<'tcx>, span: Span) -> Self::TyAndLayout { + self.tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap_or_else(|e| { + if let LayoutError::SizeOverflow(_) = e { + self.sess().span_fatal(span, &e.to_string()) + } else { + bug!("failed to get layout for `{}`: {}", ty, e) + } + }) + } +} + +impl<'tcx, 'gcc> HasParamEnv<'tcx> for CodegenCx<'gcc, 'tcx> { + fn param_env(&self) -> ParamEnv<'tcx> { + ParamEnv::reveal_all() + } +} + +impl<'b, 'tcx> CodegenCx<'b, 'tcx> { + /// Generates a new symbol name with the given prefix. This symbol name must + /// only be used for definitions with `internal` or `private` linkage. + pub fn generate_local_symbol_name(&self, prefix: &str) -> String { + let idx = self.local_gen_sym_counter.get(); + self.local_gen_sym_counter.set(idx + 1); + // Include a '.' character, so there can be no accidental conflicts with + // user defined names + let mut name = String::with_capacity(prefix.len() + 6); + name.push_str(prefix); + name.push_str("."); + base_n::push_str(idx as u128, base_n::ALPHANUMERIC_ONLY, &mut name); + name + } +} + +pub fn unit_name<'tcx>(codegen_unit: &CodegenUnit<'tcx>) -> String { + let name = &codegen_unit.name().to_string(); + mangle_name(&name.replace('-', "_")) +} + +fn to_gcc_tls_mode(tls_model: TlsModel) -> gccjit::TlsModel { + match tls_model { + TlsModel::GeneralDynamic => gccjit::TlsModel::GlobalDynamic, + TlsModel::LocalDynamic => gccjit::TlsModel::LocalDynamic, + TlsModel::InitialExec => gccjit::TlsModel::InitialExec, + TlsModel::LocalExec => gccjit::TlsModel::LocalExec, + } +} diff --git a/src/coverageinfo.rs b/src/coverageinfo.rs new file mode 100644 index 0000000000000..f966f6e75336f --- /dev/null +++ b/src/coverageinfo.rs @@ -0,0 +1,140 @@ +use gccjit::RValue; +use rustc_codegen_ssa::traits::{CoverageInfoBuilderMethods, CoverageInfoMethods}; +use rustc_hir::def_id::DefId; +use rustc_middle::mir::coverage::{ + CodeRegion, + CounterValueReference, + ExpressionOperandId, + InjectedExpressionId, + Op, +}; +use rustc_middle::ty::Instance; + +use crate::builder::Builder; +use crate::context::CodegenCx; + +impl<'a, 'gcc, 'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { + fn set_function_source_hash( + &mut self, + _instance: Instance<'tcx>, + _function_source_hash: u64, + ) -> bool { + unimplemented!(); + /*if let Some(coverage_context) = self.coverage_context() { + debug!( + "ensuring function source hash is set for instance={:?}; function_source_hash={}", + instance, function_source_hash, + ); + let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); + coverage_map + .entry(instance) + .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) + .set_function_source_hash(function_source_hash); + true + } else { + false + }*/ + } + + fn add_coverage_counter(&mut self, _instance: Instance<'tcx>, _id: CounterValueReference, _region: CodeRegion) -> bool { + /*if let Some(coverage_context) = self.coverage_context() { + debug!( + "adding counter to coverage_regions: instance={:?}, function_source_hash={}, id={:?}, \ + at {:?}", + instance, function_source_hash, id, region, + ); + let mut coverage_regions = coverage_context.function_coverage_map.borrow_mut(); + coverage_regions + .entry(instance) + .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) + .add_counter(function_source_hash, id, region); + true + } else { + false + }*/ + // TODO + false + } + + fn add_coverage_counter_expression(&mut self, _instance: Instance<'tcx>, _id: InjectedExpressionId, _lhs: ExpressionOperandId, _op: Op, _rhs: ExpressionOperandId, _region: Option) -> bool { + /*if let Some(coverage_context) = self.coverage_context() { + debug!( + "adding counter expression to coverage_regions: instance={:?}, id={:?}, {:?} {:?} {:?}, \ + at {:?}", + instance, id, lhs, op, rhs, region, + ); + let mut coverage_regions = coverage_context.function_coverage_map.borrow_mut(); + coverage_regions + .entry(instance) + .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) + .add_counter_expression(id, lhs, op, rhs, region); + true + } else { + false + }*/ + // TODO + false + } + + fn add_coverage_unreachable(&mut self, _instance: Instance<'tcx>, _region: CodeRegion) -> bool { + /*if let Some(coverage_context) = self.coverage_context() { + debug!( + "adding unreachable code to coverage_regions: instance={:?}, at {:?}", + instance, region, + ); + let mut coverage_regions = coverage_context.function_coverage_map.borrow_mut(); + coverage_regions + .entry(instance) + .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) + .add_unreachable_region(region); + true + } else { + false + }*/ + // TODO + false + } +} + +impl<'gcc, 'tcx> CoverageInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> { + fn coverageinfo_finalize(&self) { + // TODO + //mapgen::finalize(self) + } + + fn get_pgo_func_name_var(&self, _instance: Instance<'tcx>) -> RValue<'gcc> { + unimplemented!(); + /*if let Some(coverage_context) = self.coverage_context() { + debug!("getting pgo_func_name_var for instance={:?}", instance); + let mut pgo_func_name_var_map = coverage_context.pgo_func_name_var_map.borrow_mut(); + pgo_func_name_var_map + .entry(instance) + .or_insert_with(|| create_pgo_func_name_var(self, instance)) + } else { + bug!("Could not get the `coverage_context`"); + }*/ + } + + /// Functions with MIR-based coverage are normally codegenned _only_ if + /// called. LLVM coverage tools typically expect every function to be + /// defined (even if unused), with at least one call to LLVM intrinsic + /// `instrprof.increment`. + /// + /// Codegen a small function that will never be called, with one counter + /// that will never be incremented. + /// + /// For used/called functions, the coverageinfo was already added to the + /// `function_coverage_map` (keyed by function `Instance`) during codegen. + /// But in this case, since the unused function was _not_ previously + /// codegenned, collect the coverage `CodeRegion`s from the MIR and add + /// them. The first `CodeRegion` is used to add a single counter, with the + /// same counter ID used in the injected `instrprof.increment` intrinsic + /// call. Since the function is never called, all other `CodeRegion`s can be + /// added as `unreachable_region`s. + fn define_unused_fn(&self, _def_id: DefId) { + unimplemented!(); + /*let instance = declare_unused_fn(self, &def_id); + codegen_unused_fn_and_counter(self, instance); + add_unused_function_coverage(self, instance, def_id);*/ + } +} diff --git a/src/debuginfo.rs b/src/debuginfo.rs new file mode 100644 index 0000000000000..a8902e6966d53 --- /dev/null +++ b/src/debuginfo.rs @@ -0,0 +1,407 @@ +use gccjit::{FunctionType, RValue}; +use rustc_codegen_ssa::mir::debuginfo::{FunctionDebugContext, VariableKind}; +use rustc_codegen_ssa::traits::{BuilderMethods, DebugInfoBuilderMethods, DebugInfoMethods}; +use rustc_middle::middle::cstore::CrateDepKind; +use rustc_middle::mir; +use rustc_middle::ty::{Instance, Ty}; +use rustc_span::{SourceFile, Span, Symbol}; +use rustc_span::def_id::LOCAL_CRATE; +use rustc_target::abi::Size; +use rustc_target::abi::call::FnAbi; + +use crate::builder::Builder; +use crate::context::CodegenCx; + +impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> { + // FIXME(eddyb) find a common convention for all of the debuginfo-related + // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.). + fn dbg_var_addr(&mut self, _dbg_var: Self::DIVariable, _scope_metadata: Self::DIScope, _variable_alloca: Self::Value, _direct_offset: Size, _indirect_offsets: &[Size]) { + unimplemented!(); + /*let cx = self.cx(); + + // Convert the direct and indirect offsets to address ops. + // FIXME(eddyb) use `const`s instead of getting the values via FFI, + // the values should match the ones in the DWARF standard anyway. + let op_deref = || unsafe { llvm::LLVMRustDIBuilderCreateOpDeref() }; + let op_plus_uconst = || unsafe { llvm::LLVMRustDIBuilderCreateOpPlusUconst() }; + let mut addr_ops = SmallVec::<[_; 8]>::new(); + + if direct_offset.bytes() > 0 { + addr_ops.push(op_plus_uconst()); + addr_ops.push(direct_offset.bytes() as i64); + } + for &offset in indirect_offsets { + addr_ops.push(op_deref()); + if offset.bytes() > 0 { + addr_ops.push(op_plus_uconst()); + addr_ops.push(offset.bytes() as i64); + } + } + + // FIXME(eddyb) maybe this information could be extracted from `dbg_var`, + // to avoid having to pass it down in both places? + // NB: `var` doesn't seem to know about the column, so that's a limitation. + let dbg_loc = cx.create_debug_loc(scope_metadata, span); + unsafe { + // FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`. + llvm::LLVMRustDIBuilderInsertDeclareAtEnd( + DIB(cx), + variable_alloca, + dbg_var, + addr_ops.as_ptr(), + addr_ops.len() as c_uint, + dbg_loc, + self.llbb(), + ); + }*/ + } + + /*fn set_source_location(&mut self, scope: Self::DIScope, span: Span) { + unimplemented!(); + /*debug!("set_source_location: {}", self.sess().source_map().span_to_string(span)); + + let dbg_loc = self.cx().create_debug_loc(scope, span); + + unsafe { + llvm::LLVMSetCurrentDebugLocation(self.llbuilder, dbg_loc); + }*/ + }*/ + + fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) { + // TODO: replace with gcc_jit_context_new_global_with_initializer() if it's added: + // https://gcc.gnu.org/pipermail/jit/2020q3/001225.html + // + // Call the function to initialize global values here. + // We assume this is only called for the main function. + use std::iter; + + for crate_num in self.cx.tcx.crates(()).iter().copied().chain(iter::once(LOCAL_CRATE)) { + // FIXME: better way to find if a crate is of proc-macro type? + if crate_num == LOCAL_CRATE || self.cx.tcx.dep_kind(crate_num) != CrateDepKind::MacrosOnly { + // NOTE: proc-macro crates are not included in the executable, so don't call their + // initialization routine. + let initializer_name = format!("__gccGlobalCrateInit{}", self.cx.tcx.crate_name(crate_num)); + let codegen_init_func = self.context.new_function(None, FunctionType::Extern, self.context.new_type::<()>(), &[], + initializer_name, false); + self.llbb().add_eval(None, self.context.new_call(None, codegen_init_func, &[])); + } + } + + // TODO + //gdb::insert_reference_to_gdb_debug_scripts_section_global(self) + } + + fn set_var_name(&mut self, _value: RValue<'gcc>, _name: &str) { + unimplemented!(); + // Avoid wasting time if LLVM value names aren't even enabled. + /*if self.sess().fewer_names() { + return; + } + + // Only function parameters and instructions are local to a function, + // don't change the name of anything else (e.g. globals). + let param_or_inst = unsafe { + llvm::LLVMIsAArgument(value).is_some() || llvm::LLVMIsAInstruction(value).is_some() + }; + if !param_or_inst { + return; + } + + // Avoid replacing the name if it already exists. + // While we could combine the names somehow, it'd + // get noisy quick, and the usefulness is dubious. + if llvm::get_value_name(value).is_empty() { + llvm::set_value_name(value, name.as_bytes()); + }*/ + } + + fn set_dbg_loc(&mut self, _dbg_loc: Self::DILocation) { + unimplemented!(); + /*unsafe { + let dbg_loc_as_llval = llvm::LLVMRustMetadataAsValue(self.cx().llcx, dbg_loc); + llvm::LLVMSetCurrentDebugLocation(self.llbuilder, dbg_loc_as_llval); + }*/ + } +} + +impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> { + fn create_vtable_metadata(&self, _ty: Ty<'tcx>, _vtable: Self::Value) { + //metadata::create_vtable_metadata(self, ty, vtable) + } + + fn create_function_debug_context(&self, _instance: Instance<'tcx>, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _llfn: RValue<'gcc>, _mir: &mir::Body<'tcx>) -> Option> { + // TODO + None + } + + fn extend_scope_to_file(&self, _scope_metadata: Self::DIScope, _file: &SourceFile) -> Self::DIScope { + unimplemented!(); + } + + fn debuginfo_finalize(&self) { + //unimplemented!(); + } + + fn create_dbg_var(&self, _variable_name: Symbol, _variable_type: Ty<'tcx>, _scope_metadata: Self::DIScope, _variable_kind: VariableKind, _span: Span) -> Self::DIVariable { + unimplemented!(); + } + + fn dbg_scope_fn(&self, _instance: Instance<'tcx>, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _maybe_definition_llfn: Option>) -> Self::DIScope { + unimplemented!(); + /*let def_id = instance.def_id(); + let containing_scope = get_containing_scope(self, instance); + let span = self.tcx.def_span(def_id); + let loc = self.lookup_debug_loc(span.lo()); + let file_metadata = file_metadata(self, &loc.file); + + let function_type_metadata = unsafe { + let fn_signature = get_function_signature(self, fn_abi); + llvm::LLVMRustDIBuilderCreateSubroutineType(DIB(self), fn_signature) + }; + + // Find the enclosing function, in case this is a closure. + let def_key = self.tcx().def_key(def_id); + let mut name = def_key.disambiguated_data.data.to_string(); + + let enclosing_fn_def_id = self.tcx().closure_base_def_id(def_id); + + // Get_template_parameters() will append a `<...>` clause to the function + // name if necessary. + let generics = self.tcx().generics_of(enclosing_fn_def_id); + let substs = instance.substs.truncate_to(self.tcx(), generics); + let template_parameters = get_template_parameters(self, &generics, substs, &mut name); + + let linkage_name = &mangled_name_of_instance(self, instance).name; + // Omit the linkage_name if it is the same as subprogram name. + let linkage_name = if &name == linkage_name { "" } else { linkage_name }; + + // FIXME(eddyb) does this need to be separate from `loc.line` for some reason? + let scope_line = loc.line; + + let mut flags = DIFlags::FlagPrototyped; + + if fn_abi.ret.layout.abi.is_uninhabited() { + flags |= DIFlags::FlagNoReturn; + } + + let mut spflags = DISPFlags::SPFlagDefinition; + if is_node_local_to_unit(self, def_id) { + spflags |= DISPFlags::SPFlagLocalToUnit; + } + if self.sess().opts.optimize != config::OptLevel::No { + spflags |= DISPFlags::SPFlagOptimized; + } + if let Some((id, _)) = self.tcx.entry_fn(LOCAL_CRATE) { + if id.to_def_id() == def_id { + spflags |= DISPFlags::SPFlagMainSubprogram; + } + } + + unsafe { + return llvm::LLVMRustDIBuilderCreateFunction( + DIB(self), + containing_scope, + name.as_ptr().cast(), + name.len(), + linkage_name.as_ptr().cast(), + linkage_name.len(), + file_metadata, + loc.line.unwrap_or(UNKNOWN_LINE_NUMBER), + function_type_metadata, + scope_line.unwrap_or(UNKNOWN_LINE_NUMBER), + flags, + spflags, + maybe_definition_llfn, + template_parameters, + None, + ); + } + + fn get_function_signature<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + ) -> &'ll DIArray { + if cx.sess().opts.debuginfo == DebugInfo::Limited { + return create_DIArray(DIB(cx), &[]); + } + + let mut signature = Vec::with_capacity(fn_abi.args.len() + 1); + + // Return type -- llvm::DIBuilder wants this at index 0 + signature.push(if fn_abi.ret.is_ignore() { + None + } else { + Some(type_metadata(cx, fn_abi.ret.layout.ty, rustc_span::DUMMY_SP)) + }); + + // Arguments types + if cx.sess().target.options.is_like_msvc { + // FIXME(#42800): + // There is a bug in MSDIA that leads to a crash when it encounters + // a fixed-size array of `u8` or something zero-sized in a + // function-type (see #40477). + // As a workaround, we replace those fixed-size arrays with a + // pointer-type. So a function `fn foo(a: u8, b: [u8; 4])` would + // appear as `fn foo(a: u8, b: *const u8)` in debuginfo, + // and a function `fn bar(x: [(); 7])` as `fn bar(x: *const ())`. + // This transformed type is wrong, but these function types are + // already inaccurate due to ABI adjustments (see #42800). + signature.extend(fn_abi.args.iter().map(|arg| { + let t = arg.layout.ty; + let t = match t.kind() { + ty::Array(ct, _) + if (*ct == cx.tcx.types.u8) || cx.layout_of(ct).is_zst() => + { + cx.tcx.mk_imm_ptr(ct) + } + _ => t, + }; + Some(type_metadata(cx, t, rustc_span::DUMMY_SP)) + })); + } else { + signature.extend( + fn_abi + .args + .iter() + .map(|arg| Some(type_metadata(cx, arg.layout.ty, rustc_span::DUMMY_SP))), + ); + } + + create_DIArray(DIB(cx), &signature[..]) + } + + fn get_template_parameters<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + generics: &ty::Generics, + substs: SubstsRef<'tcx>, + name_to_append_suffix_to: &mut String, + ) -> &'ll DIArray { + if substs.types().next().is_none() { + return create_DIArray(DIB(cx), &[]); + } + + name_to_append_suffix_to.push('<'); + for (i, actual_type) in substs.types().enumerate() { + if i != 0 { + name_to_append_suffix_to.push(','); + } + + let actual_type = + cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), actual_type); + // Add actual type name to <...> clause of function name + let actual_type_name = compute_debuginfo_type_name(cx.tcx(), actual_type, true); + name_to_append_suffix_to.push_str(&actual_type_name[..]); + } + name_to_append_suffix_to.push('>'); + + // Again, only create type information if full debuginfo is enabled + let template_params: Vec<_> = if cx.sess().opts.debuginfo == DebugInfo::Full { + let names = get_parameter_names(cx, generics); + substs + .iter() + .zip(names) + .filter_map(|(kind, name)| { + if let GenericArgKind::Type(ty) = kind.unpack() { + let actual_type = + cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); + let actual_type_metadata = + type_metadata(cx, actual_type, rustc_span::DUMMY_SP); + let name = name.as_str(); + Some(unsafe { + Some(llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( + DIB(cx), + None, + name.as_ptr().cast(), + name.len(), + actual_type_metadata, + )) + }) + } else { + None + } + }) + .collect() + } else { + vec![] + }; + + create_DIArray(DIB(cx), &template_params[..]) + } + + fn get_parameter_names(cx: &CodegenCx<'_, '_>, generics: &ty::Generics) -> Vec { + let mut names = generics + .parent + .map_or(vec![], |def_id| get_parameter_names(cx, cx.tcx.generics_of(def_id))); + names.extend(generics.params.iter().map(|param| param.name)); + names + } + + fn get_containing_scope<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + instance: Instance<'tcx>, + ) -> &'ll DIScope { + // First, let's see if this is a method within an inherent impl. Because + // if yes, we want to make the result subroutine DIE a child of the + // subroutine's self-type. + let self_type = cx.tcx.impl_of_method(instance.def_id()).and_then(|impl_def_id| { + // If the method does *not* belong to a trait, proceed + if cx.tcx.trait_id_of_impl(impl_def_id).is_none() { + let impl_self_ty = cx.tcx.subst_and_normalize_erasing_regions( + instance.substs, + ty::ParamEnv::reveal_all(), + &cx.tcx.type_of(impl_def_id), + ); + + // Only "class" methods are generally understood by LLVM, + // so avoid methods on other types (e.g., `<*mut T>::null`). + match impl_self_ty.kind() { + ty::Adt(def, ..) if !def.is_box() => { + // Again, only create type information if full debuginfo is enabled + if cx.sess().opts.debuginfo == DebugInfo::Full + && !impl_self_ty.needs_subst() + { + Some(type_metadata(cx, impl_self_ty, rustc_span::DUMMY_SP)) + } else { + Some(namespace::item_namespace(cx, def.did)) + } + } + _ => None, + } + } else { + // For trait method impls we still use the "parallel namespace" + // strategy + None + } + }); + + self_type.unwrap_or_else(|| { + namespace::item_namespace( + cx, + DefId { + krate: instance.def_id().krate, + index: cx + .tcx + .def_key(instance.def_id()) + .parent + .expect("get_containing_scope: missing parent?"), + }, + ) + }) + }*/ + } + + fn dbg_loc(&self, _scope: Self::DIScope, _inlined_at: Option, _span: Span) -> Self::DILocation { + unimplemented!(); + /*let DebugLoc { line, col, .. } = self.lookup_debug_loc(span.lo()); + + unsafe { + llvm::LLVMRustDIBuilderCreateDebugLocation( + utils::debug_context(self).llcontext, + line.unwrap_or(UNKNOWN_LINE_NUMBER), + col.unwrap_or(UNKNOWN_COLUMN_NUMBER), + scope, + inlined_at, + ) + }*/ + } +} diff --git a/src/declare.rs b/src/declare.rs new file mode 100644 index 0000000000000..339a613c09661 --- /dev/null +++ b/src/declare.rs @@ -0,0 +1,220 @@ +use gccjit::{Function, FunctionType, GlobalKind, LValue, RValue, Type}; +use rustc_codegen_ssa::traits::BaseTypeMethods; +use rustc_middle::ty::Ty; +use rustc_span::Symbol; +use rustc_target::abi::call::FnAbi; + +use crate::abi::FnAbiGccExt; +use crate::context::{CodegenCx, unit_name}; +use crate::intrinsic::llvm; +use crate::mangled_std_symbols::{ARGV_INIT_ARRAY, ARGV_INIT_WRAPPER}; + +impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { + pub fn get_or_insert_global(&self, name: &str, ty: Type<'gcc>, is_tls: bool, link_section: Option) -> RValue<'gcc> { + if self.globals.borrow().contains_key(name) { + let typ = self.globals.borrow().get(name).expect("global").get_type(); + let global = self.context.new_global(None, GlobalKind::Imported, typ, name); + if is_tls { + global.set_tls_model(self.tls_model); + } + if let Some(link_section) = link_section { + global.set_link_section(&link_section.as_str()); + } + global.get_address(None) + } + else { + self.declare_global(name, ty, is_tls, link_section) + } + } + + pub fn declare_unnamed_global(&self, ty: Type<'gcc>) -> LValue<'gcc> { + let index = self.global_gen_sym_counter.get(); + self.global_gen_sym_counter.set(index + 1); + let name = format!("global_{}_{}", index, unit_name(&self.codegen_unit)); + self.context.new_global(None, GlobalKind::Exported, ty, &name) + } + + pub fn declare_global_with_linkage(&self, name: &str, ty: Type<'gcc>, linkage: GlobalKind) -> RValue<'gcc> { + //debug!("declare_global_with_linkage(name={:?})", name); + let global = self.context.new_global(None, linkage, ty, name) + .get_address(None); + self.globals.borrow_mut().insert(name.to_string(), global); + // NOTE: global seems to only be global in a module. So save the name instead of the value + // to import it later. + self.global_names.borrow_mut().insert(global, name.to_string()); + global + } + + pub fn declare_func(&self, name: &str, return_type: Type<'gcc>, params: &[Type<'gcc>], variadic: bool) -> RValue<'gcc> { + self.linkage.set(FunctionType::Exported); + let func = declare_raw_fn(self, name, () /*llvm::CCallConv*/, return_type, params, variadic); + // FIXME: this is a wrong cast. That requires changing the compiler API. + unsafe { std::mem::transmute(func) } + } + + pub fn declare_global(&self, name: &str, ty: Type<'gcc>, is_tls: bool, link_section: Option) -> RValue<'gcc> { + //debug!("declare_global(name={:?})", name); + // FIXME: correctly support global variable initialization. + if name.starts_with(ARGV_INIT_ARRAY) { + // NOTE: hack to avoid having to update the names in mangled_std_symbols: we save the + // name of the variable now to actually declare it later. + *self.init_argv_var.borrow_mut() = name.to_string(); + + let global = self.context.new_global(None, GlobalKind::Imported, ty, name); + if let Some(link_section) = link_section { + global.set_link_section(&link_section.as_str()); + } + return global.get_address(None); + } + let global = self.context.new_global(None, GlobalKind::Exported, ty, name); + if is_tls { + global.set_tls_model(self.tls_model); + } + if let Some(link_section) = link_section { + global.set_link_section(&link_section.as_str()); + } + let global = global.get_address(None); + self.globals.borrow_mut().insert(name.to_string(), global); + // NOTE: global seems to only be global in a module. So save the name instead of the value + // to import it later. + self.global_names.borrow_mut().insert(global, name.to_string()); + global + } + + pub fn declare_cfn(&self, name: &str, _fn_type: Type<'gcc>) -> RValue<'gcc> { + // TODO: use the fn_type parameter. + let const_string = self.context.new_type::().make_pointer().make_pointer(); + let return_type = self.type_i32(); + let variadic = false; + self.linkage.set(FunctionType::Exported); + let func = declare_raw_fn(self, name, () /*llvm::CCallConv*/, return_type, &[self.type_i32(), const_string], variadic); + // NOTE: it is needed to set the current_func here as well, because get_fn() is not called + // for the main function. + *self.current_func.borrow_mut() = Some(func); + // FIXME: this is a wrong cast. That requires changing the compiler API. + unsafe { std::mem::transmute(func) } + } + + pub fn declare_fn(&self, name: &str, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> RValue<'gcc> { + // NOTE: hack to avoid having to update the names in mangled_std_symbols: we found the name + // of the variable earlier, so we declare it now. + // Since we don't correctly support initializers yet, we initialize this variable manually + // for now. + if name.starts_with(ARGV_INIT_WRAPPER) && !self.argv_initialized.get() { + let global_name = &*self.init_argv_var.borrow(); + let return_type = self.type_void(); + let params = [ + self.context.new_parameter(None, self.int_type, "argc"), + self.context.new_parameter(None, self.u8_type.make_pointer().make_pointer(), "argv"), + self.context.new_parameter(None, self.u8_type.make_pointer().make_pointer(), "envp"), + ]; + let function = self.context.new_function(None, FunctionType::Extern, return_type, ¶ms, name, false); + let initializer = function.get_address(None); + + let param_types = [ + self.int_type, + self.u8_type.make_pointer().make_pointer(), + self.u8_type.make_pointer().make_pointer(), + ]; + let ty = self.context.new_function_pointer_type(None, return_type, ¶m_types, false); + + let global = self.context.new_global(None, GlobalKind::Exported, ty, global_name); + global.set_link_section(".init_array.00099"); + global.global_set_initializer_value(initializer); + let global = global.get_address(None); + self.globals.borrow_mut().insert(global_name.to_string(), global); + // NOTE: global seems to only be global in a module. So save the name instead of the value + // to import it later. + self.global_names.borrow_mut().insert(global, global_name.to_string()); + self.argv_initialized.set(true); + } + //debug!("declare_rust_fn(name={:?}, fn_abi={:?})", name, fn_abi); + let (return_type, params, variadic) = fn_abi.gcc_type(self); + let func = declare_raw_fn(self, name, () /*fn_abi.llvm_cconv()*/, return_type, ¶ms, variadic); + //fn_abi.apply_attrs_llfn(self, func); + // FIXME: this is a wrong cast. That requires changing the compiler API. + unsafe { std::mem::transmute(func) } + } + + pub fn define_global(&self, name: &str, ty: Type<'gcc>, is_tls: bool, link_section: Option) -> Option> { + Some(self.get_or_insert_global(name, ty, is_tls, link_section)) + } + + pub fn define_private_global(&self, ty: Type<'gcc>) -> RValue<'gcc> { + let global = self.declare_unnamed_global(ty); + global.get_address(None) + } + + pub fn get_declared_value(&self, name: &str) -> Option> { + //debug!("get_declared_value(name={:?})", name); + // TODO: use a different field than globals, because this seems to return a function? + self.globals.borrow().get(name).cloned() + } + + /*fn get_defined_value(&self, name: &str) -> Option> { + // TODO: gcc does not allow global initialization. + None + /*self.get_declared_value(name).and_then(|val| { + let declaration = unsafe { llvm::LLVMIsDeclaration(val) != 0 }; + if !declaration { Some(val) } else { None } + })*/ + }*/ +} + +/// Declare a function. +/// +/// If there’s a value with the same name already declared, the function will +/// update the declaration and return existing Value instead. +fn declare_raw_fn<'gcc>(cx: &CodegenCx<'gcc, '_>, name: &str, _callconv: () /*llvm::CallConv*/, return_type: Type<'gcc>, param_types: &[Type<'gcc>], variadic: bool) -> Function<'gcc> { + //debug!("declare_raw_fn(name={:?}, ty={:?})", name, ty); + /*let llfn = unsafe { + llvm::LLVMRustGetOrInsertFunction(cx.llmod, name.as_ptr().cast(), name.len(), ty) + };*/ + + if name.starts_with("llvm.") { + return llvm::intrinsic(name, cx); + } + let func = + if cx.functions.borrow().contains_key(name) { + *cx.functions.borrow().get(name).expect("function") + } + else { + let params: Vec<_> = param_types.into_iter().enumerate() + .map(|(index, param)| cx.context.new_parameter(None, *param, &format!("param{}", index))) // TODO: set name. + .collect(); + let func = cx.context.new_function(None, cx.linkage.get(), return_type, ¶ms, mangle_name(name), variadic); + cx.functions.borrow_mut().insert(name.to_string(), func); + func + }; + + //llvm::SetFunctionCallConv(llfn, callconv); // TODO + // Function addresses in Rust are never significant, allowing functions to + // be merged. + //llvm::SetUnnamedAddress(llfn, llvm::UnnamedAddr::Global); // TODO + + /*if cx.tcx.sess.opts.cg.no_redzone.unwrap_or(cx.tcx.sess.target.target.options.disable_redzone) { + llvm::Attribute::NoRedZone.apply_llfn(Function, llfn); + }*/ + + //attributes::default_optimisation_attrs(cx.tcx.sess, llfn); + //attributes::non_lazy_bind(cx.sess(), llfn); + + // FIXME: invalid cast. + // TODO: is this line useful? + //cx.globals.borrow_mut().insert(name.to_string(), unsafe { std::mem::transmute(func) }); + func +} + +// FIXME: this is a hack because libgccjit currently only supports alpha, num and _. +// Unsupported characters: `$` and `.`. +pub fn mangle_name(name: &str) -> String { + name.replace(|char: char| { + if !char.is_alphanumeric() && char != '_' { + debug_assert!("$.".contains(char), "Unsupported char in function name: {}", char); + true + } + else { + false + } + }, "_") +} diff --git a/src/intrinsic/llvm.rs b/src/intrinsic/llvm.rs new file mode 100644 index 0000000000000..bf9472d3ea942 --- /dev/null +++ b/src/intrinsic/llvm.rs @@ -0,0 +1,26 @@ +use gccjit::Function; + +use crate::context::CodegenCx; + +pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function<'gcc> { + let _gcc_name = + match name { + "llvm.x86.xgetbv" => { + let gcc_name = "__builtin_trap"; + let func = cx.context.get_builtin_function(gcc_name); + cx.functions.borrow_mut().insert(gcc_name.to_string(), func); + return func; + }, + // TODO: this doc specifies the equivalent GCC builtins: http://huonw.github.io/llvmint/llvmint/x86/index.html + "llvm.x86.sse2.cmp.pd" => "__builtin_ia32_cmppd", + "llvm.x86.sse2.movmsk.pd" => "__builtin_ia32_movmskpd", + "llvm.x86.sse2.pmovmskb.128" => "__builtin_ia32_pmovmskb128", + _ => unimplemented!("unsupported LLVM intrinsic {}", name) + }; + + println!("Get target builtin"); + unimplemented!(); + /*let func = cx.context.get_target_builtin_function(gcc_name); + cx.functions.borrow_mut().insert(gcc_name.to_string(), func); + func*/ +} diff --git a/src/intrinsic/mod.rs b/src/intrinsic/mod.rs new file mode 100644 index 0000000000000..083f7e01c80d0 --- /dev/null +++ b/src/intrinsic/mod.rs @@ -0,0 +1,1286 @@ +pub mod llvm; +mod simd; + +use gccjit::{ComparisonOp, Function, RValue, ToRValue, Type, UnaryOp}; +use rustc_codegen_ssa::MemFlags; +use rustc_codegen_ssa::base::wants_msvc_seh; +use rustc_codegen_ssa::common::{IntPredicate, span_invalid_monomorphization_error}; +use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; +use rustc_codegen_ssa::mir::place::PlaceRef; +use rustc_codegen_ssa::traits::{ArgAbiMethods, BaseTypeMethods, BuilderMethods, ConstMethods, IntrinsicCallMethods}; +use rustc_middle::bug; +use rustc_middle::ty::{self, Instance, Ty}; +use rustc_span::{Span, Symbol, symbol::kw, sym}; +use rustc_target::abi::{HasDataLayout, LayoutOf}; +use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode}; +use rustc_target::spec::PanicStrategy; + +use crate::abi::GccType; +use crate::builder::Builder; +use crate::common::TypeReflection; +use crate::context::CodegenCx; +use crate::type_of::LayoutGccExt; +use crate::intrinsic::simd::generic_simd_intrinsic; + +fn get_simple_intrinsic<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, name: Symbol) -> Option> { + let gcc_name = match name { + sym::sqrtf32 => "sqrtf", + sym::sqrtf64 => "sqrt", + sym::powif32 => "__builtin_powif", + sym::powif64 => "__builtin_powi", + sym::sinf32 => "sinf", + sym::sinf64 => "sin", + sym::cosf32 => "cosf", + sym::cosf64 => "cos", + sym::powf32 => "powf", + sym::powf64 => "pow", + sym::expf32 => "expf", + sym::expf64 => "exp", + sym::exp2f32 => "exp2f", + sym::exp2f64 => "exp2", + sym::logf32 => "logf", + sym::logf64 => "log", + sym::log10f32 => "log10f", + sym::log10f64 => "log10", + sym::log2f32 => "log2f", + sym::log2f64 => "log2", + sym::fmaf32 => "fmaf", + sym::fmaf64 => "fma", + sym::fabsf32 => "fabsf", + sym::fabsf64 => "fabs", + sym::minnumf32 => "fminf", + sym::minnumf64 => "fmin", + sym::maxnumf32 => "fmaxf", + sym::maxnumf64 => "fmax", + sym::copysignf32 => "copysignf", + sym::copysignf64 => "copysign", + sym::floorf32 => "floorf", + sym::floorf64 => "floor", + sym::ceilf32 => "ceilf", + sym::ceilf64 => "ceil", + sym::truncf32 => "truncf", + sym::truncf64 => "trunc", + sym::rintf32 => "rintf", + sym::rintf64 => "rint", + sym::nearbyintf32 => "nearbyintf", + sym::nearbyintf64 => "nearbyint", + sym::roundf32 => "roundf", + sym::roundf64 => "round", + sym::abort => "abort", + _ => return None, + }; + Some(cx.context.get_builtin_function(&gcc_name)) +} + +impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { + fn codegen_intrinsic_call(&mut self, instance: Instance<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OperandRef<'tcx, RValue<'gcc>>], llresult: RValue<'gcc>, span: Span) { + let tcx = self.tcx; + let callee_ty = instance.ty(tcx, ty::ParamEnv::reveal_all()); + + let (def_id, substs) = match *callee_ty.kind() { + ty::FnDef(def_id, substs) => (def_id, substs), + _ => bug!("expected fn item type, found {}", callee_ty), + }; + + let sig = callee_ty.fn_sig(tcx); + let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), sig); + let arg_tys = sig.inputs(); + let ret_ty = sig.output(); + let name = tcx.item_name(def_id); + let name_str = &*name.as_str(); + + let llret_ty = self.layout_of(ret_ty).gcc_type(self, true); + let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout); + + let simple = get_simple_intrinsic(self, name); + let llval = + match name { + _ if simple.is_some() => { + // FIXME: remove this cast when the API supports function. + let func = unsafe { std::mem::transmute(simple.expect("simple")) }; + self.call(func, &args.iter().map(|arg| arg.immediate()).collect::>(), None) + }, + sym::likely => { + self.expect(args[0].immediate(), true) + } + sym::unlikely => { + self.expect(args[0].immediate(), false) + } + kw::Try => { + try_intrinsic( + self, + args[0].immediate(), + args[1].immediate(), + args[2].immediate(), + llresult, + ); + return; + } + sym::breakpoint => { + unimplemented!(); + /*let llfn = self.get_intrinsic(&("llvm.debugtrap")); + self.call(llfn, &[], None)*/ + } + sym::va_copy => { + unimplemented!(); + /*let intrinsic = self.cx().get_intrinsic(&("llvm.va_copy")); + self.call(intrinsic, &[args[0].immediate(), args[1].immediate()], None)*/ + } + sym::va_arg => { + unimplemented!(); + /*match fn_abi.ret.layout.abi { + abi::Abi::Scalar(ref scalar) => { + match scalar.value { + Primitive::Int(..) => { + if self.cx().size_of(ret_ty).bytes() < 4 { + // `va_arg` should not be called on a integer type + // less than 4 bytes in length. If it is, promote + // the integer to a `i32` and truncate the result + // back to the smaller type. + let promoted_result = emit_va_arg(self, args[0], tcx.types.i32); + self.trunc(promoted_result, llret_ty) + } else { + emit_va_arg(self, args[0], ret_ty) + } + } + Primitive::F64 | Primitive::Pointer => { + emit_va_arg(self, args[0], ret_ty) + } + // `va_arg` should never be used with the return type f32. + Primitive::F32 => bug!("the va_arg intrinsic does not work with `f32`"), + } + } + _ => bug!("the va_arg intrinsic does not work with non-scalar types"), + }*/ + } + + sym::volatile_load | sym::unaligned_volatile_load => { + let tp_ty = substs.type_at(0); + let mut ptr = args[0].immediate(); + if let PassMode::Cast(ty) = fn_abi.ret.mode { + ptr = self.pointercast(ptr, self.type_ptr_to(ty.gcc_type(self))); + } + let load = self.volatile_load(ptr.get_type(), ptr); + // TODO + /*let align = if name == sym::unaligned_volatile_load { + 1 + } else { + self.align_of(tp_ty).bytes() as u32 + }; + unsafe { + llvm::LLVMSetAlignment(load, align); + }*/ + self.to_immediate(load, self.layout_of(tp_ty)) + } + sym::volatile_store => { + let dst = args[0].deref(self.cx()); + args[1].val.volatile_store(self, dst); + return; + } + sym::unaligned_volatile_store => { + let dst = args[0].deref(self.cx()); + args[1].val.unaligned_volatile_store(self, dst); + return; + } + sym::prefetch_read_data + | sym::prefetch_write_data + | sym::prefetch_read_instruction + | sym::prefetch_write_instruction => { + unimplemented!(); + /*let expect = self.get_intrinsic(&("llvm.prefetch")); + let (rw, cache_type) = match name { + sym::prefetch_read_data => (0, 1), + sym::prefetch_write_data => (1, 1), + sym::prefetch_read_instruction => (0, 0), + sym::prefetch_write_instruction => (1, 0), + _ => bug!(), + }; + self.call( + expect, + &[ + args[0].immediate(), + self.const_i32(rw), + args[1].immediate(), + self.const_i32(cache_type), + ], + None, + )*/ + } + sym::ctlz + | sym::ctlz_nonzero + | sym::cttz + | sym::cttz_nonzero + | sym::ctpop + | sym::bswap + | sym::bitreverse + | sym::rotate_left + | sym::rotate_right + | sym::saturating_add + | sym::saturating_sub => { + let ty = arg_tys[0]; + match int_type_width_signed(ty, self) { + Some((width, signed)) => match name { + sym::ctlz | sym::cttz => { + let func = self.current_func.borrow().expect("func"); + let then_block = func.new_block("then"); + let else_block = func.new_block("else"); + let after_block = func.new_block("after"); + + let arg = args[0].immediate(); + let result = func.new_local(None, arg.get_type(), "zeros"); + let zero = self.cx.context.new_rvalue_zero(arg.get_type()); + let cond = self.cx.context.new_comparison(None, ComparisonOp::Equals, arg, zero); + self.block.expect("block").end_with_conditional(None, cond, then_block, else_block); + + let zero_result = self.cx.context.new_rvalue_from_long(arg.get_type(), width as i64); + then_block.add_assignment(None, result, zero_result); + then_block.end_with_jump(None, after_block); + + // NOTE: since jumps were added in a place + // count_leading_zeroes() does not expect, the current blocks + // in the state need to be updated. + *self.current_block.borrow_mut() = Some(else_block); + self.block = Some(else_block); + + let zeros = + match name { + sym::ctlz => self.count_leading_zeroes(width, arg), + sym::cttz => self.count_trailing_zeroes(width, arg), + _ => unreachable!(), + }; + else_block.add_assignment(None, result, zeros); + else_block.end_with_jump(None, after_block); + + // NOTE: since jumps were added in a place rustc does not + // expect, the current blocks in the state need to be updated. + *self.current_block.borrow_mut() = Some(after_block); + self.block = Some(after_block); + + result.to_rvalue() + + /*let y = self.const_bool(false); + let llfn = self.get_intrinsic(&format!("llvm.{}.i{}", name, width)); + self.call(llfn, &[args[0].immediate(), y], None)*/ + } + sym::ctlz_nonzero => { + self.count_leading_zeroes(width, args[0].immediate()) + }, + sym::cttz_nonzero => { + self.count_trailing_zeroes(width, args[0].immediate()) + } + sym::ctpop => self.pop_count(args[0].immediate()), + sym::bswap => { + if width == 8 { + args[0].immediate() // byte swap a u8/i8 is just a no-op + } + else { + // TODO: check if it's faster to use string literals and a + // match instead of format!. + let bswap = self.cx.context.get_builtin_function(&format!("__builtin_bswap{}", width)); + let mut arg = args[0].immediate(); + // FIXME: this cast should not be necessary. Remove + // when having proper sized integer types. + let param_type = bswap.get_param(0).to_rvalue().get_type(); + if param_type != arg.get_type() { + arg = self.bitcast(arg, param_type); + } + self.cx.context.new_call(None, bswap, &[arg]) + } + }, + sym::bitreverse => self.bit_reverse(width, args[0].immediate()), + sym::rotate_left | sym::rotate_right => { + // TODO: implement using algorithm from: + // https://blog.regehr.org/archives/1063 + // for other platforms. + let is_left = name == sym::rotate_left; + let val = args[0].immediate(); + let raw_shift = args[1].immediate(); + if is_left { + self.rotate_left(val, raw_shift, width) + } + else { + self.rotate_right(val, raw_shift, width) + } + }, + sym::saturating_add => { + self.saturating_add(args[0].immediate(), args[1].immediate(), signed, width) + }, + sym::saturating_sub => { + self.saturating_sub(args[0].immediate(), args[1].immediate(), signed, width) + }, + _ => bug!(), + }, + None => { + span_invalid_monomorphization_error( + tcx.sess, + span, + &format!( + "invalid monomorphization of `{}` intrinsic: \ + expected basic integer type, found `{}`", + name, ty + ), + ); + return; + } + } + } + + sym::raw_eq => { + use rustc_target::abi::Abi::*; + let tp_ty = substs.type_at(0); + let layout = self.layout_of(tp_ty).layout; + let use_integer_compare = match layout.abi { + Scalar(_) | ScalarPair(_, _) => true, + Uninhabited | Vector { .. } => false, + Aggregate { .. } => { + // For rusty ABIs, small aggregates are actually passed + // as `RegKind::Integer` (see `FnAbi::adjust_for_abi`), + // so we re-use that same threshold here. + layout.size <= self.data_layout().pointer_size * 2 + } + }; + + let a = args[0].immediate(); + let b = args[1].immediate(); + if layout.size.bytes() == 0 { + self.const_bool(true) + } + /*else if use_integer_compare { + let integer_ty = self.type_ix(layout.size.bits()); // FIXME: LLVM creates an integer of 96 bits for [i32; 3], but gcc doesn't support this, so it creates an integer of 128 bits. + let ptr_ty = self.type_ptr_to(integer_ty); + let a_ptr = self.bitcast(a, ptr_ty); + let a_val = self.load(integer_ty, a_ptr, layout.align.abi); + let b_ptr = self.bitcast(b, ptr_ty); + let b_val = self.load(integer_ty, b_ptr, layout.align.abi); + self.icmp(IntPredicate::IntEQ, a_val, b_val) + }*/ + else { + let void_ptr_type = self.context.new_type::<*const ()>(); + let a_ptr = self.bitcast(a, void_ptr_type); + let b_ptr = self.bitcast(b, void_ptr_type); + let n = self.context.new_cast(None, self.const_usize(layout.size.bytes()), self.sizet_type); + let builtin = self.context.get_builtin_function("memcmp"); + let cmp = self.context.new_call(None, builtin, &[a_ptr, b_ptr, n]); + self.icmp(IntPredicate::IntEQ, cmp, self.const_i32(0)) + } + } + + _ if name_str.starts_with("simd_") => { + match generic_simd_intrinsic(self, name, callee_ty, args, ret_ty, llret_ty, span) { + Ok(llval) => llval, + Err(()) => return, + } + } + + _ => bug!("unknown intrinsic '{}'", name), + }; + + if !fn_abi.ret.is_ignore() { + if let PassMode::Cast(ty) = fn_abi.ret.mode { + let ptr_llty = self.type_ptr_to(ty.gcc_type(self)); + let ptr = self.pointercast(result.llval, ptr_llty); + self.store(llval, ptr, result.align); + } + else { + OperandRef::from_immediate_or_packed_pair(self, llval, result.layout) + .val + .store(self, result); + } + } + } + + fn abort(&mut self) { + let func = self.context.get_builtin_function("abort"); + let func: RValue<'gcc> = unsafe { std::mem::transmute(func) }; + self.call(func, &[], None); + } + + fn assume(&mut self, value: Self::Value) { + // TODO: switch to asumme when it exists. + // Or use something like this: + // #define __assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0) + self.expect(value, true); + } + + fn expect(&mut self, cond: Self::Value, _expected: bool) -> Self::Value { + // TODO + /*let expect = self.context.get_builtin_function("__builtin_expect"); + let expect: RValue<'gcc> = unsafe { std::mem::transmute(expect) }; + self.call(expect, &[cond, self.const_bool(expected)], None)*/ + cond + } + + fn sideeffect(&mut self) { + // TODO + /*if self.tcx().sess.opts.debugging_opts.insert_sideeffect { + let fnname = self.get_intrinsic(&("llvm.sideeffect")); + self.call(fnname, &[], None); + }*/ + } + + fn va_start(&mut self, _va_list: RValue<'gcc>) -> RValue<'gcc> { + unimplemented!(); + /*let intrinsic = self.cx().get_intrinsic("llvm.va_start"); + self.call(intrinsic, &[va_list], None)*/ + } + + fn va_end(&mut self, _va_list: RValue<'gcc>) -> RValue<'gcc> { + unimplemented!(); + /*let intrinsic = self.cx().get_intrinsic("llvm.va_end"); + self.call(intrinsic, &[va_list], None)*/ + } +} + +impl<'a, 'gcc, 'tcx> ArgAbiMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { + fn store_fn_arg(&mut self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, idx: &mut usize, dst: PlaceRef<'tcx, Self::Value>) { + arg_abi.store_fn_arg(self, idx, dst) + } + + fn store_arg(&mut self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>, val: RValue<'gcc>, dst: PlaceRef<'tcx, RValue<'gcc>>) { + arg_abi.store(self, val, dst) + } + + fn arg_memory_ty(&self, arg_abi: &ArgAbi<'tcx, Ty<'tcx>>) -> Type<'gcc> { + arg_abi.memory_ty(self) + } +} + +pub trait ArgAbiExt<'gcc, 'tcx> { + fn memory_ty(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>; + fn store(&self, bx: &mut Builder<'_, 'gcc, 'tcx>, val: RValue<'gcc>, dst: PlaceRef<'tcx, RValue<'gcc>>); + fn store_fn_arg(&self, bx: &mut Builder<'_, 'gcc, 'tcx>, idx: &mut usize, dst: PlaceRef<'tcx, RValue<'gcc>>); +} + +impl<'gcc, 'tcx> ArgAbiExt<'gcc, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> { + /// Gets the LLVM type for a place of the original Rust type of + /// this argument/return, i.e., the result of `type_of::type_of`. + fn memory_ty(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> { + self.layout.gcc_type(cx, true) + } + + /// Stores a direct/indirect value described by this ArgAbi into a + /// place for the original Rust type of this argument/return. + /// Can be used for both storing formal arguments into Rust variables + /// or results of call/invoke instructions into their destinations. + fn store(&self, bx: &mut Builder<'_, 'gcc, 'tcx>, val: RValue<'gcc>, dst: PlaceRef<'tcx, RValue<'gcc>>) { + if self.is_ignore() { + return; + } + if self.is_sized_indirect() { + OperandValue::Ref(val, None, self.layout.align.abi).store(bx, dst) + } + else if self.is_unsized_indirect() { + bug!("unsized `ArgAbi` must be handled through `store_fn_arg`"); + } + else if let PassMode::Cast(cast) = self.mode { + // FIXME(eddyb): Figure out when the simpler Store is safe, clang + // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}. + let can_store_through_cast_ptr = false; + if can_store_through_cast_ptr { + let cast_ptr_llty = bx.type_ptr_to(cast.gcc_type(bx)); + let cast_dst = bx.pointercast(dst.llval, cast_ptr_llty); + bx.store(val, cast_dst, self.layout.align.abi); + } + else { + // The actual return type is a struct, but the ABI + // adaptation code has cast it into some scalar type. The + // code that follows is the only reliable way I have + // found to do a transform like i64 -> {i32,i32}. + // Basically we dump the data onto the stack then memcpy it. + // + // Other approaches I tried: + // - Casting rust ret pointer to the foreign type and using Store + // is (a) unsafe if size of foreign type > size of rust type and + // (b) runs afoul of strict aliasing rules, yielding invalid + // assembly under -O (specifically, the store gets removed). + // - Truncating foreign type to correct integral type and then + // bitcasting to the struct type yields invalid cast errors. + + // We instead thus allocate some scratch space... + let scratch_size = cast.size(bx); + let scratch_align = cast.align(bx); + let llscratch = bx.alloca(cast.gcc_type(bx), scratch_align); + bx.lifetime_start(llscratch, scratch_size); + + // ... where we first store the value... + bx.store(val, llscratch, scratch_align); + + // ... and then memcpy it to the intended destination. + bx.memcpy( + dst.llval, + self.layout.align.abi, + llscratch, + scratch_align, + bx.const_usize(self.layout.size.bytes()), + MemFlags::empty(), + ); + + bx.lifetime_end(llscratch, scratch_size); + } + } + else { + OperandValue::Immediate(val).store(bx, dst); + } + } + + fn store_fn_arg<'a>(&self, bx: &mut Builder<'a, 'gcc, 'tcx>, idx: &mut usize, dst: PlaceRef<'tcx, RValue<'gcc>>) { + let mut next = || { + let val = bx.current_func().get_param(*idx as i32); + *idx += 1; + val.to_rvalue() + }; + match self.mode { + PassMode::Ignore => {} + PassMode::Pair(..) => { + OperandValue::Pair(next(), next()).store(bx, dst); + } + PassMode::Indirect { extra_attrs: Some(_), .. } => { + OperandValue::Ref(next(), Some(next()), self.layout.align.abi).store(bx, dst); + } + PassMode::Direct(_) | PassMode::Indirect { extra_attrs: None, .. } | PassMode::Cast(_) => { + let next_arg = next(); + self.store(bx, next_arg.to_rvalue(), dst); + } + } + } +} + +fn int_type_width_signed<'gcc, 'tcx>(ty: Ty<'tcx>, cx: &CodegenCx<'gcc, 'tcx>) -> Option<(u64, bool)> { + match ty.kind() { + ty::Int(t) => Some(( + match t { + rustc_middle::ty::IntTy::Isize => u64::from(cx.tcx.sess.target.pointer_width), + rustc_middle::ty::IntTy::I8 => 8, + rustc_middle::ty::IntTy::I16 => 16, + rustc_middle::ty::IntTy::I32 => 32, + rustc_middle::ty::IntTy::I64 => 64, + rustc_middle::ty::IntTy::I128 => 128, + }, + true, + )), + ty::Uint(t) => Some(( + match t { + rustc_middle::ty::UintTy::Usize => u64::from(cx.tcx.sess.target.pointer_width), + rustc_middle::ty::UintTy::U8 => 8, + rustc_middle::ty::UintTy::U16 => 16, + rustc_middle::ty::UintTy::U32 => 32, + rustc_middle::ty::UintTy::U64 => 64, + rustc_middle::ty::UintTy::U128 => 128, + }, + false, + )), + _ => None, + } +} + +impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { + fn bit_reverse(&mut self, width: u64, value: RValue<'gcc>) -> RValue<'gcc> { + let typ = value.get_type(); + let context = &self.cx.context; + match width { + 8 => { + // First step. + let left = self.and(value, context.new_rvalue_from_int(typ, 0xF0)); + let left = self.lshr(left, context.new_rvalue_from_int(typ, 4)); + let right = self.and(value, context.new_rvalue_from_int(typ, 0x0F)); + let right = self.shl(right, context.new_rvalue_from_int(typ, 4)); + let step1 = self.or(left, right); + + // Second step. + let left = self.and(step1, context.new_rvalue_from_int(typ, 0xCC)); + let left = self.lshr(left, context.new_rvalue_from_int(typ, 2)); + let right = self.and(step1, context.new_rvalue_from_int(typ, 0x33)); + let right = self.shl(right, context.new_rvalue_from_int(typ, 2)); + let step2 = self.or(left, right); + + // Third step. + let left = self.and(step2, context.new_rvalue_from_int(typ, 0xAA)); + let left = self.lshr(left, context.new_rvalue_from_int(typ, 1)); + let right = self.and(step2, context.new_rvalue_from_int(typ, 0x55)); + let right = self.shl(right, context.new_rvalue_from_int(typ, 1)); + let step3 = self.or(left, right); + + step3 + }, + 16 => { + // First step. + let left = self.and(value, context.new_rvalue_from_int(typ, 0x5555)); + let left = self.shl(left, context.new_rvalue_from_int(typ, 1)); + let right = self.and(value, context.new_rvalue_from_int(typ, 0xAAAA)); + let right = self.lshr(right, context.new_rvalue_from_int(typ, 1)); + let step1 = self.or(left, right); + + // Second step. + let left = self.and(step1, context.new_rvalue_from_int(typ, 0x3333)); + let left = self.shl(left, context.new_rvalue_from_int(typ, 2)); + let right = self.and(step1, context.new_rvalue_from_int(typ, 0xCCCC)); + let right = self.lshr(right, context.new_rvalue_from_int(typ, 2)); + let step2 = self.or(left, right); + + // Third step. + let left = self.and(step2, context.new_rvalue_from_int(typ, 0x0F0F)); + let left = self.shl(left, context.new_rvalue_from_int(typ, 4)); + let right = self.and(step2, context.new_rvalue_from_int(typ, 0xF0F0)); + let right = self.lshr(right, context.new_rvalue_from_int(typ, 4)); + let step3 = self.or(left, right); + + // Fourth step. + let left = self.and(step3, context.new_rvalue_from_int(typ, 0x00FF)); + let left = self.shl(left, context.new_rvalue_from_int(typ, 8)); + let right = self.and(step3, context.new_rvalue_from_int(typ, 0xFF00)); + let right = self.lshr(right, context.new_rvalue_from_int(typ, 8)); + let step4 = self.or(left, right); + + step4 + }, + 32 => { + // TODO: Refactor with other implementations. + // First step. + let left = self.and(value, context.new_rvalue_from_long(typ, 0x55555555)); + let left = self.shl(left, context.new_rvalue_from_long(typ, 1)); + let right = self.and(value, context.new_rvalue_from_long(typ, 0xAAAAAAAA)); + let right = self.lshr(right, context.new_rvalue_from_long(typ, 1)); + let step1 = self.or(left, right); + + // Second step. + let left = self.and(step1, context.new_rvalue_from_long(typ, 0x33333333)); + let left = self.shl(left, context.new_rvalue_from_long(typ, 2)); + let right = self.and(step1, context.new_rvalue_from_long(typ, 0xCCCCCCCC)); + let right = self.lshr(right, context.new_rvalue_from_long(typ, 2)); + let step2 = self.or(left, right); + + // Third step. + let left = self.and(step2, context.new_rvalue_from_long(typ, 0x0F0F0F0F)); + let left = self.shl(left, context.new_rvalue_from_long(typ, 4)); + let right = self.and(step2, context.new_rvalue_from_long(typ, 0xF0F0F0F0)); + let right = self.lshr(right, context.new_rvalue_from_long(typ, 4)); + let step3 = self.or(left, right); + + // Fourth step. + let left = self.and(step3, context.new_rvalue_from_long(typ, 0x00FF00FF)); + let left = self.shl(left, context.new_rvalue_from_long(typ, 8)); + let right = self.and(step3, context.new_rvalue_from_long(typ, 0xFF00FF00)); + let right = self.lshr(right, context.new_rvalue_from_long(typ, 8)); + let step4 = self.or(left, right); + + // Fifth step. + let left = self.and(step4, context.new_rvalue_from_long(typ, 0x0000FFFF)); + let left = self.shl(left, context.new_rvalue_from_long(typ, 16)); + let right = self.and(step4, context.new_rvalue_from_long(typ, 0xFFFF0000)); + let right = self.lshr(right, context.new_rvalue_from_long(typ, 16)); + let step5 = self.or(left, right); + + step5 + }, + 64 => { + // First step. + let left = self.shl(value, context.new_rvalue_from_long(typ, 32)); + let right = self.lshr(value, context.new_rvalue_from_long(typ, 32)); + let step1 = self.or(left, right); + + // Second step. + let left = self.and(step1, context.new_rvalue_from_long(typ, 0x0001FFFF0001FFFF)); + let left = self.shl(left, context.new_rvalue_from_long(typ, 15)); + let right = self.and(step1, context.new_rvalue_from_long(typ, 0xFFFE0000FFFE0000u64 as i64)); // TODO: transmute the number instead? + let right = self.lshr(right, context.new_rvalue_from_long(typ, 17)); + let step2 = self.or(left, right); + + // Third step. + let left = self.lshr(step2, context.new_rvalue_from_long(typ, 10)); + let left = self.xor(step2, left); + let temp = self.and(left, context.new_rvalue_from_long(typ, 0x003F801F003F801F)); + + let left = self.shl(temp, context.new_rvalue_from_long(typ, 10)); + let left = self.or(temp, left); + let step3 = self.xor(left, step2); + + // Fourth step. + let left = self.lshr(step3, context.new_rvalue_from_long(typ, 4)); + let left = self.xor(step3, left); + let temp = self.and(left, context.new_rvalue_from_long(typ, 0x0E0384210E038421)); + + let left = self.shl(temp, context.new_rvalue_from_long(typ, 4)); + let left = self.or(temp, left); + let step4 = self.xor(left, step3); + + // Fifth step. + let left = self.lshr(step4, context.new_rvalue_from_long(typ, 2)); + let left = self.xor(step4, left); + let temp = self.and(left, context.new_rvalue_from_long(typ, 0x2248884222488842)); + + let left = self.shl(temp, context.new_rvalue_from_long(typ, 2)); + let left = self.or(temp, left); + let step5 = self.xor(left, step4); + + step5 + }, + 128 => { + // TODO: find a more efficient implementation? + let sixty_four = self.context.new_rvalue_from_long(typ, 64); + let high = self.context.new_cast(None, value >> sixty_four, self.u64_type); + let low = self.context.new_cast(None, value, self.u64_type); + + let reversed_high = self.bit_reverse(64, high); + let reversed_low = self.bit_reverse(64, low); + + let new_low = self.context.new_cast(None, reversed_high, typ); + let new_high = self.context.new_cast(None, reversed_low, typ) << sixty_four; + + new_low | new_high + }, + _ => { + panic!("cannot bit reverse with width = {}", width); + }, + } + } + + fn count_leading_zeroes(&self, width: u64, arg: RValue<'gcc>) -> RValue<'gcc> { + // TODO: use width? + let arg_type = arg.get_type(); + let count_leading_zeroes = + if arg_type.is_uint(&self.cx) { + "__builtin_clz" + } + else if arg_type.is_ulong(&self.cx) { + "__builtin_clzl" + } + else if arg_type.is_ulonglong(&self.cx) { + "__builtin_clzll" + } + else if width == 128 { + // Algorithm from: https://stackoverflow.com/a/28433850/389119 + let array_type = self.context.new_array_type(None, arg_type, 3); + let result = self.current_func() + .new_local(None, array_type, "count_loading_zeroes_results"); + + let sixty_four = self.context.new_rvalue_from_long(arg_type, 64); + let high = self.context.new_cast(None, arg >> sixty_four, self.u64_type); + let low = self.context.new_cast(None, arg, self.u64_type); + + let zero = self.context.new_rvalue_zero(self.usize_type); + let one = self.context.new_rvalue_one(self.usize_type); + let two = self.context.new_rvalue_from_long(self.usize_type, 2); + + let clzll = self.context.get_builtin_function("__builtin_clzll"); + + let first_elem = self.context.new_array_access(None, result, zero); + let first_value = self.context.new_cast(None, self.context.new_call(None, clzll, &[high]), arg_type); + self.llbb() + .add_assignment(None, first_elem, first_value); + + let second_elem = self.context.new_array_access(None, result, one); + let second_value = self.context.new_cast(None, self.context.new_call(None, clzll, &[low]), arg_type) + sixty_four; + self.llbb() + .add_assignment(None, second_elem, second_value); + + let third_elem = self.context.new_array_access(None, result, two); + let third_value = self.context.new_rvalue_from_long(arg_type, 128); + self.llbb() + .add_assignment(None, third_elem, third_value); + + let not_high = self.context.new_unary_op(None, UnaryOp::LogicalNegate, self.u64_type, high); + let not_low = self.context.new_unary_op(None, UnaryOp::LogicalNegate, self.u64_type, low); + let not_low_and_not_high = not_low & not_high; + let index = not_high + not_low_and_not_high; + + let res = self.context.new_array_access(None, result, index); + + return self.context.new_cast(None, res, arg_type); + } + else { + let count_leading_zeroes = self.context.get_builtin_function("__builtin_clz"); + let arg = self.context.new_cast(None, arg, self.uint_type); + let diff = self.int_width(self.uint_type) - self.int_width(arg_type); + let diff = self.context.new_rvalue_from_long(self.int_type, diff); + let res = self.context.new_call(None, count_leading_zeroes, &[arg]) - diff; + return self.context.new_cast(None, res, arg_type); + }; + let count_leading_zeroes = self.context.get_builtin_function(count_leading_zeroes); + let res = self.context.new_call(None, count_leading_zeroes, &[arg]); + self.context.new_cast(None, res, arg_type) + } + + fn count_trailing_zeroes(&self, _width: u64, arg: RValue<'gcc>) -> RValue<'gcc> { + let arg_type = arg.get_type(); + let (count_trailing_zeroes, expected_type) = + if arg_type.is_uchar(&self.cx) || arg_type.is_ushort(&self.cx) || arg_type.is_uint(&self.cx) { + // NOTE: we don't need to & 0xFF for uchar because the result is undefined on zero. + ("__builtin_ctz", self.cx.uint_type) + } + else if arg_type.is_ulong(&self.cx) { + ("__builtin_ctzl", self.cx.ulong_type) + } + else if arg_type.is_ulonglong(&self.cx) { + ("__builtin_ctzll", self.cx.ulonglong_type) + } + else if arg_type.is_u128(&self.cx) { + // Adapted from the algorithm to count leading zeroes from: https://stackoverflow.com/a/28433850/389119 + let array_type = self.context.new_array_type(None, arg_type, 3); + let result = self.current_func() + .new_local(None, array_type, "count_loading_zeroes_results"); + + let sixty_four = self.context.new_rvalue_from_long(arg_type, 64); + let high = self.context.new_cast(None, arg >> sixty_four, self.u64_type); + let low = self.context.new_cast(None, arg, self.u64_type); + + let zero = self.context.new_rvalue_zero(self.usize_type); + let one = self.context.new_rvalue_one(self.usize_type); + let two = self.context.new_rvalue_from_long(self.usize_type, 2); + + let ctzll = self.context.get_builtin_function("__builtin_ctzll"); + + let first_elem = self.context.new_array_access(None, result, zero); + let first_value = self.context.new_cast(None, self.context.new_call(None, ctzll, &[low]), arg_type); + self.llbb() + .add_assignment(None, first_elem, first_value); + + let second_elem = self.context.new_array_access(None, result, one); + let second_value = self.context.new_cast(None, self.context.new_call(None, ctzll, &[high]), arg_type) + sixty_four; + self.llbb() + .add_assignment(None, second_elem, second_value); + + let third_elem = self.context.new_array_access(None, result, two); + let third_value = self.context.new_rvalue_from_long(arg_type, 128); + self.llbb() + .add_assignment(None, third_elem, third_value); + + let not_low = self.context.new_unary_op(None, UnaryOp::LogicalNegate, self.u64_type, low); + let not_high = self.context.new_unary_op(None, UnaryOp::LogicalNegate, self.u64_type, high); + let not_low_and_not_high = not_low & not_high; + let index = not_low + not_low_and_not_high; + + let res = self.context.new_array_access(None, result, index); + + return self.context.new_cast(None, res, arg_type); + } + else { + unimplemented!("count_trailing_zeroes for {:?}", arg_type); + }; + let count_trailing_zeroes = self.context.get_builtin_function(count_trailing_zeroes); + let arg = + if arg_type != expected_type { + self.context.new_cast(None, arg, expected_type) + } + else { + arg + }; + let res = self.context.new_call(None, count_trailing_zeroes, &[arg]); + self.context.new_cast(None, res, arg_type) + } + + fn int_width(&self, typ: Type<'gcc>) -> i64 { + self.cx.int_width(typ) as i64 + } + + fn pop_count(&self, value: RValue<'gcc>) -> RValue<'gcc> { + // TODO: use the optimized version with fewer operations. + let value_type = value.get_type(); + + if value_type.is_u128(&self.cx) { + // TODO: implement in the normal algorithm below to have a more efficient + // implementation (that does not require a call to __popcountdi2). + let popcount = self.context.get_builtin_function("__builtin_popcountll"); + let sixty_four = self.context.new_rvalue_from_long(value_type, 64); + let high = self.context.new_cast(None, value >> sixty_four, self.cx.ulonglong_type); + let high = self.context.new_call(None, popcount, &[high]); + let low = self.context.new_cast(None, value, self.cx.ulonglong_type); + let low = self.context.new_call(None, popcount, &[low]); + return high + low; + } + + // First step. + let mask = self.context.new_rvalue_from_long(value_type, 0x5555555555555555); + let left = value & mask; + let shifted = value >> self.context.new_rvalue_from_int(value_type, 1); + let right = shifted & mask; + let value = left + right; + + // Second step. + let mask = self.context.new_rvalue_from_long(value_type, 0x3333333333333333); + let left = value & mask; + let shifted = value >> self.context.new_rvalue_from_int(value_type, 2); + let right = shifted & mask; + let value = left + right; + + // Third step. + let mask = self.context.new_rvalue_from_long(value_type, 0x0F0F0F0F0F0F0F0F); + let left = value & mask; + let shifted = value >> self.context.new_rvalue_from_int(value_type, 4); + let right = shifted & mask; + let value = left + right; + + if value_type.is_u8(&self.cx) { + return value; + } + + // Fourth step. + let mask = self.context.new_rvalue_from_long(value_type, 0x00FF00FF00FF00FF); + let left = value & mask; + let shifted = value >> self.context.new_rvalue_from_int(value_type, 8); + let right = shifted & mask; + let value = left + right; + + if value_type.is_u16(&self.cx) { + return value; + } + + // Fifth step. + let mask = self.context.new_rvalue_from_long(value_type, 0x0000FFFF0000FFFF); + let left = value & mask; + let shifted = value >> self.context.new_rvalue_from_int(value_type, 16); + let right = shifted & mask; + let value = left + right; + + if value_type.is_u32(&self.cx) { + return value; + } + + // Sixth step. + let mask = self.context.new_rvalue_from_long(value_type, 0x00000000FFFFFFFF); + let left = value & mask; + let shifted = value >> self.context.new_rvalue_from_int(value_type, 32); + let right = shifted & mask; + let value = left + right; + + value + } + + // Algorithm from: https://blog.regehr.org/archives/1063 + fn rotate_left(&mut self, value: RValue<'gcc>, shift: RValue<'gcc>, width: u64) -> RValue<'gcc> { + let max = self.context.new_rvalue_from_long(shift.get_type(), width as i64); + let shift = shift % max; + let lhs = self.shl(value, shift); + let result_and = + self.and( + self.context.new_unary_op(None, UnaryOp::Minus, shift.get_type(), shift), + self.context.new_rvalue_from_long(shift.get_type(), width as i64 - 1), + ); + let rhs = self.lshr(value, result_and); + self.or(lhs, rhs) + } + + // Algorithm from: https://blog.regehr.org/archives/1063 + fn rotate_right(&mut self, value: RValue<'gcc>, shift: RValue<'gcc>, width: u64) -> RValue<'gcc> { + let max = self.context.new_rvalue_from_long(shift.get_type(), width as i64); + let shift = shift % max; + let lhs = self.lshr(value, shift); + let result_and = + self.and( + self.context.new_unary_op(None, UnaryOp::Minus, shift.get_type(), shift), + self.context.new_rvalue_from_long(shift.get_type(), width as i64 - 1), + ); + let rhs = self.shl(value, result_and); + self.or(lhs, rhs) + } + + fn saturating_add(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool, width: u64) -> RValue<'gcc> { + let func = self.current_func.borrow().expect("func"); + + if signed { + // Algorithm from: https://stackoverflow.com/a/56531252/389119 + let after_block = func.new_block("after"); + let func_name = + match width { + 8 => "__builtin_add_overflow", + 16 => "__builtin_add_overflow", + 32 => "__builtin_sadd_overflow", + 64 => "__builtin_saddll_overflow", + 128 => "__builtin_add_overflow", + _ => unreachable!(), + }; + let overflow_func = self.context.get_builtin_function(func_name); + let result_type = lhs.get_type(); + let res = func.new_local(None, result_type, "saturating_sum"); + let overflow = self.overflow_call(overflow_func, &[lhs, rhs, res.get_address(None)], None); + + let then_block = func.new_block("then"); + + let unsigned_type = self.context.new_int_type(width as i32 / 8, false); + let shifted = self.context.new_cast(None, lhs, unsigned_type) >> self.context.new_rvalue_from_int(unsigned_type, width as i32 - 1); + let uint_max = self.context.new_unary_op(None, UnaryOp::BitwiseNegate, unsigned_type, + self.context.new_rvalue_from_int(unsigned_type, 0) + ); + let int_max = uint_max >> self.context.new_rvalue_one(unsigned_type); + then_block.add_assignment(None, res, self.context.new_cast(None, shifted + int_max, result_type)); + then_block.end_with_jump(None, after_block); + + self.block.expect("block").end_with_conditional(None, overflow, then_block, after_block); + + // NOTE: since jumps were added in a place rustc does not + // expect, the current blocks in the state need to be updated. + *self.current_block.borrow_mut() = Some(after_block); + self.block = Some(after_block); + + res.to_rvalue() + } + else { + // Algorithm from: http://locklessinc.com/articles/sat_arithmetic/ + let res = lhs + rhs; + let res_type = res.get_type(); + let cond = self.context.new_comparison(None, ComparisonOp::LessThan, res, lhs); + let value = self.context.new_unary_op(None, UnaryOp::Minus, res_type, self.context.new_cast(None, cond, res_type)); + res | value + } + } + + // Algorithm from: https://locklessinc.com/articles/sat_arithmetic/ + fn saturating_sub(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>, signed: bool, width: u64) -> RValue<'gcc> { + if signed { + // Also based on algorithm from: https://stackoverflow.com/a/56531252/389119 + let func_name = + match width { + 8 => "__builtin_sub_overflow", + 16 => "__builtin_sub_overflow", + 32 => "__builtin_ssub_overflow", + 64 => "__builtin_ssubll_overflow", + 128 => "__builtin_sub_overflow", + _ => unreachable!(), + }; + let overflow_func = self.context.get_builtin_function(func_name); + let result_type = lhs.get_type(); + let func = self.current_func.borrow().expect("func"); + let res = func.new_local(None, result_type, "saturating_diff"); + let overflow = self.overflow_call(overflow_func, &[lhs, rhs, res.get_address(None)], None); + + let then_block = func.new_block("then"); + let after_block = func.new_block("after"); + + let unsigned_type = self.context.new_int_type(width as i32 / 8, false); + let shifted = self.context.new_cast(None, lhs, unsigned_type) >> self.context.new_rvalue_from_int(unsigned_type, width as i32 - 1); + let uint_max = self.context.new_unary_op(None, UnaryOp::BitwiseNegate, unsigned_type, + self.context.new_rvalue_from_int(unsigned_type, 0) + ); + let int_max = uint_max >> self.context.new_rvalue_one(unsigned_type); + then_block.add_assignment(None, res, self.context.new_cast(None, shifted + int_max, result_type)); + then_block.end_with_jump(None, after_block); + + self.block.expect("block").end_with_conditional(None, overflow, then_block, after_block); + + // NOTE: since jumps were added in a place rustc does not + // expect, the current blocks in the state need to be updated. + *self.current_block.borrow_mut() = Some(after_block); + self.block = Some(after_block); + + res.to_rvalue() + } + else { + let res = lhs - rhs; + let comparison = self.context.new_comparison(None, ComparisonOp::LessThanEquals, res, lhs); + let comparison = self.context.new_cast(None, comparison, lhs.get_type()); + let unary_op = self.context.new_unary_op(None, UnaryOp::Minus, comparison.get_type(), comparison); + self.and(res, unary_op) + } + } +} + +fn try_intrinsic<'gcc, 'tcx>(bx: &mut Builder<'_, 'gcc, 'tcx>, try_func: RValue<'gcc>, data: RValue<'gcc>, _catch_func: RValue<'gcc>, dest: RValue<'gcc>) { + if bx.sess().panic_strategy() == PanicStrategy::Abort { + bx.call(try_func, &[data], None); + // Return 0 unconditionally from the intrinsic call; + // we can never unwind. + let ret_align = bx.tcx.data_layout.i32_align.abi; + bx.store(bx.const_i32(0), dest, ret_align); + } + else if wants_msvc_seh(bx.sess()) { + unimplemented!(); + //codegen_msvc_try(bx, try_func, data, catch_func, dest); + } + else { + unimplemented!(); + //codegen_gnu_try(bx, try_func, data, catch_func, dest); + } +} + +// MSVC's definition of the `rust_try` function. +// +// This implementation uses the new exception handling instructions in LLVM +// which have support in LLVM for SEH on MSVC targets. Although these +// instructions are meant to work for all targets, as of the time of this +// writing, however, LLVM does not recommend the usage of these new instructions +// as the old ones are still more optimized. +/*fn codegen_msvc_try<'a, 'gcc, 'tcx>(_bx: &mut Builder<'a, 'gcc, 'tcx>, _try_func: RValue<'gcc>, _data: RValue<'gcc>, _catch_func: RValue<'gcc>, _dest: RValue<'gcc>) { + unimplemented!(); + /*let llfn = get_rust_try_fn(bx, &mut |mut bx| { + bx.set_personality_fn(bx.eh_personality()); + bx.sideeffect(); + + let mut normal = bx.build_sibling_block("normal"); + let mut catchswitch = bx.build_sibling_block("catchswitch"); + let mut catchpad = bx.build_sibling_block("catchpad"); + let mut caught = bx.build_sibling_block("caught"); + + let try_func = llvm::get_param(bx.llfn(), 0); + let data = llvm::get_param(bx.llfn(), 1); + let catch_func = llvm::get_param(bx.llfn(), 2); + + // We're generating an IR snippet that looks like: + // + // declare i32 @rust_try(%try_func, %data, %catch_func) { + // %slot = alloca u8* + // invoke %try_func(%data) to label %normal unwind label %catchswitch + // + // normal: + // ret i32 0 + // + // catchswitch: + // %cs = catchswitch within none [%catchpad] unwind to caller + // + // catchpad: + // %tok = catchpad within %cs [%type_descriptor, 0, %slot] + // %ptr = load %slot + // call %catch_func(%data, %ptr) + // catchret from %tok to label %caught + // + // caught: + // ret i32 1 + // } + // + // This structure follows the basic usage of throw/try/catch in LLVM. + // For example, compile this C++ snippet to see what LLVM generates: + // + // #include + // + // struct rust_panic { + // rust_panic(const rust_panic&); + // ~rust_panic(); + // + // uint64_t x[2]; + // }; + // + // int __rust_try( + // void (*try_func)(void*), + // void *data, + // void (*catch_func)(void*, void*) noexcept + // ) { + // try { + // try_func(data); + // return 0; + // } catch(rust_panic& a) { + // catch_func(data, &a); + // return 1; + // } + // } + // + // More information can be found in libstd's seh.rs implementation. + let ptr_align = bx.tcx().data_layout.pointer_align.abi; + let slot = bx.alloca(bx.type_i8p(), ptr_align); + bx.invoke(try_func, &[data], normal.llbb(), catchswitch.llbb(), None); + + normal.ret(bx.const_i32(0)); + + let cs = catchswitch.catch_switch(None, None, 1); + catchswitch.add_handler(cs, catchpad.llbb()); + + // We can't use the TypeDescriptor defined in libpanic_unwind because it + // might be in another DLL and the SEH encoding only supports specifying + // a TypeDescriptor from the current module. + // + // However this isn't an issue since the MSVC runtime uses string + // comparison on the type name to match TypeDescriptors rather than + // pointer equality. + // + // So instead we generate a new TypeDescriptor in each module that uses + // `try` and let the linker merge duplicate definitions in the same + // module. + // + // When modifying, make sure that the type_name string exactly matches + // the one used in src/libpanic_unwind/seh.rs. + let type_info_vtable = bx.declare_global("??_7type_info@@6B@", bx.type_i8p()); + let type_name = bx.const_bytes(b"rust_panic\0"); + let type_info = + bx.const_struct(&[type_info_vtable, bx.const_null(bx.type_i8p()), type_name], false); + let tydesc = bx.declare_global("__rust_panic_type_info", bx.val_ty(type_info)); + unsafe { + llvm::LLVMRustSetLinkage(tydesc, llvm::Linkage::LinkOnceODRLinkage); + llvm::SetUniqueComdat(bx.llmod, tydesc); + llvm::LLVMSetInitializer(tydesc, type_info); + } + + // The flag value of 8 indicates that we are catching the exception by + // reference instead of by value. We can't use catch by value because + // that requires copying the exception object, which we don't support + // since our exception object effectively contains a Box. + // + // Source: MicrosoftCXXABI::getAddrOfCXXCatchHandlerType in clang + let flags = bx.const_i32(8); + let funclet = catchpad.catch_pad(cs, &[tydesc, flags, slot]); + let ptr = catchpad.load(slot, ptr_align); + catchpad.call(catch_func, &[data, ptr], Some(&funclet)); + + catchpad.catch_ret(&funclet, caught.llbb()); + + caught.ret(bx.const_i32(1)); + }); + + // Note that no invoke is used here because by definition this function + // can't panic (that's what it's catching). + let ret = bx.call(llfn, &[try_func, data, catch_func], None); + let i32_align = bx.tcx().data_layout.i32_align.abi; + bx.store(ret, dest, i32_align);*/ +}*/ + +// Definition of the standard `try` function for Rust using the GNU-like model +// of exceptions (e.g., the normal semantics of LLVM's `landingpad` and `invoke` +// instructions). +// +// This codegen is a little surprising because we always call a shim +// function instead of inlining the call to `invoke` manually here. This is done +// because in LLVM we're only allowed to have one personality per function +// definition. The call to the `try` intrinsic is being inlined into the +// function calling it, and that function may already have other personality +// functions in play. By calling a shim we're guaranteed that our shim will have +// the right personality function. +/*fn codegen_gnu_try<'a, 'gcc, 'tcx>(_bx: &mut Builder<'a, 'gcc, 'tcx>, _try_func: RValue<'gcc>, _data: RValue<'gcc>, _catch_func: RValue<'gcc>, _dest: RValue<'gcc>) { + unimplemented!(); + /*let llfn = get_rust_try_fn(bx, &mut |mut bx| { + // Codegens the shims described above: + // + // bx: + // invoke %try_func(%data) normal %normal unwind %catch + // + // normal: + // ret 0 + // + // catch: + // (%ptr, _) = landingpad + // call %catch_func(%data, %ptr) + // ret 1 + + bx.sideeffect(); + + let mut then = bx.build_sibling_block("then"); + let mut catch = bx.build_sibling_block("catch"); + + let try_func = llvm::get_param(bx.llfn(), 0); + let data = llvm::get_param(bx.llfn(), 1); + let catch_func = llvm::get_param(bx.llfn(), 2); + bx.invoke(try_func, &[data], then.llbb(), catch.llbb(), None); + then.ret(bx.const_i32(0)); + + // Type indicator for the exception being thrown. + // + // The first value in this tuple is a pointer to the exception object + // being thrown. The second value is a "selector" indicating which of + // the landing pad clauses the exception's type had been matched to. + // rust_try ignores the selector. + let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false); + let vals = catch.landing_pad(lpad_ty, bx.eh_personality(), 1); + let tydesc = match bx.tcx().lang_items().eh_catch_typeinfo() { + Some(tydesc) => { + let tydesc = bx.get_static(tydesc); + bx.bitcast(tydesc, bx.type_i8p()) + } + None => bx.const_null(bx.type_i8p()), + }; + catch.add_clause(vals, tydesc); + let ptr = catch.extract_value(vals, 0); + catch.call(catch_func, &[data, ptr], None); + catch.ret(bx.const_i32(1)); + }); + + // Note that no invoke is used here because by definition this function + // can't panic (that's what it's catching). + let ret = bx.call(llfn, &[try_func, data, catch_func], None); + let i32_align = bx.tcx().data_layout.i32_align.abi; + bx.store(ret, dest, i32_align);*/ +}*/ diff --git a/src/intrinsic/simd.rs b/src/intrinsic/simd.rs new file mode 100644 index 0000000000000..71cf5cce9f4e1 --- /dev/null +++ b/src/intrinsic/simd.rs @@ -0,0 +1,1001 @@ +use gccjit::{RValue, Type}; +use rustc_codegen_ssa::base::compare_simd_types; +use rustc_codegen_ssa::common::{TypeKind, span_invalid_monomorphization_error}; +use rustc_codegen_ssa::mir::operand::OperandRef; +use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods}; +use rustc_hir as hir; +use rustc_middle::span_bug; +use rustc_middle::ty::layout::HasTyCtxt; +use rustc_middle::ty::{self, Ty}; +use rustc_span::{Span, Symbol, sym}; + +use crate::builder::Builder; + +pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, name: Symbol, callee_ty: Ty<'tcx>, args: &[OperandRef<'tcx, RValue<'gcc>>], ret_ty: Ty<'tcx>, llret_ty: Type<'gcc>, span: Span) -> Result, ()> { + //println!("Generic simd: {}", name); + + // macros for error handling: + macro_rules! emit_error { + ($msg: tt) => { + emit_error!($msg, ) + }; + ($msg: tt, $($fmt: tt)*) => { + span_invalid_monomorphization_error( + bx.sess(), span, + &format!(concat!("invalid monomorphization of `{}` intrinsic: ", $msg), + name, $($fmt)*)); + } + } + + macro_rules! return_error { + ($($fmt: tt)*) => { + { + emit_error!($($fmt)*); + return Err(()); + } + } + } + + macro_rules! require { + ($cond: expr, $($fmt: tt)*) => { + if !$cond { + return_error!($($fmt)*); + } + }; + } + + macro_rules! require_simd { + ($ty: expr, $position: expr) => { + require!($ty.is_simd(), "expected SIMD {} type, found non-SIMD `{}`", $position, $ty) + }; + } + + let tcx = bx.tcx(); + let sig = + tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), callee_ty.fn_sig(tcx)); + let arg_tys = sig.inputs(); + let name_str = &*name.as_str(); + + /*if name == sym::simd_select_bitmask { + let in_ty = arg_tys[0]; + let m_len = match in_ty.kind() { + // Note that this `.unwrap()` crashes for isize/usize, that's sort + // of intentional as there's not currently a use case for that. + ty::Int(i) => i.bit_width().unwrap(), + ty::Uint(i) => i.bit_width().unwrap(), + _ => return_error!("`{}` is not an integral type", in_ty), + }; + require_simd!(arg_tys[1], "argument"); + let (v_len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); + require!( + // Allow masks for vectors with fewer than 8 elements to be + // represented with a u8 or i8. + m_len == v_len || (m_len == 8 && v_len < 8), + "mismatched lengths: mask length `{}` != other vector length `{}`", + m_len, + v_len + ); + let i1 = bx.type_i1(); + let im = bx.type_ix(v_len); + let i1xn = bx.type_vector(i1, v_len); + let m_im = bx.trunc(args[0].immediate(), im); + let m_i1s = bx.bitcast(m_im, i1xn); + return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate())); + }*/ + + // every intrinsic below takes a SIMD vector as its first argument + require_simd!(arg_tys[0], "input"); + let in_ty = arg_tys[0]; + + let comparison = match name { + sym::simd_eq => Some(hir::BinOpKind::Eq), + sym::simd_ne => Some(hir::BinOpKind::Ne), + sym::simd_lt => Some(hir::BinOpKind::Lt), + sym::simd_le => Some(hir::BinOpKind::Le), + sym::simd_gt => Some(hir::BinOpKind::Gt), + sym::simd_ge => Some(hir::BinOpKind::Ge), + _ => None, + }; + + let (in_len, in_elem) = arg_tys[0].simd_size_and_type(bx.tcx()); + if let Some(cmp_op) = comparison { + require_simd!(ret_ty, "return"); + + let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx()); + require!( + in_len == out_len, + "expected return type with length {} (same as input type `{}`), \ + found `{}` with length {}", + in_len, + in_ty, + ret_ty, + out_len + ); + require!( + bx.type_kind(bx.element_type(llret_ty)) == TypeKind::Integer, + "expected return type with integer elements, found `{}` with non-integer `{}`", + ret_ty, + out_ty + ); + + return Ok(compare_simd_types( + bx, + args[0].immediate(), + args[1].immediate(), + in_elem, + llret_ty, + cmp_op, + )); + } + + if let Some(stripped) = name_str.strip_prefix("simd_shuffle") { + let n: u64 = stripped.parse().unwrap_or_else(|_| { + span_bug!(span, "bad `simd_shuffle` instruction only caught in codegen?") + }); + + require_simd!(ret_ty, "return"); + + let (out_len, out_ty) = ret_ty.simd_size_and_type(bx.tcx()); + require!( + out_len == n, + "expected return type of length {}, found `{}` with length {}", + n, + ret_ty, + out_len + ); + require!( + in_elem == out_ty, + "expected return element type `{}` (element of input `{}`), \ + found `{}` with element type `{}`", + in_elem, + in_ty, + ret_ty, + out_ty + ); + + //let total_len = u128::from(in_len) * 2; + + let vector = args[2].immediate(); + + // TODO: + /*let indices: Option> = (0..n) + .map(|i| { + let arg_idx = i; + let val = bx.const_get_vector_element(vector, i as u64); + match bx.const_to_opt_u128(val, true) { + None => { + emit_error!("shuffle index #{} is not a constant", arg_idx); + None + } + Some(idx) if idx >= total_len => { + emit_error!( + "shuffle index #{} is out of bounds (limit {})", + arg_idx, + total_len + ); + None + } + Some(idx) => Some(bx.const_i32(idx as i32)), + } + }) + .collect(); + let indices = match indices { + Some(i) => i, + None => return Ok(bx.const_null(llret_ty)), + };*/ + + return Ok(bx.shuffle_vector( + args[0].immediate(), + args[1].immediate(), + vector, + )); + } + + /*if name == sym::simd_insert { + require!( + in_elem == arg_tys[2], + "expected inserted type `{}` (element of input `{}`), found `{}`", + in_elem, + in_ty, + arg_tys[2] + ); + return Ok(bx.insert_element( + args[0].immediate(), + args[2].immediate(), + args[1].immediate(), + )); + } + if name == sym::simd_extract { + require!( + ret_ty == in_elem, + "expected return type `{}` (element of input `{}`), found `{}`", + in_elem, + in_ty, + ret_ty + ); + return Ok(bx.extract_element(args[0].immediate(), args[1].immediate())); + } + + if name == sym::simd_select { + let m_elem_ty = in_elem; + let m_len = in_len; + require_simd!(arg_tys[1], "argument"); + let (v_len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); + require!( + m_len == v_len, + "mismatched lengths: mask length `{}` != other vector length `{}`", + m_len, + v_len + ); + match m_elem_ty.kind() { + ty::Int(_) => {} + _ => return_error!("mask element type is `{}`, expected `i_`", m_elem_ty), + } + // truncate the mask to a vector of i1s + let i1 = bx.type_i1(); + let i1xn = bx.type_vector(i1, m_len as u64); + let m_i1s = bx.trunc(args[0].immediate(), i1xn); + return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate())); + } + + if name == sym::simd_bitmask { + // The `fn simd_bitmask(vector) -> unsigned integer` intrinsic takes a + // vector mask and returns an unsigned integer containing the most + // significant bit (MSB) of each lane. + + // If the vector has less than 8 lanes, an u8 is returned with zeroed + // trailing bits. + let expected_int_bits = in_len.max(8); + match ret_ty.kind() { + ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => (), + _ => return_error!("bitmask `{}`, expected `u{}`", ret_ty, expected_int_bits), + } + + // Integer vector : + let (i_xn, in_elem_bitwidth) = match in_elem.kind() { + ty::Int(i) => ( + args[0].immediate(), + i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()), + ), + ty::Uint(i) => ( + args[0].immediate(), + i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()), + ), + _ => return_error!( + "vector argument `{}`'s element type `{}`, expected integer element type", + in_ty, + in_elem + ), + }; + + // Shift the MSB to the right by "in_elem_bitwidth - 1" into the first bit position. + let shift_indices = + vec![ + bx.cx.const_int(bx.type_ix(in_elem_bitwidth), (in_elem_bitwidth - 1) as _); + in_len as _ + ]; + let i_xn_msb = bx.lshr(i_xn, bx.const_vector(shift_indices.as_slice())); + // Truncate vector to an + let i1xn = bx.trunc(i_xn_msb, bx.type_vector(bx.type_i1(), in_len)); + // Bitcast to iN: + let i_ = bx.bitcast(i1xn, bx.type_ix(in_len)); + // Zero-extend iN to the bitmask type: + return Ok(bx.zext(i_, bx.type_ix(expected_int_bits))); + } + + fn simd_simple_float_intrinsic<'a, 'gcc, 'tcx>( + name: Symbol, + in_elem: &::rustc_middle::ty::TyS<'_>, + in_ty: &::rustc_middle::ty::TyS<'_>, + in_len: u64, + bx: &mut Builder<'a, 'gcc, 'tcx>, + span: Span, + args: &[OperandRef<'tcx, RValue<'gcc>>], + ) -> Result, ()> { + macro_rules! emit_error { + ($msg: tt) => { + emit_error!($msg, ) + }; + ($msg: tt, $($fmt: tt)*) => { + span_invalid_monomorphization_error( + bx.sess(), span, + &format!(concat!("invalid monomorphization of `{}` intrinsic: ", $msg), + name, $($fmt)*)); + } + } + macro_rules! return_error { + ($($fmt: tt)*) => { + { + emit_error!($($fmt)*); + return Err(()); + } + } + } + + let (elem_ty_str, elem_ty) = if let ty::Float(f) = in_elem.kind() { + let elem_ty = bx.cx.type_float_from_ty(*f); + match f.bit_width() { + 32 => ("f32", elem_ty), + 64 => ("f64", elem_ty), + _ => { + return_error!( + "unsupported element type `{}` of floating-point vector `{}`", + f.name_str(), + in_ty + ); + } + } + } else { + return_error!("`{}` is not a floating-point type", in_ty); + }; + + let vec_ty = bx.type_vector(elem_ty, in_len); + + let (intr_name, fn_ty) = match name { + sym::simd_ceil => ("ceil", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_fabs => ("fabs", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_fcos => ("cos", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_fexp2 => ("exp2", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_fexp => ("exp", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_flog10 => ("log10", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_flog2 => ("log2", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_flog => ("log", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_floor => ("floor", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_fma => ("fma", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)), + sym::simd_fpowi => ("powi", bx.type_func(&[vec_ty, bx.type_i32()], vec_ty)), + sym::simd_fpow => ("pow", bx.type_func(&[vec_ty, vec_ty], vec_ty)), + sym::simd_fsin => ("sin", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_fsqrt => ("sqrt", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_round => ("round", bx.type_func(&[vec_ty], vec_ty)), + sym::simd_trunc => ("trunc", bx.type_func(&[vec_ty], vec_ty)), + _ => return_error!("unrecognized intrinsic `{}`", name), + }; + let llvm_name = &format!("llvm.{0}.v{1}{2}", intr_name, in_len, elem_ty_str); + let f = bx.declare_cfn(&llvm_name, fn_ty); + let c = bx.call(f, &args.iter().map(|arg| arg.immediate()).collect::>(), None); + Ok(c) + } + + if std::matches!( + name, + sym::simd_ceil + | sym::simd_fabs + | sym::simd_fcos + | sym::simd_fexp2 + | sym::simd_fexp + | sym::simd_flog10 + | sym::simd_flog2 + | sym::simd_flog + | sym::simd_floor + | sym::simd_fma + | sym::simd_fpow + | sym::simd_fpowi + | sym::simd_fsin + | sym::simd_fsqrt + | sym::simd_round + | sym::simd_trunc + ) { + return simd_simple_float_intrinsic(name, in_elem, in_ty, in_len, bx, span, args); + } + + // FIXME: use: + // https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Function.h#L182 + // https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Intrinsics.h#L81 + fn llvm_vector_str(elem_ty: Ty<'_>, vec_len: u64, no_pointers: usize) -> String { + let p0s: String = "p0".repeat(no_pointers); + match *elem_ty.kind() { + ty::Int(v) => format!("v{}{}i{}", vec_len, p0s, v.bit_width().unwrap()), + ty::Uint(v) => format!("v{}{}i{}", vec_len, p0s, v.bit_width().unwrap()), + ty::Float(v) => format!("v{}{}f{}", vec_len, p0s, v.bit_width()), + _ => unreachable!(), + } + } + + fn gcc_vector_ty<'gcc>( + cx: &CodegenCx<'gcc, '_>, + elem_ty: Ty<'_>, + vec_len: u64, + mut no_pointers: usize, + ) -> Type<'gcc> { + // FIXME: use cx.layout_of(ty).llvm_type() ? + let mut elem_ty = match *elem_ty.kind() { + ty::Int(v) => cx.type_int_from_ty(v), + ty::Uint(v) => cx.type_uint_from_ty(v), + ty::Float(v) => cx.type_float_from_ty(v), + _ => unreachable!(), + }; + while no_pointers > 0 { + elem_ty = cx.type_ptr_to(elem_ty); + no_pointers -= 1; + } + cx.type_vector(elem_ty, vec_len) + } + + if name == sym::simd_gather { + // simd_gather(values: , pointers: , + // mask: ) -> + // * N: number of elements in the input vectors + // * T: type of the element to load + // * M: any integer width is supported, will be truncated to i1 + + // All types must be simd vector types + require_simd!(in_ty, "first"); + require_simd!(arg_tys[1], "second"); + require_simd!(arg_tys[2], "third"); + require_simd!(ret_ty, "return"); + + // Of the same length: + let (out_len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); + let (out_len2, _) = arg_tys[2].simd_size_and_type(bx.tcx()); + require!( + in_len == out_len, + "expected {} argument with length {} (same as input type `{}`), \ + found `{}` with length {}", + "second", + in_len, + in_ty, + arg_tys[1], + out_len + ); + require!( + in_len == out_len2, + "expected {} argument with length {} (same as input type `{}`), \ + found `{}` with length {}", + "third", + in_len, + in_ty, + arg_tys[2], + out_len2 + ); + + // The return type must match the first argument type + require!(ret_ty == in_ty, "expected return type `{}`, found `{}`", in_ty, ret_ty); + + // This counts how many pointers + fn ptr_count(t: Ty<'_>) -> usize { + match t.kind() { + ty::RawPtr(p) => 1 + ptr_count(p.ty), + _ => 0, + } + } + + // Non-ptr type + fn non_ptr(t: Ty<'_>) -> Ty<'_> { + match t.kind() { + ty::RawPtr(p) => non_ptr(p.ty), + _ => t, + } + } + + // The second argument must be a simd vector with an element type that's a pointer + // to the element type of the first argument + let (_, element_ty0) = arg_tys[0].simd_size_and_type(bx.tcx()); + let (_, element_ty1) = arg_tys[1].simd_size_and_type(bx.tcx()); + let (pointer_count, underlying_ty) = match element_ty1.kind() { + ty::RawPtr(p) if p.ty == in_elem => (ptr_count(element_ty1), non_ptr(element_ty1)), + _ => { + require!( + false, + "expected element type `{}` of second argument `{}` \ + to be a pointer to the element type `{}` of the first \ + argument `{}`, found `{}` != `*_ {}`", + element_ty1, + arg_tys[1], + in_elem, + in_ty, + element_ty1, + in_elem + ); + unreachable!(); + } + }; + assert!(pointer_count > 0); + assert_eq!(pointer_count - 1, ptr_count(element_ty0)); + assert_eq!(underlying_ty, non_ptr(element_ty0)); + + // The element type of the third argument must be a signed integer type of any width: + let (_, element_ty2) = arg_tys[2].simd_size_and_type(bx.tcx()); + match element_ty2.kind() { + ty::Int(_) => (), + _ => { + require!( + false, + "expected element type `{}` of third argument `{}` \ + to be a signed integer type", + element_ty2, + arg_tys[2] + ); + } + } + + // Alignment of T, must be a constant integer value: + let alignment_ty = bx.type_i32(); + let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32); + + // Truncate the mask vector to a vector of i1s: + let (mask, mask_ty) = { + let i1 = bx.type_i1(); + let i1xn = bx.type_vector(i1, in_len); + (bx.trunc(args[2].immediate(), i1xn), i1xn) + }; + + // Type of the vector of pointers: + let llvm_pointer_vec_ty = gcc_vector_ty(bx, underlying_ty, in_len, pointer_count); + let llvm_pointer_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count); + + // Type of the vector of elements: + let llvm_elem_vec_ty = gcc_vector_ty(bx, underlying_ty, in_len, pointer_count - 1); + let llvm_elem_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count - 1); + + let llvm_intrinsic = + format!("llvm.masked.gather.{}.{}", llvm_elem_vec_str, llvm_pointer_vec_str); + let f = bx.declare_cfn( + &llvm_intrinsic, + bx.type_func( + &[llvm_pointer_vec_ty, alignment_ty, mask_ty, llvm_elem_vec_ty], + llvm_elem_vec_ty, + ), + ); + let v = bx.call(f, &[args[1].immediate(), alignment, mask, args[0].immediate()], None); + return Ok(v); + } + + if name == sym::simd_scatter { + // simd_scatter(values: , pointers: , + // mask: ) -> () + // * N: number of elements in the input vectors + // * T: type of the element to load + // * M: any integer width is supported, will be truncated to i1 + + // All types must be simd vector types + require_simd!(in_ty, "first"); + require_simd!(arg_tys[1], "second"); + require_simd!(arg_tys[2], "third"); + + // Of the same length: + let (element_len1, _) = arg_tys[1].simd_size_and_type(bx.tcx()); + let (element_len2, _) = arg_tys[2].simd_size_and_type(bx.tcx()); + require!( + in_len == element_len1, + "expected {} argument with length {} (same as input type `{}`), \ + found `{}` with length {}", + "second", + in_len, + in_ty, + arg_tys[1], + element_len1 + ); + require!( + in_len == element_len2, + "expected {} argument with length {} (same as input type `{}`), \ + found `{}` with length {}", + "third", + in_len, + in_ty, + arg_tys[2], + element_len2 + ); + + // This counts how many pointers + fn ptr_count(t: Ty<'_>) -> usize { + match t.kind() { + ty::RawPtr(p) => 1 + ptr_count(p.ty), + _ => 0, + } + } + + // Non-ptr type + fn non_ptr(t: Ty<'_>) -> Ty<'_> { + match t.kind() { + ty::RawPtr(p) => non_ptr(p.ty), + _ => t, + } + } + + // The second argument must be a simd vector with an element type that's a pointer + // to the element type of the first argument + let (_, element_ty0) = arg_tys[0].simd_size_and_type(bx.tcx()); + let (_, element_ty1) = arg_tys[1].simd_size_and_type(bx.tcx()); + let (_, element_ty2) = arg_tys[2].simd_size_and_type(bx.tcx()); + let (pointer_count, underlying_ty) = match element_ty1.kind() { + ty::RawPtr(p) if p.ty == in_elem && p.mutbl == hir::Mutability::Mut => { + (ptr_count(element_ty1), non_ptr(element_ty1)) + } + _ => { + require!( + false, + "expected element type `{}` of second argument `{}` \ + to be a pointer to the element type `{}` of the first \ + argument `{}`, found `{}` != `*mut {}`", + element_ty1, + arg_tys[1], + in_elem, + in_ty, + element_ty1, + in_elem + ); + unreachable!(); + } + }; + assert!(pointer_count > 0); + assert_eq!(pointer_count - 1, ptr_count(element_ty0)); + assert_eq!(underlying_ty, non_ptr(element_ty0)); + + // The element type of the third argument must be a signed integer type of any width: + match element_ty2.kind() { + ty::Int(_) => (), + _ => { + require!( + false, + "expected element type `{}` of third argument `{}` \ + be a signed integer type", + element_ty2, + arg_tys[2] + ); + } + } + + // Alignment of T, must be a constant integer value: + let alignment_ty = bx.type_i32(); + let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32); + + // Truncate the mask vector to a vector of i1s: + let (mask, mask_ty) = { + let i1 = bx.type_i1(); + let i1xn = bx.type_vector(i1, in_len); + (bx.trunc(args[2].immediate(), i1xn), i1xn) + }; + + let ret_t = bx.type_void(); + + // Type of the vector of pointers: + let llvm_pointer_vec_ty = gcc_vector_ty(bx, underlying_ty, in_len, pointer_count); + let llvm_pointer_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count); + + // Type of the vector of elements: + let llvm_elem_vec_ty = gcc_vector_ty(bx, underlying_ty, in_len, pointer_count - 1); + let llvm_elem_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count - 1); + + let llvm_intrinsic = + format!("llvm.masked.scatter.{}.{}", llvm_elem_vec_str, llvm_pointer_vec_str); + let f = bx.declare_cfn( + &llvm_intrinsic, + bx.type_func(&[llvm_elem_vec_ty, llvm_pointer_vec_ty, alignment_ty, mask_ty], ret_t), + ); + let v = bx.call(f, &[args[0].immediate(), args[1].immediate(), alignment, mask], None); + return Ok(v); + } + + macro_rules! arith_red { + ($name:ident : $integer_reduce:ident, $float_reduce:ident, $ordered:expr, $op:ident, + $identity:expr) => { + if name == sym::$name { + require!( + ret_ty == in_elem, + "expected return type `{}` (element of input `{}`), found `{}`", + in_elem, + in_ty, + ret_ty + ); + return match in_elem.kind() { + ty::Int(_) | ty::Uint(_) => { + let r = bx.$integer_reduce(args[0].immediate()); + if $ordered { + // if overflow occurs, the result is the + // mathematical result modulo 2^n: + Ok(bx.$op(args[1].immediate(), r)) + } else { + Ok(bx.$integer_reduce(args[0].immediate())) + } + } + ty::Float(f) => { + let acc = if $ordered { + // ordered arithmetic reductions take an accumulator + args[1].immediate() + } else { + // unordered arithmetic reductions use the identity accumulator + match f.bit_width() { + 32 => bx.const_real(bx.type_f32(), $identity), + 64 => bx.const_real(bx.type_f64(), $identity), + v => return_error!( + r#" +unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, + sym::$name, + in_ty, + in_elem, + v, + ret_ty + ), + } + }; + Ok(bx.$float_reduce(acc, args[0].immediate())) + } + _ => return_error!( + "unsupported {} from `{}` with element `{}` to `{}`", + sym::$name, + in_ty, + in_elem, + ret_ty + ), + }; + } + }; + } + + arith_red!(simd_reduce_add_ordered: vector_reduce_add, vector_reduce_fadd, true, add, 0.0); + arith_red!(simd_reduce_mul_ordered: vector_reduce_mul, vector_reduce_fmul, true, mul, 1.0); + arith_red!( + simd_reduce_add_unordered: vector_reduce_add, + vector_reduce_fadd_fast, + false, + add, + 0.0 + ); + arith_red!( + simd_reduce_mul_unordered: vector_reduce_mul, + vector_reduce_fmul_fast, + false, + mul, + 1.0 + ); + + macro_rules! minmax_red { + ($name:ident: $int_red:ident, $float_red:ident) => { + if name == sym::$name { + require!( + ret_ty == in_elem, + "expected return type `{}` (element of input `{}`), found `{}`", + in_elem, + in_ty, + ret_ty + ); + return match in_elem.kind() { + ty::Int(_i) => Ok(bx.$int_red(args[0].immediate(), true)), + ty::Uint(_u) => Ok(bx.$int_red(args[0].immediate(), false)), + ty::Float(_f) => Ok(bx.$float_red(args[0].immediate())), + _ => return_error!( + "unsupported {} from `{}` with element `{}` to `{}`", + sym::$name, + in_ty, + in_elem, + ret_ty + ), + }; + } + }; + } + + minmax_red!(simd_reduce_min: vector_reduce_min, vector_reduce_fmin); + minmax_red!(simd_reduce_max: vector_reduce_max, vector_reduce_fmax); + + minmax_red!(simd_reduce_min_nanless: vector_reduce_min, vector_reduce_fmin_fast); + minmax_red!(simd_reduce_max_nanless: vector_reduce_max, vector_reduce_fmax_fast); + + macro_rules! bitwise_red { + ($name:ident : $red:ident, $boolean:expr) => { + if name == sym::$name { + let input = if !$boolean { + require!( + ret_ty == in_elem, + "expected return type `{}` (element of input `{}`), found `{}`", + in_elem, + in_ty, + ret_ty + ); + args[0].immediate() + } else { + match in_elem.kind() { + ty::Int(_) | ty::Uint(_) => {} + _ => return_error!( + "unsupported {} from `{}` with element `{}` to `{}`", + sym::$name, + in_ty, + in_elem, + ret_ty + ), + } + + // boolean reductions operate on vectors of i1s: + let i1 = bx.type_i1(); + let i1xn = bx.type_vector(i1, in_len as u64); + bx.trunc(args[0].immediate(), i1xn) + }; + return match in_elem.kind() { + ty::Int(_) | ty::Uint(_) => { + let r = bx.$red(input); + Ok(if !$boolean { r } else { bx.zext(r, bx.type_bool()) }) + } + _ => return_error!( + "unsupported {} from `{}` with element `{}` to `{}`", + sym::$name, + in_ty, + in_elem, + ret_ty + ), + }; + } + }; + } + + bitwise_red!(simd_reduce_and: vector_reduce_and, false); + bitwise_red!(simd_reduce_or: vector_reduce_or, false); + bitwise_red!(simd_reduce_xor: vector_reduce_xor, false); + bitwise_red!(simd_reduce_all: vector_reduce_and, true); + bitwise_red!(simd_reduce_any: vector_reduce_or, true); + + if name == sym::simd_cast { + require_simd!(ret_ty, "return"); + let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx()); + require!( + in_len == out_len, + "expected return type with length {} (same as input type `{}`), \ + found `{}` with length {}", + in_len, + in_ty, + ret_ty, + out_len + ); + // casting cares about nominal type, not just structural type + if in_elem == out_elem { + return Ok(args[0].immediate()); + } + + enum Style { + Float, + Int(/* is signed? */ bool), + Unsupported, + } + + let (in_style, in_width) = match in_elem.kind() { + // vectors of pointer-sized integers should've been + // disallowed before here, so this unwrap is safe. + ty::Int(i) => (Style::Int(true), i.bit_width().unwrap()), + ty::Uint(u) => (Style::Int(false), u.bit_width().unwrap()), + ty::Float(f) => (Style::Float, f.bit_width()), + _ => (Style::Unsupported, 0), + }; + let (out_style, out_width) = match out_elem.kind() { + ty::Int(i) => (Style::Int(true), i.bit_width().unwrap()), + ty::Uint(u) => (Style::Int(false), u.bit_width().unwrap()), + ty::Float(f) => (Style::Float, f.bit_width()), + _ => (Style::Unsupported, 0), + }; + + match (in_style, out_style) { + (Style::Int(in_is_signed), Style::Int(_)) => { + return Ok(match in_width.cmp(&out_width) { + Ordering::Greater => bx.trunc(args[0].immediate(), llret_ty), + Ordering::Equal => args[0].immediate(), + Ordering::Less => { + if in_is_signed { + bx.sext(args[0].immediate(), llret_ty) + } else { + bx.zext(args[0].immediate(), llret_ty) + } + } + }); + } + (Style::Int(in_is_signed), Style::Float) => { + return Ok(if in_is_signed { + bx.sitofp(args[0].immediate(), llret_ty) + } else { + bx.uitofp(args[0].immediate(), llret_ty) + }); + } + (Style::Float, Style::Int(out_is_signed)) => { + return Ok(if out_is_signed { + bx.fptosi(args[0].immediate(), llret_ty) + } else { + bx.fptoui(args[0].immediate(), llret_ty) + }); + } + (Style::Float, Style::Float) => { + return Ok(match in_width.cmp(&out_width) { + Ordering::Greater => bx.fptrunc(args[0].immediate(), llret_ty), + Ordering::Equal => args[0].immediate(), + Ordering::Less => bx.fpext(args[0].immediate(), llret_ty), + }); + } + _ => { /* Unsupported. Fallthrough. */ } + } + require!( + false, + "unsupported cast from `{}` with element `{}` to `{}` with element `{}`", + in_ty, + in_elem, + ret_ty, + out_elem + ); + }*/ + + macro_rules! arith_binary { + ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => { + $(if name == sym::$name { + match in_elem.kind() { + $($(ty::$p(_))|* => { + return Ok(bx.$call(args[0].immediate(), args[1].immediate())) + })* + _ => {}, + } + require!(false, + "unsupported operation on `{}` with element `{}`", + in_ty, + in_elem) + })* + } + } + + arith_binary! { + simd_add: Uint, Int => add, Float => fadd; + simd_sub: Uint, Int => sub, Float => fsub; + simd_mul: Uint, Int => mul, Float => fmul; + simd_div: Uint => udiv, Int => sdiv, Float => fdiv; + simd_rem: Uint => urem, Int => srem, Float => frem; + simd_shl: Uint, Int => shl; + simd_shr: Uint => lshr, Int => ashr; + simd_and: Uint, Int => and; + simd_or: Uint, Int => or; // FIXME: calling or might not work on vectors. + simd_xor: Uint, Int => xor; + /*simd_fmax: Float => maxnum; + simd_fmin: Float => minnum;*/ + } + + /*macro_rules! arith_unary { + ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => { + $(if name == sym::$name { + match in_elem.kind() { + $($(ty::$p(_))|* => { + return Ok(bx.$call(args[0].immediate())) + })* + _ => {}, + } + require!(false, + "unsupported operation on `{}` with element `{}`", + in_ty, + in_elem) + })* + } + } + + arith_unary! { + simd_neg: Int => neg, Float => fneg; + } + + if name == sym::simd_saturating_add || name == sym::simd_saturating_sub { + let lhs = args[0].immediate(); + let rhs = args[1].immediate(); + let is_add = name == sym::simd_saturating_add; + let ptr_bits = bx.tcx().data_layout.pointer_size.bits() as _; + let (signed, elem_width, elem_ty) = match *in_elem.kind() { + ty::Int(i) => (true, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_int_from_ty(i)), + ty::Uint(i) => (false, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_uint_from_ty(i)), + _ => { + return_error!( + "expected element type `{}` of vector type `{}` \ + to be a signed or unsigned integer type", + arg_tys[0].simd_size_and_type(bx.tcx()).1, + arg_tys[0] + ); + } + }; + let llvm_intrinsic = &format!( + "llvm.{}{}.sat.v{}i{}", + if signed { 's' } else { 'u' }, + if is_add { "add" } else { "sub" }, + in_len, + elem_width + ); + let vec_ty = bx.cx.type_vector(elem_ty, in_len as u64); + + let f = bx.declare_cfn( + &llvm_intrinsic, + bx.type_func(&[vec_ty, vec_ty], vec_ty), + ); + let v = bx.call(f, &[lhs, rhs], None); + return Ok(v); + }*/ + + unimplemented!("simd {}", name); + + //span_bug!(span, "unknown SIMD intrinsic"); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000000..797251814d7f4 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,342 @@ +/* + * TODO: support #[inline] attributes. + * TODO: support LTO. + * + * TODO: remove the local gccjit LD_LIBRARY_PATH in config.sh. + * TODO: remove the object dependency. + * TODO: remove the patches. + */ + +#![feature(rustc_private, decl_macro, associated_type_bounds, never_type, trusted_len)] +#![allow(broken_intra_doc_links)] +#![recursion_limit="256"] +#![warn(rust_2018_idioms)] +#![warn(unused_lifetimes)] + +/*extern crate flate2; +extern crate libc;*/ +extern crate rustc_ast; +extern crate rustc_codegen_ssa; +extern crate rustc_data_structures; +extern crate rustc_errors; +//extern crate rustc_fs_util; +extern crate rustc_hir; +extern crate rustc_metadata; +extern crate rustc_middle; +extern crate rustc_mir; +extern crate rustc_session; +extern crate rustc_span; +extern crate rustc_symbol_mangling; +extern crate rustc_target; +extern crate snap; + +// This prevents duplicating functions and statics that are already part of the host rustc process. +#[allow(unused_extern_crates)] +extern crate rustc_driver; + +mod abi; +mod allocator; +mod archive; +mod asm; +mod back; +mod base; +mod builder; +mod callee; +mod common; +mod consts; +mod context; +mod coverageinfo; +mod debuginfo; +mod declare; +mod intrinsic; +mod mangled_std_symbols; +mod mono_item; +mod type_; +mod type_of; +mod va_arg; + +use std::any::Any; +use std::sync::Arc; + +use gccjit::{Block, Context, FunctionType, OptimizationLevel}; +use rustc_ast::expand::allocator::AllocatorKind; +use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen}; +use rustc_codegen_ssa::base::codegen_crate; +use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryFn}; +use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; +use rustc_codegen_ssa::target_features::supported_target_features; +use rustc_codegen_ssa::traits::{CodegenBackend, ExtraBackendMethods, ModuleBufferMethods, ThinBufferMethods, WriteBackendMethods}; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::{ErrorReported, Handler}; +use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; +use rustc_middle::middle::cstore::EncodedMetadata; +use rustc_middle::ty::TyCtxt; +use rustc_session::config::{CrateType, Lto, OptLevel, OutputFilenames}; +use rustc_session::Session; +use rustc_span::Symbol; +use rustc_span::fatal_error::FatalError; + +use crate::context::unit_name; + +pub struct PrintOnPanic String>(pub F); + +impl String> Drop for PrintOnPanic { + fn drop(&mut self) { + if ::std::thread::panicking() { + println!("{}", (self.0)()); + } + } +} + +#[derive(Clone)] +pub struct GccCodegenBackend; + +impl CodegenBackend for GccCodegenBackend { + fn init(&self, sess: &Session) { + if sess.lto() != Lto::No { + sess.warn("LTO is not supported. You may get a linker error."); + } + } + + fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>, metadata: EncodedMetadata, need_metadata_module: bool) -> Box { + let target_cpu = target_cpu(tcx.sess); + let res = codegen_crate(self.clone(), tcx, target_cpu.to_string(), metadata, need_metadata_module); + + rustc_symbol_mangling::test::report_symbol_names(tcx); + + Box::new(res) + } + + fn join_codegen(&self, ongoing_codegen: Box, sess: &Session) -> Result<(CodegenResults, FxHashMap), ErrorReported> { + let (codegen_results, work_products) = ongoing_codegen + .downcast::>() + .expect("Expected GccCodegenBackend's OngoingCodegen, found Box") + .join(sess); + + Ok((codegen_results, work_products)) + } + + fn link(&self, sess: &Session, mut codegen_results: CodegenResults, outputs: &OutputFilenames) -> Result<(), ErrorReported> { + use rustc_codegen_ssa::back::link::link_binary; + if let Some(symbols) = codegen_results.crate_info.exported_symbols.get_mut(&CrateType::Dylib) { + // TODO: remove when global initializer work without calling a function at runtime. + // HACK: since this codegen add some symbols (e.g. __gccGlobalCrateInit) and the UI + // tests load libstd.so as a dynamic library, and rustc use a version-script to specify + // the symbols visibility, we add * to export all symbols. + // It seems other symbols from libstd/libcore are causing some issues here as well. + symbols.push("*".to_string()); + } + + link_binary::>( + sess, + &codegen_results, + outputs, + ) + } + + fn target_features(&self, sess: &Session) -> Vec { + target_features(sess) + } +} + +impl ExtraBackendMethods for GccCodegenBackend { + fn new_metadata<'tcx>(&self, _tcx: TyCtxt<'tcx>, _mod_name: &str) -> Self::Module { + GccContext { + context: Context::default(), + } + } + + fn write_compressed_metadata<'tcx>(&self, tcx: TyCtxt<'tcx>, metadata: &EncodedMetadata, gcc_module: &mut Self::Module) { + base::write_compressed_metadata(tcx, metadata, gcc_module) + } + + fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, mods: &mut Self::Module, kind: AllocatorKind, has_alloc_error_handler: bool) { + unsafe { allocator::codegen(tcx, mods, kind, has_alloc_error_handler) } + } + + fn compile_codegen_unit<'tcx>(&self, tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (ModuleCodegen, u64) { + base::compile_codegen_unit(tcx, cgu_name) + } + + fn target_machine_factory(&self, _sess: &Session, _opt_level: OptLevel) -> TargetMachineFactoryFn { + // TODO: set opt level. + Arc::new(|_| { + Ok(()) + }) + } + + fn target_cpu<'b>(&self, _sess: &'b Session) -> &'b str { + unimplemented!(); + } + + fn tune_cpu<'b>(&self, _sess: &'b Session) -> Option<&'b str> { + None + // TODO + //llvm_util::tune_cpu(sess) + } +} + +pub struct ModuleBuffer; + +impl ModuleBufferMethods for ModuleBuffer { + fn data(&self) -> &[u8] { + unimplemented!(); + } +} + +pub struct ThinBuffer; + +impl ThinBufferMethods for ThinBuffer { + fn data(&self) -> &[u8] { + unimplemented!(); + } +} + +pub struct GccContext { + context: Context<'static>, +} + +unsafe impl Send for GccContext {} +// FIXME: that shouldn't be Sync. Parallel compilation is currently disabled with "-Zno-parallel-llvm". Try to disable it here. +unsafe impl Sync for GccContext {} + +impl WriteBackendMethods for GccCodegenBackend { + type Module = GccContext; + type TargetMachine = (); + type ModuleBuffer = ModuleBuffer; + type Context = (); + type ThinData = (); + type ThinBuffer = ThinBuffer; + + fn run_fat_lto(_cgcx: &CodegenContext, mut modules: Vec>, _cached_modules: Vec<(SerializedModule, WorkProduct)>) -> Result, FatalError> { + // TODO: implement LTO by sending -flto to libgccjit and adding the appropriate gcc linker plugins. + // NOTE: implemented elsewhere. + let module = + match modules.remove(0) { + FatLTOInput::InMemory(module) => module, + FatLTOInput::Serialized { .. } => { + unimplemented!(); + /*info!("pushing serialized module {:?}", name); + let buffer = SerializedModule::Local(buffer); + serialized_modules.push((buffer, CString::new(name).unwrap()));*/ + } + }; + Ok(LtoModuleCodegen::Fat { module: Some(module), _serialized_bitcode: vec![] }) + } + + fn run_thin_lto(_cgcx: &CodegenContext, _modules: Vec<(String, Self::ThinBuffer)>, _cached_modules: Vec<(SerializedModule, WorkProduct)>) -> Result<(Vec>, Vec), FatalError> { + unimplemented!(); + } + + fn print_pass_timings(&self) { + unimplemented!(); + } + + unsafe fn optimize(_cgcx: &CodegenContext, _diag_handler: &Handler, module: &ModuleCodegen, config: &ModuleConfig) -> Result<(), FatalError> { + //if cgcx.lto == Lto::Fat { + //module.module_llvm.context.add_driver_option("-flto"); + //} + module.module_llvm.context.set_optimization_level(to_gcc_opt_level(config.opt_level)); + Ok(()) + } + + unsafe fn optimize_thin(_cgcx: &CodegenContext, _thin: &mut ThinModule) -> Result, FatalError> { + unimplemented!(); + } + + unsafe fn codegen(cgcx: &CodegenContext, diag_handler: &Handler, module: ModuleCodegen, config: &ModuleConfig) -> Result { + back::write::codegen(cgcx, diag_handler, module, config) + } + + fn prepare_thin(_module: ModuleCodegen) -> (String, Self::ThinBuffer) { + unimplemented!(); + } + + fn serialize_module(_module: ModuleCodegen) -> (String, Self::ModuleBuffer) { + unimplemented!(); + } + + fn run_lto_pass_manager(_cgcx: &CodegenContext, _module: &ModuleCodegen, _config: &ModuleConfig, _thin: bool) -> Result<(), FatalError> { + // TODO + Ok(()) + } + + fn run_link(cgcx: &CodegenContext, diag_handler: &Handler, modules: Vec>) -> Result, FatalError> { + back::write::link(cgcx, diag_handler, modules) + } +} + +/*fn target_triple(sess: &Session) -> target_lexicon::Triple { + sess.target.llvm_target.parse().unwrap() +}*/ + +/// This is the entrypoint for a hot plugged rustc_codegen_gccjit +#[no_mangle] +pub fn __rustc_codegen_backend() -> Box { + Box::new(GccCodegenBackend) +} + +fn to_gcc_opt_level(optlevel: Option) -> OptimizationLevel { + match optlevel { + None => OptimizationLevel::None, + Some(level) => { + match level { + OptLevel::No => OptimizationLevel::None, + OptLevel::Less => OptimizationLevel::Limited, + OptLevel::Default => OptimizationLevel::Standard, + OptLevel::Aggressive => OptimizationLevel::Aggressive, + OptLevel::Size | OptLevel::SizeMin => OptimizationLevel::Limited, + } + }, + } +} + +fn create_function_calling_initializers<'gcc, 'tcx>(tcx: TyCtxt<'tcx>, context: &Context<'gcc>, block: Block<'gcc>) { + let codegen_units = tcx.collect_and_partition_mono_items(()).1; + for codegen_unit in codegen_units { + let codegen_init_func = context.new_function(None, FunctionType::Extern, context.new_type::<()>(), &[], + &format!("__gccGlobalInit{}", unit_name(&codegen_unit)), false); + block.add_eval(None, context.new_call(None, codegen_init_func, &[])); + } +} + +fn handle_native(name: &str) -> &str { + if name != "native" { + return name; + } + + unimplemented!(); + /*unsafe { + let mut len = 0; + let ptr = llvm::LLVMRustGetHostCPUName(&mut len); + str::from_utf8(slice::from_raw_parts(ptr as *const u8, len)).unwrap() + }*/ +} + +pub fn target_cpu(sess: &Session) -> &str { + let name = sess.opts.cg.target_cpu.as_ref().unwrap_or(&sess.target.cpu); + handle_native(name) +} + +pub fn target_features(sess: &Session) -> Vec { + supported_target_features(sess) + .iter() + .filter_map( + |&(feature, gate)| { + if sess.is_nightly_build() || gate.is_none() { Some(feature) } else { None } + }, + ) + .filter(|_feature| { + /*if feature.starts_with("sse") { + return true; + }*/ + // TODO: implement a way to get enabled feature in libgccjit. + //println!("Feature: {}", feature); + /*let llvm_feature = to_llvm_feature(sess, feature); + let cstr = CString::new(llvm_feature).unwrap(); + unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) }*/ + false + }) + .map(|feature| Symbol::intern(feature)) + .collect() +} diff --git a/src/mangled_std_symbols.rs b/src/mangled_std_symbols.rs new file mode 100644 index 0000000000000..b0c3f214d66c1 --- /dev/null +++ b/src/mangled_std_symbols.rs @@ -0,0 +1,4 @@ +pub const ARGV_INIT_ARRAY: &str = "_ZN3std3sys4unix4args3imp15ARGV_INIT_ARRAY"; +pub const ARGV_INIT_WRAPPER: &str = "_ZN3std3sys4unix4args3imp15ARGV_INIT_ARRAY12init_wrapper"; +pub const ARGC: &str = "_ZN3std3sys4unix4args3imp4ARGC"; +pub const ARGV: &str = "_ZN3std3sys4unix4args3imp4ARGV"; diff --git a/src/mono_item.rs b/src/mono_item.rs new file mode 100644 index 0000000000000..c261efbbc559f --- /dev/null +++ b/src/mono_item.rs @@ -0,0 +1,59 @@ +use rustc_codegen_ssa::traits::PreDefineMethods; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::mir::mono::{Linkage, Visibility}; +use rustc_middle::ty::{self, Instance, TypeFoldable}; +use rustc_middle::ty::layout::FnAbiExt; +use rustc_span::def_id::DefId; +use rustc_target::abi::LayoutOf; +use rustc_target::abi::call::FnAbi; + +use crate::base; +use crate::context::CodegenCx; +use crate::type_of::LayoutGccExt; + +impl<'gcc, 'tcx> PreDefineMethods<'tcx> for CodegenCx<'gcc, 'tcx> { + fn predefine_static(&self, def_id: DefId, _linkage: Linkage, _visibility: Visibility, symbol_name: &str) { + let attrs = self.tcx.codegen_fn_attrs(def_id); + let instance = Instance::mono(self.tcx, def_id); + let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all()); + let gcc_type = self.layout_of(ty).gcc_type(self, true); + + let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL); + let global = self.define_global(symbol_name, gcc_type, is_tls, attrs.link_section).unwrap_or_else(|| { + self.sess().span_fatal( + self.tcx.def_span(def_id), + &format!("symbol `{}` is already defined", symbol_name), + ) + }); + + // TODO + /*unsafe { + llvm::LLVMRustSetLinkage(global, base::linkage_to_llvm(linkage)); + llvm::LLVMRustSetVisibility(global, base::visibility_to_llvm(visibility)); + }*/ + + self.instances.borrow_mut().insert(instance, global); + } + + fn predefine_fn(&self, instance: Instance<'tcx>, linkage: Linkage, _visibility: Visibility, symbol_name: &str) { + assert!(!instance.substs.needs_infer() && !instance.substs.has_param_types_or_consts()); + + let fn_abi = FnAbi::of_instance(self, instance, &[]); + self.linkage.set(base::linkage_to_gcc(linkage)); + let _decl = self.declare_fn(symbol_name, &fn_abi); + //let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); + + // TODO: call set_link_section() to allow initializing argc/argv. + //base::set_link_section(decl, &attrs); + /*if linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR { + llvm::SetUniqueComdat(self.llmod, decl); + }*/ + + //debug!("predefine_fn: instance = {:?}", instance); + + // TODO: use inline attribute from there in linkage.set() above: + //attributes::from_fn_attrs(self, decl, instance); + + //self.instances.borrow_mut().insert(instance, decl); + } +} diff --git a/src/type_.rs b/src/type_.rs new file mode 100644 index 0000000000000..90f7d9d9bbace --- /dev/null +++ b/src/type_.rs @@ -0,0 +1,355 @@ +use std::convert::TryInto; + +use gccjit::{RValue, Struct, Type}; +use rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods}; +use rustc_codegen_ssa::common::TypeKind; +use rustc_middle::bug; +use rustc_middle::ty::layout::TyAndLayout; +use rustc_target::abi::{AddressSpace, Align, Integer, Size}; + +use crate::common::TypeReflection; +use crate::context::CodegenCx; +use crate::type_of::LayoutGccExt; + +impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { + pub fn type_ix(&self, num_bits: u64) -> Type<'gcc> { + // gcc only supports 1, 2, 4 or 8-byte integers. + let bytes = (num_bits / 8).next_power_of_two() as i32; + match bytes { + 1 => self.i8_type, + 2 => self.i16_type, + 4 => self.i32_type, + 8 => self.i64_type, + 16 => self.i128_type, + _ => panic!("unexpected num_bits: {}", num_bits), + } + /* + let bytes = (num_bits / 8).next_power_of_two() as i32; + println!("num_bits: {}, bytes: {}", num_bits, bytes); + self.context.new_int_type(bytes, true) // TODO: check if it is indeed a signed integer. + */ + } + + /*pub fn type_bool(&self) -> Type<'gcc> { + self.bool_type + }*/ + + pub fn type_void(&self) -> Type<'gcc> { + self.context.new_type::<()>() + } + + pub fn type_size_t(&self) -> Type<'gcc> { + self.context.new_type::() + } + + pub fn type_u8(&self) -> Type<'gcc> { + self.u8_type + } + + pub fn type_u16(&self) -> Type<'gcc> { + self.u16_type + } + + pub fn type_u32(&self) -> Type<'gcc> { + self.u32_type + } + + pub fn type_u64(&self) -> Type<'gcc> { + self.u64_type + } + + pub fn type_u128(&self) -> Type<'gcc> { + self.u128_type + } + + pub fn type_pointee_for_align(&self, align: Align) -> Type<'gcc> { + // FIXME(eddyb) We could find a better approximation if ity.align < align. + let ity = Integer::approximate_align(self, align); + self.type_from_integer(ity) + } + + /*pub fn type_int_from_ty(&self, t: ty::IntTy) -> Type<'gcc> { + match t { + ty::IntTy::Isize => self.type_isize(), + ty::IntTy::I8 => self.type_i8(), + ty::IntTy::I16 => self.type_i16(), + ty::IntTy::I32 => self.type_i32(), + ty::IntTy::I64 => self.type_i64(), + ty::IntTy::I128 => self.type_i128(), + } + } + + pub fn type_uint_from_ty(&self, t: ty::UintTy) -> Type<'gcc> { + match t { + ty::UintTy::Usize => self.type_isize(), + ty::UintTy::U8 => self.type_i8(), + ty::UintTy::U16 => self.type_i16(), + ty::UintTy::U32 => self.type_i32(), + ty::UintTy::U64 => self.type_i64(), + ty::UintTy::U128 => self.type_i128(), + } + } + + pub fn type_float_from_ty(&self, t: ty::FloatTy) -> Type<'gcc> { + match t { + ty::FloatTy::F32 => self.type_f32(), + ty::FloatTy::F64 => self.type_f64(), + } + } + + pub fn type_vector(&self, ty: Type<'gcc>, len: u64) -> Type<'gcc> { + self.context.new_vector_type(ty, len) + }*/ +} + +impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> { + fn type_i1(&self) -> Type<'gcc> { + self.bool_type + } + + fn type_i8(&self) -> Type<'gcc> { + self.i8_type + } + + fn type_i16(&self) -> Type<'gcc> { + self.i16_type + } + + fn type_i32(&self) -> Type<'gcc> { + self.i32_type + } + + fn type_i64(&self) -> Type<'gcc> { + self.i64_type + } + + fn type_i128(&self) -> Type<'gcc> { + self.i128_type + } + + fn type_isize(&self) -> Type<'gcc> { + self.isize_type + } + + fn type_f32(&self) -> Type<'gcc> { + self.context.new_type::() + } + + fn type_f64(&self) -> Type<'gcc> { + self.context.new_type::() + } + + fn type_func(&self, params: &[Type<'gcc>], return_type: Type<'gcc>) -> Type<'gcc> { + self.context.new_function_pointer_type(None, return_type, params, false) + } + + fn type_struct(&self, fields: &[Type<'gcc>], _packed: bool) -> Type<'gcc> { + let types = fields.to_vec(); + if let Some(typ) = self.struct_types.borrow().get(fields) { + return typ.clone(); + } + let fields: Vec<_> = fields.iter().enumerate() + .map(|(index, field)| self.context.new_field(None, *field, &format!("field{}_TODO", index))) + .collect(); + // TODO: use packed. + //let name = types.iter().map(|typ| format!("{:?}", typ)).collect::>().join("_"); + //let typ = self.context.new_struct_type(None, format!("struct{}", name), &fields).as_type(); + let typ = self.context.new_struct_type(None, "struct", &fields).as_type(); + self.struct_types.borrow_mut().insert(types, typ); + typ + } + + fn type_kind(&self, typ: Type<'gcc>) -> TypeKind { + if typ.is_integral() { + TypeKind::Integer + } + else if typ.is_vector().is_some() { + TypeKind::Vector + } + else { + // TODO + TypeKind::Void + } + } + + fn type_ptr_to(&self, ty: Type<'gcc>) -> Type<'gcc> { + // TODO + /*assert_ne!(self.type_kind(ty), TypeKind::Function, + "don't call ptr_to on function types, use ptr_to_gcc_type on FnAbi instead" + );*/ + ty.make_pointer() + } + + fn type_ptr_to_ext(&self, ty: Type<'gcc>, _address_space: AddressSpace) -> Type<'gcc> { + // TODO: use address_space + ty.make_pointer() + } + + fn element_type(&self, ty: Type<'gcc>) -> Type<'gcc> { + if let Some(typ) = ty.is_array() { + typ + } + else if let Some(vector_type) = ty.is_vector() { + vector_type.get_element_type() + } + else if let Some(typ) = ty.get_pointee() { + typ + } + else { + unreachable!() + } + } + + fn vector_length(&self, _ty: Type<'gcc>) -> usize { + unimplemented!(); + //unsafe { llvm::LLVMGetVectorSize(ty) as usize } + } + + fn float_width(&self, typ: Type<'gcc>) -> usize { + let f32 = self.context.new_type::(); + let f64 = self.context.new_type::(); + if typ == f32 { + 32 + } + else if typ == f64 { + 64 + } + else { + panic!("Cannot get width of float type {:?}", typ); + } + // TODO: support other sizes. + /*match self.type_kind(ty) { + TypeKind::Float => 32, + TypeKind::Double => 64, + TypeKind::X86_FP80 => 80, + TypeKind::FP128 | TypeKind::PPC_FP128 => 128, + _ => bug!("llvm_float_width called on a non-float type"), + }*/ + } + + fn int_width(&self, typ: Type<'gcc>) -> u64 { + if typ.is_i8(self) || typ.is_u8(self) { + 8 + } + else if typ.is_i16(self) || typ.is_u16(self) { + 16 + } + else if typ.is_i32(self) || typ.is_u32(self) { + 32 + } + else if typ.is_i64(self) || typ.is_u64(self) { + 64 + } + else if typ.is_i128(self) || typ.is_u128(self) { + 128 + } + else { + panic!("Cannot get width of int type {:?}", typ); + } + } + + fn val_ty(&self, value: RValue<'gcc>) -> Type<'gcc> { + value.get_type() + } +} + +impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { + pub fn type_padding_filler(&self, size: Size, align: Align) -> Type<'gcc> { + let unit = Integer::approximate_align(self, align); + let size = size.bytes(); + let unit_size = unit.size().bytes(); + assert_eq!(size % unit_size, 0); + self.type_array(self.type_from_integer(unit), size / unit_size) + } + + pub fn set_struct_body(&self, typ: Struct<'gcc>, fields: &[Type<'gcc>], _packed: bool) { + // TODO: use packed. + let fields: Vec<_> = fields.iter().enumerate() + .map(|(index, field)| self.context.new_field(None, *field, &format!("field_{}", index))) + .collect(); + typ.set_fields(None, &fields); + } + + /*fn type_struct(&self, fields: &[Type<'gcc>], packed: bool) -> Type<'gcc> { + // TODO: use packed. + let fields: Vec<_> = fields.iter().enumerate() + .map(|(index, field)| self.context.new_field(None, *field, &format!("field_{}", index))) + .collect(); + return self.context.new_struct_type(None, "unnamedStruct", &fields).as_type(); + }*/ + + pub fn type_named_struct(&self, name: &str) -> Struct<'gcc> { + self.context.new_opaque_struct_type(None, name) + } + + pub fn type_array(&self, ty: Type<'gcc>, mut len: u64) -> Type<'gcc> { + if let Some(struct_type) = ty.is_struct() { + if struct_type.get_field_count() == 0 { + // NOTE: since gccjit only supports i32 for the array size and libcore's tests uses a + // size of usize::MAX in test_binary_search, we workaround this by setting the size to + // zero for ZSTs. + // FIXME: fix gccjit API. + len = 0; + } + } + + let len: i32 = len.try_into().expect("array len"); + + self.context.new_array_type(None, ty, len) + } +} + +pub fn struct_fields<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout<'tcx>) -> (Vec>, bool) { + //debug!("struct_fields: {:#?}", layout); + let field_count = layout.fields.count(); + + let mut packed = false; + let mut offset = Size::ZERO; + let mut prev_effective_align = layout.align.abi; + let mut result: Vec<_> = Vec::with_capacity(1 + field_count * 2); + for i in layout.fields.index_by_increasing_offset() { + let target_offset = layout.fields.offset(i as usize); + let field = layout.field(cx, i); + let effective_field_align = + layout.align.abi.min(field.align.abi).restrict_for_offset(target_offset); + packed |= effective_field_align < field.align.abi; + + /*debug!( + "struct_fields: {}: {:?} offset: {:?} target_offset: {:?} \ + effective_field_align: {}", + i, + field, + offset, + target_offset, + effective_field_align.bytes() + );*/ + assert!(target_offset >= offset); + let padding = target_offset - offset; + let padding_align = prev_effective_align.min(effective_field_align); + assert_eq!(offset.align_to(padding_align) + padding, target_offset); + result.push(cx.type_padding_filler(padding, padding_align)); + //debug!(" padding before: {:?}", padding); + + result.push(field.gcc_type(cx, !field.ty.is_any_ptr())); // FIXME: might need to check if the type is inside another, like Box. + offset = target_offset + field.size; + prev_effective_align = effective_field_align; + } + if !layout.is_unsized() && field_count > 0 { + if offset > layout.size { + bug!("layout: {:#?} stride: {:?} offset: {:?}", layout, layout.size, offset); + } + let padding = layout.size - offset; + let padding_align = prev_effective_align; + assert_eq!(offset.align_to(padding_align) + padding, layout.size); + /*debug!( + "struct_fields: pad_bytes: {:?} offset: {:?} stride: {:?}", + padding, offset, layout.size + );*/ + result.push(cx.type_padding_filler(padding, padding_align)); + assert_eq!(result.len(), 1 + field_count * 2); + } else { + //debug!("struct_fields: offset: {:?} stride: {:?}", offset, layout.size); + } + + (result, packed) +} diff --git a/src/type_of.rs b/src/type_of.rs new file mode 100644 index 0000000000000..c5db0a1b2e459 --- /dev/null +++ b/src/type_of.rs @@ -0,0 +1,366 @@ +use std::fmt::Write; + +use gccjit::{Struct, Type}; +use crate::rustc_codegen_ssa::traits::{BaseTypeMethods, DerivedTypeMethods, LayoutTypeMethods}; +use rustc_middle::bug; +use rustc_middle::ty::{self, Ty, TypeFoldable}; +use rustc_middle::ty::layout::{FnAbiExt, TyAndLayout}; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_target::abi::{self, Abi, F32, F64, FieldsShape, Int, Integer, LayoutOf, Pointer, PointeeInfo, Size, TyAndLayoutMethods, Variants}; +use rustc_target::abi::call::{CastTarget, FnAbi, Reg}; + +use crate::abi::{FnAbiGccExt, GccType}; +use crate::context::CodegenCx; +use crate::type_::struct_fields; + +impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { + fn type_from_unsigned_integer(&self, i: Integer) -> Type<'gcc> { + use Integer::*; + match i { + I8 => self.type_u8(), + I16 => self.type_u16(), + I32 => self.type_u32(), + I64 => self.type_u64(), + I128 => self.type_u128(), + } + } +} + +pub fn uncached_gcc_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout<'tcx>, defer: &mut Option<(Struct<'gcc>, TyAndLayout<'tcx>)>) -> Type<'gcc> { + match layout.abi { + Abi::Scalar(_) => bug!("handled elsewhere"), + Abi::Vector { ref element, count } => { + let element = layout.scalar_gcc_type_at(cx, element, Size::ZERO); + return cx.context.new_vector_type(element, count); + }, + Abi::ScalarPair(..) => { + return cx.type_struct( + &[ + layout.scalar_pair_element_gcc_type(cx, 0, false), + layout.scalar_pair_element_gcc_type(cx, 1, false), + ], + false, + ); + } + Abi::Uninhabited | Abi::Aggregate { .. } => {} + } + + let name = match layout.ty.kind() { + // FIXME(eddyb) producing readable type names for trait objects can result + // in problematically distinct types due to HRTB and subtyping (see #47638). + // ty::Dynamic(..) | + ty::Adt(..) | ty::Closure(..) | ty::Foreign(..) | ty::Generator(..) | ty::Str + if !cx.sess().fewer_names() => + { + let mut name = with_no_trimmed_paths(|| layout.ty.to_string()); + if let (&ty::Adt(def, _), &Variants::Single { index }) = + (layout.ty.kind(), &layout.variants) + { + if def.is_enum() && !def.variants.is_empty() { + write!(&mut name, "::{}", def.variants[index].ident).unwrap(); + } + } + if let (&ty::Generator(_, _, _), &Variants::Single { index }) = + (layout.ty.kind(), &layout.variants) + { + write!(&mut name, "::{}", ty::GeneratorSubsts::variant_name(index)).unwrap(); + } + Some(name) + } + ty::Adt(..) => { + // If `Some` is returned then a named struct is created in LLVM. Name collisions are + // avoided by LLVM (with increasing suffixes). If rustc doesn't generate names then that + // can improve perf. + // FIXME: I don't think that's true for libgccjit. + Some(String::new()) + } + _ => None, + }; + + match layout.fields { + FieldsShape::Primitive | FieldsShape::Union(_) => { + let fill = cx.type_padding_filler(layout.size, layout.align.abi); + let packed = false; + match name { + None => cx.type_struct(&[fill], packed), + Some(ref name) => { + let gcc_type = cx.type_named_struct(name); + cx.set_struct_body(gcc_type, &[fill], packed); + gcc_type.as_type() + }, + } + } + FieldsShape::Array { count, .. } => cx.type_array(layout.field(cx, 0).gcc_type(cx, true), count), + FieldsShape::Arbitrary { .. } => + match name { + None => { + let (gcc_fields, packed) = struct_fields(cx, layout); + cx.type_struct(&gcc_fields, packed) + }, + Some(ref name) => { + let gcc_type = cx.type_named_struct(name); + *defer = Some((gcc_type, layout)); + gcc_type.as_type() + }, + }, + } +} + +pub trait LayoutGccExt<'tcx> { + fn is_gcc_immediate(&self) -> bool; + fn is_gcc_scalar_pair(&self) -> bool; + fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, set_fields: bool) -> Type<'gcc>; + fn immediate_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>; + fn scalar_gcc_type_at<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, scalar: &abi::Scalar, offset: Size) -> Type<'gcc>; + fn scalar_pair_element_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, index: usize, immediate: bool) -> Type<'gcc>; + fn gcc_field_index(&self, index: usize) -> u64; + fn pointee_info_at<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, offset: Size) -> Option; +} + +impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { + fn is_gcc_immediate(&self) -> bool { + match self.abi { + Abi::Scalar(_) | Abi::Vector { .. } => true, + Abi::ScalarPair(..) => false, + Abi::Uninhabited | Abi::Aggregate { .. } => self.is_zst(), + } + } + + fn is_gcc_scalar_pair(&self) -> bool { + match self.abi { + Abi::ScalarPair(..) => true, + Abi::Uninhabited | Abi::Scalar(_) | Abi::Vector { .. } | Abi::Aggregate { .. } => false, + } + } + + /// Gets the GCC type corresponding to a Rust type, i.e., `rustc_middle::ty::Ty`. + /// The pointee type of the pointer in `PlaceRef` is always this type. + /// For sized types, it is also the right LLVM type for an `alloca` + /// containing a value of that type, and most immediates (except `bool`). + /// Unsized types, however, are represented by a "minimal unit", e.g. + /// `[T]` becomes `T`, while `str` and `Trait` turn into `i8` - this + /// is useful for indexing slices, as `&[T]`'s data pointer is `T*`. + /// If the type is an unsized struct, the regular layout is generated, + /// with the inner-most trailing unsized field using the "minimal unit" + /// of that field's type - this is useful for taking the address of + /// that field and ensuring the struct has the right alignment. + fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, set_fields: bool) -> Type<'gcc> { + if let Abi::Scalar(ref scalar) = self.abi { + // Use a different cache for scalars because pointers to DSTs + // can be either fat or thin (data pointers of fat pointers). + if let Some(&ty) = cx.scalar_types.borrow().get(&self.ty) { + return ty; + } + let ty = + match *self.ty.kind() { + ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { + cx.type_ptr_to(cx.layout_of(ty).gcc_type(cx, set_fields)) + } + ty::Adt(def, _) if def.is_box() => { + cx.type_ptr_to(cx.layout_of(self.ty.boxed_ty()).gcc_type(cx, true)) + } + ty::FnPtr(sig) => cx.fn_ptr_backend_type(&FnAbi::of_fn_ptr(cx, sig, &[])), + _ => self.scalar_gcc_type_at(cx, scalar, Size::ZERO), + }; + cx.scalar_types.borrow_mut().insert(self.ty, ty); + return ty; + } + + // Check the cache. + let variant_index = + match self.variants { + Variants::Single { index } => Some(index), + _ => None, + }; + let cached_type = cx.types.borrow().get(&(self.ty, variant_index)).cloned(); + if let Some(ty) = cached_type { + let type_to_set_fields = cx.types_with_fields_to_set.borrow_mut().remove(&ty); + if let Some((struct_type, layout)) = type_to_set_fields { + // Since we might be trying to generate a type containing another type which is not + // completely generated yet, we deferred setting the fields until now. + let (fields, packed) = struct_fields(cx, layout); + cx.set_struct_body(struct_type, &fields, packed); + } + return ty; + } + + //debug!("gcc_type({:#?})", self); + + assert!(!self.ty.has_escaping_bound_vars(), "{:?} has escaping bound vars", self.ty); + + // Make sure lifetimes are erased, to avoid generating distinct LLVM + // types for Rust types that only differ in the choice of lifetimes. + let normal_ty = cx.tcx.erase_regions(self.ty); + + let mut defer = None; + let ty = + if self.ty != normal_ty { + let mut layout = cx.layout_of(normal_ty); + if let Some(v) = variant_index { + layout = layout.for_variant(cx, v); + } + layout.gcc_type(cx, true) + } + else { + uncached_gcc_type(cx, *self, &mut defer) + }; + //debug!("--> mapped {:#?} to ty={:?}", self, ty); + + cx.types.borrow_mut().insert((self.ty, variant_index), ty); + + if let Some((ty, layout)) = defer { + //TODO: do we still need this conditions and the set_fields parameter? + //if set_fields { + let (fields, packed) = struct_fields(cx, layout); + cx.set_struct_body(ty, &fields, packed); + /*} + else { + // Since we might be trying to generate a type containing another type which is not + // completely generated yet, we don't set the fields right now, but we save the + // type to set the fields later. + cx.types_with_fields_to_set.borrow_mut().insert(ty.as_type(), (ty, layout)); + }*/ + } + + ty + } + + fn immediate_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc> { + if let Abi::Scalar(ref scalar) = self.abi { + if scalar.is_bool() { + return cx.type_i1(); + } + } + self.gcc_type(cx, true) + } + + fn scalar_gcc_type_at<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, scalar: &abi::Scalar, offset: Size) -> Type<'gcc> { + match scalar.value { + Int(i, true) => cx.type_from_integer(i), + Int(i, false) => cx.type_from_unsigned_integer(i), + F32 => cx.type_f32(), + F64 => cx.type_f64(), + Pointer => { + // If we know the alignment, pick something better than i8. + let pointee = + if let Some(pointee) = self.pointee_info_at(cx, offset) { + cx.type_pointee_for_align(pointee.align) + } + else { + cx.type_i8() + }; + cx.type_ptr_to(pointee) + } + } + } + + fn scalar_pair_element_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, index: usize, immediate: bool) -> Type<'gcc> { + // TODO: remove llvm hack: + // HACK(eddyb) special-case fat pointers until LLVM removes + // pointee types, to avoid bitcasting every `OperandRef::deref`. + match self.ty.kind() { + ty::Ref(..) | ty::RawPtr(_) => { + return self.field(cx, index).gcc_type(cx, true); + } + ty::Adt(def, _) if def.is_box() => { + let ptr_ty = cx.tcx.mk_mut_ptr(self.ty.boxed_ty()); + return cx.layout_of(ptr_ty).scalar_pair_element_gcc_type(cx, index, immediate); + } + _ => {} + } + + let (a, b) = match self.abi { + Abi::ScalarPair(ref a, ref b) => (a, b), + _ => bug!("TyAndLayout::scalar_pair_element_llty({:?}): not applicable", self), + }; + let scalar = [a, b][index]; + + // Make sure to return the same type `immediate_gcc_type` would when + // dealing with an immediate pair. This means that `(bool, bool)` is + // effectively represented as `{i8, i8}` in memory and two `i1`s as an + // immediate, just like `bool` is typically `i8` in memory and only `i1` + // when immediate. We need to load/store `bool` as `i8` to avoid + // crippling LLVM optimizations or triggering other LLVM bugs with `i1`. + // TODO: this bugs certainly don't happen in this case since the bool type is used instead of i1. + if /*immediate &&*/ scalar.is_bool() { + return cx.type_i1(); + } + + let offset = + if index == 0 { + Size::ZERO + } + else { + a.value.size(cx).align_to(b.value.align(cx).abi) + }; + self.scalar_gcc_type_at(cx, scalar, offset) + } + + fn gcc_field_index(&self, index: usize) -> u64 { + match self.abi { + Abi::Scalar(_) | Abi::ScalarPair(..) => { + bug!("TyAndLayout::gcc_field_index({:?}): not applicable", self) + } + _ => {} + } + match self.fields { + FieldsShape::Primitive | FieldsShape::Union(_) => { + bug!("TyAndLayout::gcc_field_index({:?}): not applicable", self) + } + + FieldsShape::Array { .. } => index as u64, + + FieldsShape::Arbitrary { .. } => 1 + (self.fields.memory_index(index) as u64) * 2, + } + } + + fn pointee_info_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, offset: Size) -> Option { + if let Some(&pointee) = cx.pointee_infos.borrow().get(&(self.ty, offset)) { + return pointee; + } + + let result = Ty::pointee_info_at(*self, cx, offset); + + cx.pointee_infos.borrow_mut().insert((self.ty, offset), result); + result + } +} + +impl<'gcc, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> { + fn backend_type(&self, layout: TyAndLayout<'tcx>) -> Type<'gcc> { + layout.gcc_type(self, true) + } + + fn immediate_backend_type(&self, layout: TyAndLayout<'tcx>) -> Type<'gcc> { + layout.immediate_gcc_type(self) + } + + fn is_backend_immediate(&self, layout: TyAndLayout<'tcx>) -> bool { + layout.is_gcc_immediate() + } + + fn is_backend_scalar_pair(&self, layout: TyAndLayout<'tcx>) -> bool { + layout.is_gcc_scalar_pair() + } + + fn backend_field_index(&self, layout: TyAndLayout<'tcx>, index: usize) -> u64 { + layout.gcc_field_index(index) + } + + fn scalar_pair_element_backend_type(&self, layout: TyAndLayout<'tcx>, index: usize, immediate: bool) -> Type<'gcc> { + layout.scalar_pair_element_gcc_type(self, index, immediate) + } + + fn cast_backend_type(&self, ty: &CastTarget) -> Type<'gcc> { + ty.gcc_type(self) + } + + fn fn_ptr_backend_type(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Type<'gcc> { + fn_abi.ptr_to_gcc_type(self) + } + + fn reg_backend_type(&self, _ty: &Reg) -> Type<'gcc> { + unimplemented!(); + //ty.gcc_type(self) + } +} diff --git a/src/va_arg.rs b/src/va_arg.rs new file mode 100644 index 0000000000000..404a169d39ac9 --- /dev/null +++ b/src/va_arg.rs @@ -0,0 +1,179 @@ +/*use gccjit::{RValue, ToRValue, Type}; +use rustc_codegen_ssa::mir::operand::OperandRef; +use rustc_codegen_ssa::{ + common::IntPredicate, + traits::{BaseTypeMethods, BuilderMethods, ConstMethods, DerivedTypeMethods}, +}; +use rustc_middle::ty::layout::HasTyCtxt; +use rustc_middle::ty::Ty; +use rustc_target::abi::{Align, Endian, HasDataLayout, LayoutOf, Size}; + +use crate::builder::Builder; +use crate::type_of::LayoutGccExt; + +fn round_pointer_up_to_alignment<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, addr: RValue<'gcc>, align: Align, ptr_ty: Type<'gcc>) -> RValue<'gcc> { + let mut ptr_as_int = bx.ptrtoint(addr, bx.cx().type_isize()); + ptr_as_int = bx.add(ptr_as_int, bx.cx().const_i32(align.bytes() as i32 - 1)); + ptr_as_int = bx.and(ptr_as_int, bx.cx().const_i32(-(align.bytes() as i32))); + bx.inttoptr(ptr_as_int, ptr_ty) +} + +fn emit_direct_ptr_va_arg<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, list: OperandRef<'tcx, RValue<'gcc>>, llty: Type<'gcc>, size: Size, align: Align, slot_size: Align, allow_higher_align: bool) -> (RValue<'gcc>, Align) { + let va_list_ptr_ty = bx.cx().type_ptr_to(bx.cx.type_i8p()); + let va_list_addr = + if list.layout.gcc_type(bx.cx, true) != va_list_ptr_ty { + bx.bitcast(list.immediate(), va_list_ptr_ty) + } + else { + list.immediate() + }; + + let ptr = bx.load(va_list_addr, bx.tcx().data_layout.pointer_align.abi); + + let (addr, addr_align) = if allow_higher_align && align > slot_size { + (round_pointer_up_to_alignment(bx, ptr, align, bx.cx().type_i8p()), align) + } else { + (ptr, slot_size) + }; + + let aligned_size = size.align_to(slot_size).bytes() as i32; + let full_direct_size = bx.cx().const_i32(aligned_size); + let next = bx.inbounds_gep(addr, &[full_direct_size]); + bx.store(next, va_list_addr, bx.tcx().data_layout.pointer_align.abi); + + if size.bytes() < slot_size.bytes() && bx.tcx().sess.target.endian == Endian::Big { + let adjusted_size = bx.cx().const_i32((slot_size.bytes() - size.bytes()) as i32); + let adjusted = bx.inbounds_gep(addr, &[adjusted_size]); + (bx.bitcast(adjusted, bx.cx().type_ptr_to(llty)), addr_align) + } else { + (bx.bitcast(addr, bx.cx().type_ptr_to(llty)), addr_align) + } +} + +fn emit_ptr_va_arg<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, list: OperandRef<'tcx, RValue<'gcc>>, target_ty: Ty<'tcx>, indirect: bool, slot_size: Align, allow_higher_align: bool) -> RValue<'gcc> { + let layout = bx.cx.layout_of(target_ty); + let (llty, size, align) = + if indirect { + ( + bx.cx.layout_of(bx.cx.tcx.mk_imm_ptr(target_ty)).gcc_type(bx.cx, true), + bx.cx.data_layout().pointer_size, + bx.cx.data_layout().pointer_align, + ) + } + else { + (layout.gcc_type(bx.cx, true), layout.size, layout.align) + }; + let (addr, addr_align) = emit_direct_ptr_va_arg(bx, list, llty, size, align.abi, slot_size, allow_higher_align); + if indirect { + let tmp_ret = bx.load(addr, addr_align); + bx.load(tmp_ret, align.abi) + } + else { + bx.load(addr, addr_align) + } +} + +fn emit_aapcs_va_arg<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, list: OperandRef<'tcx, RValue<'gcc>>, target_ty: Ty<'tcx>) -> RValue<'gcc> { + // Implementation of the AAPCS64 calling convention for va_args see + // https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst + let va_list_addr = list.immediate(); + let layout = bx.cx.layout_of(target_ty); + let gcc_type = layout.immediate_gcc_type(bx); + + let function = bx.llbb().get_function(); + let variable = function.new_local(None, gcc_type, "va_arg"); + + let mut maybe_reg = bx.build_sibling_block("va_arg.maybe_reg"); + let mut in_reg = bx.build_sibling_block("va_arg.in_reg"); + let mut on_stack = bx.build_sibling_block("va_arg.on_stack"); + let end = bx.build_sibling_block("va_arg.end"); + let zero = bx.const_i32(0); + let offset_align = Align::from_bytes(4).unwrap(); + assert!(bx.tcx().sess.target.endian == Endian::Little); + + let gr_type = target_ty.is_any_ptr() || target_ty.is_integral(); + let (reg_off, reg_top_index, slot_size) = if gr_type { + let gr_offs = bx.struct_gep(va_list_addr, 7); + let nreg = (layout.size.bytes() + 7) / 8; + (gr_offs, 3, nreg * 8) + } else { + let vr_off = bx.struct_gep(va_list_addr, 9); + let nreg = (layout.size.bytes() + 15) / 16; + (vr_off, 5, nreg * 16) + }; + + // if the offset >= 0 then the value will be on the stack + let mut reg_off_v = bx.load(reg_off, offset_align); + let use_stack = bx.icmp(IntPredicate::IntSGE, reg_off_v, zero); + bx.cond_br(use_stack, on_stack.llbb(), maybe_reg.llbb()); + + // The value at this point might be in a register, but there is a chance that + // it could be on the stack so we have to update the offset and then check + // the offset again. + + if gr_type && layout.align.abi.bytes() > 8 { + reg_off_v = maybe_reg.add(reg_off_v, bx.const_i32(15)); + reg_off_v = maybe_reg.and(reg_off_v, bx.const_i32(-16)); + } + let new_reg_off_v = maybe_reg.add(reg_off_v, bx.const_i32(slot_size as i32)); + + maybe_reg.store(new_reg_off_v, reg_off, offset_align); + + // Check to see if we have overflowed the registers as a result of this. + // If we have then we need to use the stack for this value + let use_stack = maybe_reg.icmp(IntPredicate::IntSGT, new_reg_off_v, zero); + maybe_reg.cond_br(use_stack, on_stack.llbb(), in_reg.llbb()); + + let top = in_reg.struct_gep(va_list_addr, reg_top_index); + let top = in_reg.load(top, bx.tcx().data_layout.pointer_align.abi); + + // reg_value = *(@top + reg_off_v); + let top = in_reg.gep(top, &[reg_off_v]); + let top = in_reg.bitcast(top, bx.cx.type_ptr_to(layout.gcc_type(bx, true))); + let reg_value = in_reg.load(top, layout.align.abi); + in_reg.assign(variable, reg_value); + in_reg.br(end.llbb()); + + // On Stack block + let stack_value = + emit_ptr_va_arg(&mut on_stack, list, target_ty, false, Align::from_bytes(8).unwrap(), true); + on_stack.assign(variable, stack_value); + on_stack.br(end.llbb()); + + *bx = end; + variable.to_rvalue() +} + +pub(super) fn emit_va_arg<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, addr: OperandRef<'tcx, RValue<'gcc>>, target_ty: Ty<'tcx>) -> RValue<'gcc> { + // Determine the va_arg implementation to use. The LLVM va_arg instruction + // is lacking in some instances, so we should only use it as a fallback. + let target = &bx.cx.tcx.sess.target; + let arch = &bx.cx.tcx.sess.target.arch; + match &**arch { + // Windows x86 + "x86" if target.options.is_like_windows => { + emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(4).unwrap(), false) + } + // Generic x86 + "x86" => emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(4).unwrap(), true), + // Windows AArch64 + "aarch64" if target.options.is_like_windows => { + emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(8).unwrap(), false) + } + // macOS / iOS AArch64 + "aarch64" if target.options.is_like_osx => { + emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(8).unwrap(), true) + } + "aarch64" => emit_aapcs_va_arg(bx, addr, target_ty), + // Windows x86_64 + "x86_64" if target.options.is_like_windows => { + let target_ty_size = bx.cx.size_of(target_ty).bytes(); + let indirect: bool = target_ty_size > 8 || !target_ty_size.is_power_of_two(); + emit_ptr_va_arg(bx, addr, target_ty, indirect, Align::from_bytes(8).unwrap(), false) + } + // For all other architecture/OS combinations fall back to using + // the LLVM va_arg instruction. + // https://llvm.org/docs/LangRef.html#va-arg-instruction + _ => bx.va_arg(addr.immediate(), bx.cx.layout_of(target_ty).gcc_type(bx.cx, true)), + } +}*/ diff --git a/test.sh b/test.sh new file mode 100755 index 0000000000000..1e13b06cf9c3d --- /dev/null +++ b/test.sh @@ -0,0 +1,197 @@ +#!/bin/bash + +# TODO: rewrite to cargo-make (or just) or something like that to only rebuild the sysroot when needed? + +#set -x +set -e + +export GCC_PATH=$(cat gcc_path) + +export LD_LIBRARY_PATH="$GCC_PATH" +export LIBRARY_PATH="$GCC_PATH" + +if [[ "$1" == "--release" ]]; then + export CHANNEL='release' + CARGO_INCREMENTAL=1 cargo rustc --release +else + echo $LD_LIBRARY_PATH + export CHANNEL='debug' + cargo rustc +fi + +source config.sh + +rm -r target/out || true +mkdir -p target/out/gccjit + +echo "[BUILD] mini_core" +$RUSTC example/mini_core.rs --crate-name mini_core --crate-type lib,dylib --target $TARGET_TRIPLE + +echo "[BUILD] example" +$RUSTC example/example.rs --crate-type lib --target $TARGET_TRIPLE + +#if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then + #echo "[JIT] mini_core_hello_world" + #CG_CLIF_JIT=1 CG_CLIF_JIT_ARGS="abc bcd" $RUSTC --crate-type bin -Cprefer-dynamic 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" +$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 "[BUILD] sysroot" +time ./build_sysroot/build_sysroot.sh + +echo "[AOT] arbitrary_self_types_pointers_and_wrappers" +$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 + +echo "[AOT] alloc_system" +$RUSTC example/alloc_system.rs --crate-type lib --target "$TARGET_TRIPLE" + +# FIXME: this requires linking an additional lib for __popcountdi2 +#echo "[AOT] alloc_example" +#$RUSTC example/alloc_example.rs --crate-type bin --target $TARGET_TRIPLE +#$RUN_WRAPPER ./target/out/alloc_example + +#if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then + #echo "[JIT] std_example" + #CG_CLIF_JIT=1 $RUSTC --crate-type bin -Cprefer-dynamic 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. +$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" +$RUSTC example/std_example.rs --crate-type bin --target $TARGET_TRIPLE +$RUN_WRAPPER ./target/out/std_example --target $TARGET_TRIPLE + +echo "[AOT] subslice-patterns-const-eval" +$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" +$RUSTC example/track-caller-attribute.rs --crate-type bin -Cpanic=abort --target $TARGET_TRIPLE +$RUN_WRAPPER ./target/out/track-caller-attribute + +# FIXME: this requires linking an additional lib for __popcountdi2 +#echo "[BUILD] mod_bench" +#$RUSTC example/mod_bench.rs --crate-type bin --target $TARGET_TRIPLE + +# FIXME linker gives multiple definitions error on Linux +#echo "[BUILD] sysroot in release mode" +#./build_sysroot/build_sysroot.sh --release + +#pushd simple-raytracer +#if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then + #echo "[BENCH COMPILE] ebobby/simple-raytracer" + #hyperfine --runs ${RUN_RUNS:-10} --warmup 1 --prepare "rm -r target/*/debug || true" \ + #"RUSTFLAGS='' cargo build --target $TARGET_TRIPLE" \ + #"../cargo.sh build" + + #echo "[BENCH RUN] ebobby/simple-raytracer" + #cp ./target/*/debug/main ./raytracer_cg_gccjit + #hyperfine --runs ${RUN_RUNS:-10} ./raytracer_cg_llvm ./raytracer_cg_gccjit +#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 +../../../../../cargo.sh test +popd + +#pushd regex +#echo "[TEST] rust-lang/regex example shootout-regex-dna" +#../cargo.sh clean +## Make sure `[codegen mono items] start` doesn't poison the diff +#../cargo.sh build --example shootout-regex-dna +#cat examples/regexdna-input.txt | ../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" +#../cargo.sh test --tests -- --exclude-should-panic --test-threads 1 -Zunstable-options +#popd + +#echo +#echo "[BENCH COMPILE] mod_bench" + +#COMPILE_MOD_BENCH_INLINE="$RUSTC example/mod_bench.rs --crate-type bin -Zmir-opt-level=3 -O --crate-name mod_bench_inline" +#COMPILE_MOD_BENCH_LLVM_0="rustc example/mod_bench.rs --crate-type bin -Copt-level=0 -o target/out/mod_bench_llvm_0 -Cpanic=abort" +#COMPILE_MOD_BENCH_LLVM_1="rustc example/mod_bench.rs --crate-type bin -Copt-level=1 -o target/out/mod_bench_llvm_1 -Cpanic=abort" +#COMPILE_MOD_BENCH_LLVM_2="rustc example/mod_bench.rs --crate-type bin -Copt-level=2 -o target/out/mod_bench_llvm_2 -Cpanic=abort" +#COMPILE_MOD_BENCH_LLVM_3="rustc example/mod_bench.rs --crate-type bin -Copt-level=3 -o target/out/mod_bench_llvm_3 -Cpanic=abort" + +## Use 100 runs, because a single compilations doesn't take more than ~150ms, so it isn't very slow +#hyperfine --runs ${COMPILE_RUNS:-100} "$COMPILE_MOD_BENCH_INLINE" "$COMPILE_MOD_BENCH_LLVM_0" "$COMPILE_MOD_BENCH_LLVM_1" "$COMPILE_MOD_BENCH_LLVM_2" "$COMPILE_MOD_BENCH_LLVM_3" + +#echo +#echo "[BENCH RUN] mod_bench" +#hyperfine --runs ${RUN_RUNS:-10} ./target/out/mod_bench{,_inline} ./target/out/mod_bench_llvm_* + +echo +echo "[TEST] rust-lang/rust" + +rust_toolchain=$(cat rust-toolchain) + +git clone https://github.com/rust-lang/rust.git || true +cd rust +git fetch +git checkout $(rustc -V | cut -d' ' -f3 | tr -d '(') +export RUSTFLAGS= + +#git apply ../rust_lang.patch + + +rm config.toml || true + +cat > config.toml <>() + .join("\n"); + Some(lines) + }) + .test_cmds(move |path| { + // Test command 1: Compile `x.rs` into `tempdir/x`. + let mut exe = PathBuf::new(); + exe.push(&tempdir); + exe.push(path.file_stem().expect("file_stem")); + let mut compiler = Command::new("rustc"); + compiler.args(&[ + &format!("-Zcodegen-backend={}/target/debug/librustc_codegen_gcc.so", current_dir), + "--sysroot", &format!("{}/build_sysroot/sysroot/", current_dir), + "-Zno-parallel-llvm", + "-C", "panic=abort", + "-C", "link-arg=-lc", + "-o", exe.to_str().expect("to_str"), + path.to_str().expect("to_str"), + ]); + // Test command 2: run `tempdir/x`. + let runtime = Command::new(exe); + vec![("Compiler", compiler), ("Run-time", runtime)] + }) + .run(); +} diff --git a/tests/run/abort1.rs b/tests/run/abort1.rs new file mode 100644 index 0000000000000..291af5993aa25 --- /dev/null +++ b/tests/run/abort1.rs @@ -0,0 +1,51 @@ +// Compiler: +// +// Run-time: +// status: signal + +#![feature(auto_traits, lang_items, no_core, start, intrinsics)] + +#![no_std] +#![no_core] + +/* + * Core + */ + +// Because we don't have core yet. +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +trait Copy { +} + +impl Copy for isize {} + +#[lang = "receiver"] +trait Receiver { +} + +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +mod intrinsics { + use super::Sized; + + extern "rust-intrinsic" { + pub fn abort() -> !; + } +} + +/* + * Code + */ + +fn test_fail() -> ! { + unsafe { intrinsics::abort() }; +} + +#[start] +fn main(mut argc: isize, _argv: *const *const u8) -> isize { + test_fail(); +} diff --git a/tests/run/abort2.rs b/tests/run/abort2.rs new file mode 100644 index 0000000000000..3c87c567892b5 --- /dev/null +++ b/tests/run/abort2.rs @@ -0,0 +1,53 @@ +// Compiler: +// +// Run-time: +// status: signal + +#![feature(auto_traits, lang_items, no_core, start, intrinsics)] + +#![no_std] +#![no_core] + +/* + * Core + */ + +// Because we don't have core yet. +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +trait Copy { +} + +impl Copy for isize {} + +#[lang = "receiver"] +trait Receiver { +} + +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +mod intrinsics { + use super::Sized; + + extern "rust-intrinsic" { + pub fn abort() -> !; + } +} + +/* + * Code + */ + +fn fail() -> i32 { + unsafe { intrinsics::abort() }; + 0 +} + +#[start] +fn main(mut argc: isize, _argv: *const *const u8) -> isize { + fail(); + 0 +} diff --git a/tests/run/array.rs b/tests/run/array.rs new file mode 100644 index 0000000000000..8b621d8a3530f --- /dev/null +++ b/tests/run/array.rs @@ -0,0 +1,229 @@ +// Compiler: +// +// Run-time: +// status: 0 +// stdout: 42 +// 7 +// 5 +// 10 + +#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)] + +#![no_std] +#![no_core] + +/* + * Core + */ + +// Because we don't have core yet. +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +trait Copy { +} + +impl Copy for isize {} +impl Copy for usize {} +impl Copy for i32 {} +impl Copy for u8 {} +impl Copy for i8 {} +impl Copy for i16 {} + +#[lang = "receiver"] +trait Receiver { +} + +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +mod libc { + #[link(name = "c")] + extern "C" { + pub fn printf(format: *const i8, ...) -> i32; + pub fn puts(s: *const u8) -> i32; + } +} + +#[lang = "index"] +pub trait Index { + type Output: ?Sized; + fn index(&self, index: Idx) -> &Self::Output; +} + +impl Index for [T; 3] { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self[index] + } +} + +impl Index for [T] { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self[index] + } +} + +#[lang = "drop_in_place"] +#[allow(unconditional_recursion)] +pub unsafe fn drop_in_place(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 = "panic"] +#[track_caller] +#[no_mangle] +pub fn panic(_msg: &str) -> ! { + unsafe { + libc::puts("Panicking\0" as *const str as *const u8); + intrinsics::abort(); + } +} + +#[lang = "panic_location"] +struct PanicLocation { + file: &'static str, + line: u32, + column: u32, +} + +#[lang = "panic_bounds_check"] +#[track_caller] +#[no_mangle] +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(); + } +} + +mod intrinsics { + extern "rust-intrinsic" { + pub fn abort() -> !; + } +} + +#[lang = "add"] +trait Add { + 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 i32 { + 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 + } +} + +impl Add for isize { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + self + rhs + } +} + +#[lang = "sub"] +pub trait Sub { + 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 isize { + 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 + } +} + + +/* + * Code + */ + +static mut ONE: usize = 1; + +fn make_array() -> [u8; 3] { + [42, 10, 5] +} + +#[start] +fn main(argc: isize, _argv: *const *const u8) -> isize { + let array = [42, 7, 5]; + let array2 = make_array(); + unsafe { + libc::printf(b"%ld\n\0" as *const u8 as *const i8, array[ONE - 1]); + libc::printf(b"%ld\n\0" as *const u8 as *const i8, array[ONE]); + libc::printf(b"%ld\n\0" as *const u8 as *const i8, array[ONE + 1]); + + libc::printf(b"%d\n\0" as *const u8 as *const i8, array2[argc as usize] as u32); + } + 0 +} diff --git a/tests/run/asm.rs b/tests/run/asm.rs new file mode 100644 index 0000000000000..bd76153e047e4 --- /dev/null +++ b/tests/run/asm.rs @@ -0,0 +1,66 @@ +// Compiler: +// +// Run-time: +// status: 0 + +#![feature(asm, global_asm)] + +global_asm!(" + .global add_asm +add_asm: + mov rax, rdi + add rax, rsi + ret" +); + +extern "C" { + fn add_asm(a: i64, b: i64) -> i64; +} + +fn main() { + unsafe { + asm!("nop"); + } + + let x: u64; + unsafe { + asm!("mov $5, {}", + out(reg) x, + options(att_syntax) + ); + } + assert_eq!(x, 5); + + let x: u64; + let input: u64 = 42; + unsafe { + asm!("mov {input}, {output}", + "add $1, {output}", + input = in(reg) input, + output = out(reg) x, + options(att_syntax) + ); + } + assert_eq!(x, 43); + + let x: u64; + unsafe { + asm!("mov {}, 6", + out(reg) x, + ); + } + assert_eq!(x, 6); + + let x: u64; + let input: u64 = 42; + unsafe { + asm!("mov {output}, {input}", + "add {output}, 1", + input = in(reg) input, + output = out(reg) x, + ); + } + assert_eq!(x, 43); + + assert_eq!(unsafe { add_asm(40, 2) }, 42); +} diff --git a/tests/run/assign.rs b/tests/run/assign.rs new file mode 100644 index 0000000000000..cc8647006ca63 --- /dev/null +++ b/tests/run/assign.rs @@ -0,0 +1,153 @@ +// Compiler: +// +// Run-time: +// stdout: 2 +// 7 8 +// 10 + +#![allow(unused_attributes)] +#![feature(auto_traits, lang_items, no_core, start, intrinsics, track_caller)] + +#![no_std] +#![no_core] + +/* + * Core + */ + +// Because we don't have core yet. +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +trait Copy { +} + +impl Copy for isize {} +impl Copy for *mut i32 {} +impl Copy for usize {} +impl Copy for u8 {} +impl Copy for i8 {} +impl Copy for i32 {} + +#[lang = "receiver"] +trait Receiver { +} + +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +#[lang = "panic_location"] +struct PanicLocation { + file: &'static str, + line: u32, + column: u32, +} + +mod libc { + #[link(name = "c")] + extern "C" { + pub fn puts(s: *const u8) -> i32; + pub fn fflush(stream: *mut i32) -> i32; + pub fn printf(format: *const i8, ...) -> i32; + + pub static STDOUT: *mut i32; + } +} + +mod intrinsics { + extern "rust-intrinsic" { + pub fn abort() -> !; + } +} + +#[lang = "panic"] +#[track_caller] +#[no_mangle] +pub fn panic(_msg: &str) -> ! { + unsafe { + libc::puts("Panicking\0" as *const str as *const u8); + libc::fflush(libc::STDOUT); + intrinsics::abort(); + } +} + +#[lang = "add"] +trait Add { + 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 i32 { + 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 + } +} + +impl Add for isize { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + self + rhs + } +} + +/* + * Code + */ + +fn inc_ref(num: &mut isize) -> isize { + *num = *num + 5; + *num + 1 +} + +fn inc(num: isize) -> isize { + num + 1 +} + + +#[start] +fn main(mut argc: isize, _argv: *const *const u8) -> isize { + argc = inc(argc); + unsafe { + libc::printf(b"%ld\n\0" as *const u8 as *const i8, argc); + } + + let b = inc_ref(&mut argc); + unsafe { + libc::printf(b"%ld %ld\n\0" as *const u8 as *const i8, argc, b); + } + + argc = 10; + unsafe { + libc::printf(b"%ld\n\0" as *const u8 as *const i8, argc); + } + 0 +} diff --git a/tests/run/closure.rs b/tests/run/closure.rs new file mode 100644 index 0000000000000..7121a5f0d5221 --- /dev/null +++ b/tests/run/closure.rs @@ -0,0 +1,230 @@ +// Compiler: +// +// Run-time: +// status: 0 +// stdout: Arg: 1 +// Argument: 1 +// String arg: 1 +// Int argument: 2 +// Both args: 11 + +#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics, + unboxed_closures)] + +#![no_std] +#![no_core] + +/* + * Core + */ + +// Because we don't have core yet. +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +trait Copy { +} + +impl Copy for isize {} +impl Copy for usize {} +impl Copy for i32 {} +impl Copy for u32 {} +impl Copy for u8 {} +impl Copy for i8 {} + +#[lang = "receiver"] +trait Receiver { +} + +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +mod libc { + #[link(name = "c")] + extern "C" { + pub fn puts(s: *const u8) -> i32; + pub fn printf(format: *const i8, ...) -> i32; + } +} + +#[lang = "index"] +pub trait Index { + type Output: ?Sized; + fn index(&self, index: Idx) -> &Self::Output; +} + +impl Index for [T; 3] { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self[index] + } +} + +impl Index for [T] { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self[index] + } +} + +#[lang = "drop_in_place"] +#[allow(unconditional_recursion)] +pub unsafe fn drop_in_place(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 = "panic_location"] +struct PanicLocation { + file: &'static str, + line: u32, + column: u32, +} + +#[lang = "panic_bounds_check"] +#[track_caller] +#[no_mangle] +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(); + } +} + +mod intrinsics { + extern "rust-intrinsic" { + pub fn abort() -> !; + } +} + +#[lang = "unsize"] +pub trait Unsize {} + +#[lang = "coerce_unsized"] +pub trait CoerceUnsized {} + +impl<'a, 'b: 'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} +impl<'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {} +impl, U: ?Sized> CoerceUnsized<*const U> for *const T {} +impl, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} + +#[lang = "fn_once"] +#[rustc_paren_sugar] +pub trait FnOnce { + #[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: FnOnce { + extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output; +} + +#[lang = "add"] +trait Add { + 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 i32 { + 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 + } +} + +impl Add for isize { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + self + rhs + } +} + +#[lang = "panic"] +#[track_caller] +#[no_mangle] +pub fn panic(_msg: &str) -> ! { + unsafe { + libc::puts("Panicking\0" as *const str as *const u8); + intrinsics::abort(); + } +} + +/* + * Code + */ + +#[start] +fn main(mut argc: isize, _argv: *const *const u8) -> isize { + let string = "Arg: %d\n\0"; + let mut closure = || { + unsafe { + libc::printf(string as *const str as *const i8, argc); + } + }; + closure(); + + let mut closure = || { + unsafe { + libc::printf("Argument: %d\n\0" as *const str as *const i8, argc); + } + }; + closure(); + + let mut closure = |string| { + unsafe { + libc::printf(string as *const str as *const i8, argc); + } + }; + closure("String arg: %d\n\0"); + + let mut closure = |arg: isize| { + unsafe { + libc::printf("Int argument: %d\n\0" as *const str as *const i8, arg); + } + }; + closure(argc + 1); + + let mut closure = |string, arg: isize| { + unsafe { + libc::printf(string as *const str as *const i8, arg); + } + }; + closure("Both args: %d\n\0", argc + 10); + + 0 +} diff --git a/tests/run/condition.rs b/tests/run/condition.rs new file mode 100644 index 0000000000000..6a2e2d5bb11a9 --- /dev/null +++ b/tests/run/condition.rs @@ -0,0 +1,320 @@ +// Compiler: +// +// Run-time: +// status: 0 +// stdout: true +// 1 + +#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)] + +#![no_std] +#![no_core] + +/* + * Core + */ + +// Because we don't have core yet. +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +trait Copy { +} + +impl Copy for isize {} +impl Copy for usize {} +impl Copy for u64 {} +impl Copy for i32 {} +impl Copy for u32 {} +impl Copy for bool {} +impl Copy for u16 {} +impl Copy for i16 {} +impl Copy for char {} +impl Copy for i8 {} +impl Copy for u8 {} + +#[lang = "receiver"] +trait Receiver { +} + +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +mod libc { + #[link(name = "c")] + extern "C" { + pub fn printf(format: *const i8, ...) -> i32; + pub fn puts(s: *const u8) -> i32; + } +} + +#[lang = "index"] +pub trait Index { + type Output: ?Sized; + fn index(&self, index: Idx) -> &Self::Output; +} + +impl Index for [T; 3] { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self[index] + } +} + +impl Index for [T] { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self[index] + } +} + +#[lang = "drop_in_place"] +#[allow(unconditional_recursion)] +pub unsafe fn drop_in_place(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 = "panic"] +#[track_caller] +#[no_mangle] +pub fn panic(_msg: &str) -> ! { + unsafe { + libc::puts("Panicking\0" as *const str as *const u8); + intrinsics::abort(); + } +} + +#[lang = "panic_location"] +struct PanicLocation { + file: &'static str, + line: u32, + column: u32, +} + +#[lang = "panic_bounds_check"] +#[track_caller] +#[no_mangle] +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(); + } +} + +mod intrinsics { + extern "rust-intrinsic" { + pub fn abort() -> !; + } +} + +#[lang = "add"] +trait Add { + 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 i32 { + 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 + } +} + +impl Add for isize { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + self + rhs + } +} + +#[lang = "sub"] +pub trait Sub { + 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 isize { + 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 = "eq"] +pub trait PartialEq { + 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 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) + } +} + +/* + * Code + */ + +#[start] +fn main(argc: isize, _argv: *const *const u8) -> isize { + unsafe { + if argc == 1 { + libc::printf(b"true\n\0" as *const u8 as *const i8); + } + + let string = + match argc { + 1 => b"1\n\0", + 2 => b"2\n\0", + 3 => b"3\n\0", + 4 => b"4\n\0", + 5 => b"5\n\0", + _ => b"_\n\0", + }; + libc::printf(string as *const u8 as *const i8); + } + 0 +} diff --git a/tests/run/empty_main.rs b/tests/run/empty_main.rs new file mode 100644 index 0000000000000..c02cfd2a85f03 --- /dev/null +++ b/tests/run/empty_main.rs @@ -0,0 +1,39 @@ +// Compiler: +// +// Run-time: +// status: 0 + +#![feature(auto_traits, lang_items, no_core, start)] + +#![no_std] +#![no_core] + +/* + * Core + */ + +// Because we don't have core yet. +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +trait Copy { +} + +impl Copy for isize {} + +#[lang = "receiver"] +trait Receiver { +} + +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +/* + * Code + */ + +#[start] +fn main(mut argc: isize, _argv: *const *const u8) -> isize { + 0 +} diff --git a/tests/run/exit.rs b/tests/run/exit.rs new file mode 100644 index 0000000000000..956e53dd4aa65 --- /dev/null +++ b/tests/run/exit.rs @@ -0,0 +1,49 @@ +// Compiler: +// +// Run-time: +// status: 2 + +#![feature(auto_traits, lang_items, no_core, start, intrinsics)] + +#![no_std] +#![no_core] + +mod libc { + #[link(name = "c")] + extern "C" { + pub fn exit(status: i32); + } +} + +/* + * Core + */ + +// Because we don't have core yet. +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +trait Copy { +} + +impl Copy for isize {} + +#[lang = "receiver"] +trait Receiver { +} + +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +/* + * Code + */ + +#[start] +fn main(mut argc: isize, _argv: *const *const u8) -> isize { + unsafe { + libc::exit(2); + } + 0 +} diff --git a/tests/run/exit_code.rs b/tests/run/exit_code.rs new file mode 100644 index 0000000000000..eeab352095123 --- /dev/null +++ b/tests/run/exit_code.rs @@ -0,0 +1,39 @@ +// Compiler: +// +// Run-time: +// status: 1 + +#![feature(auto_traits, lang_items, no_core, start)] + +#![no_std] +#![no_core] + +/* + * Core + */ + +// Because we don't have core yet. +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +trait Copy { +} + +impl Copy for isize {} + +#[lang = "receiver"] +trait Receiver { +} + +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +/* + * Code + */ + +#[start] +fn main(mut argc: isize, _argv: *const *const u8) -> isize { + 1 +} diff --git a/tests/run/fun_ptr.rs b/tests/run/fun_ptr.rs new file mode 100644 index 0000000000000..a226fff79e51b --- /dev/null +++ b/tests/run/fun_ptr.rs @@ -0,0 +1,223 @@ +// Compiler: +// +// Run-time: +// status: 0 +// stdout: 1 + +#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)] + +#![no_std] +#![no_core] + +/* + * Core + */ + +// Because we don't have core yet. +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +trait Copy { +} + +impl Copy for isize {} +impl Copy for usize {} +impl Copy for i32 {} +impl Copy for u8 {} +impl Copy for i8 {} +impl Copy for i16 {} + +#[lang = "receiver"] +trait Receiver { +} + +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +mod libc { + #[link(name = "c")] + extern "C" { + pub fn printf(format: *const i8, ...) -> i32; + pub fn puts(s: *const u8) -> i32; + } +} + +#[lang = "index"] +pub trait Index { + type Output: ?Sized; + fn index(&self, index: Idx) -> &Self::Output; +} + +impl Index for [T; 3] { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self[index] + } +} + +impl Index for [T] { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self[index] + } +} + +#[lang = "drop_in_place"] +#[allow(unconditional_recursion)] +pub unsafe fn drop_in_place(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 = "panic"] +#[track_caller] +#[no_mangle] +pub fn panic(_msg: &str) -> ! { + unsafe { + libc::puts("Panicking\0" as *const str as *const u8); + intrinsics::abort(); + } +} + +#[lang = "panic_location"] +struct PanicLocation { + file: &'static str, + line: u32, + column: u32, +} + +#[lang = "panic_bounds_check"] +#[track_caller] +#[no_mangle] +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(); + } +} + +mod intrinsics { + extern "rust-intrinsic" { + pub fn abort() -> !; + } +} + +#[lang = "add"] +trait Add { + 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 i32 { + 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 + } +} + +impl Add for isize { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + self + rhs + } +} + +#[lang = "sub"] +pub trait Sub { + 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 isize { + 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 + } +} + + +/* + * Code + */ + +fn i16_as_i8(a: i16) -> i8 { + a as i8 +} + +fn call_func(func: fn(i16) -> i8, param: i16) -> i8 { + func(param) +} + +#[start] +fn main(argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let result = call_func(i16_as_i8, argc as i16) as isize; + libc::printf(b"%ld\n\0" as *const u8 as *const i8, result); + } + 0 +} diff --git a/tests/run/int_overflow.rs b/tests/run/int_overflow.rs new file mode 100644 index 0000000000000..7111703ca2532 --- /dev/null +++ b/tests/run/int_overflow.rs @@ -0,0 +1,129 @@ +// Compiler: +// +// Run-time: +// stdout: Panicking +// status: signal + +#![allow(unused_attributes)] +#![feature(auto_traits, lang_items, no_core, start, intrinsics)] + +#![no_std] +#![no_core] + +/* + * Core + */ + +// Because we don't have core yet. +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +trait Copy { +} + +impl Copy for isize {} +impl Copy for *mut i32 {} +impl Copy for usize {} +impl Copy for i32 {} +impl Copy for u8 {} +impl Copy for i8 {} + +#[lang = "receiver"] +trait Receiver { +} + +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +#[lang = "panic_location"] +struct PanicLocation { + file: &'static str, + line: u32, + column: u32, +} + +mod libc { + #[link(name = "c")] + extern "C" { + pub fn puts(s: *const u8) -> i32; + pub fn fflush(stream: *mut i32) -> i32; + + pub static STDOUT: *mut i32; + } +} + +mod intrinsics { + extern "rust-intrinsic" { + pub fn abort() -> !; + } +} + +#[lang = "panic"] +#[track_caller] +#[no_mangle] +pub fn panic(_msg: &str) -> ! { + unsafe { + libc::puts("Panicking\0" as *const str as *const u8); + libc::fflush(libc::STDOUT); + intrinsics::abort(); + } +} + +#[lang = "add"] +trait Add { + 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 i32 { + 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 + } +} + +impl Add for isize { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + self + rhs + } +} + +/* + * Code + */ + +#[start] +fn main(mut argc: isize, _argv: *const *const u8) -> isize { + let int = 9223372036854775807isize; + let int = int + argc; + int +} diff --git a/tests/run/mut_ref.rs b/tests/run/mut_ref.rs new file mode 100644 index 0000000000000..e8876009cc610 --- /dev/null +++ b/tests/run/mut_ref.rs @@ -0,0 +1,165 @@ + +// Compiler: +// +// Run-time: +// stdout: 2 +// 7 +// 6 +// 11 + +#![allow(unused_attributes)] +#![feature(auto_traits, lang_items, no_core, start, intrinsics, track_caller)] + +#![no_std] +#![no_core] + +/* + * Core + */ + +// Because we don't have core yet. +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +trait Copy { +} + +impl Copy for isize {} +impl Copy for *mut i32 {} +impl Copy for usize {} +impl Copy for u8 {} +impl Copy for i8 {} +impl Copy for i32 {} + +#[lang = "receiver"] +trait Receiver { +} + +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +#[lang = "panic_location"] +struct PanicLocation { + file: &'static str, + line: u32, + column: u32, +} + +mod libc { + #[link(name = "c")] + extern "C" { + pub fn puts(s: *const u8) -> i32; + pub fn fflush(stream: *mut i32) -> i32; + pub fn printf(format: *const i8, ...) -> i32; + + pub static STDOUT: *mut i32; + } +} + +mod intrinsics { + extern "rust-intrinsic" { + pub fn abort() -> !; + } +} + +#[lang = "panic"] +#[track_caller] +#[no_mangle] +pub fn panic(_msg: &str) -> ! { + unsafe { + libc::puts("Panicking\0" as *const str as *const u8); + libc::fflush(libc::STDOUT); + intrinsics::abort(); + } +} + +#[lang = "add"] +trait Add { + 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 i32 { + 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 + } +} + +impl Add for isize { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + self + rhs + } +} + +/* + * Code + */ + +struct Test { + field: isize, +} + +fn test(num: isize) -> Test { + Test { + field: num + 1, + } +} + +fn update_num(num: &mut isize) { + *num = *num + 5; +} + +#[start] +fn main(mut argc: isize, _argv: *const *const u8) -> isize { + let mut test = test(argc); + unsafe { + libc::printf(b"%ld\n\0" as *const u8 as *const i8, test.field); + } + update_num(&mut test.field); + unsafe { + libc::printf(b"%ld\n\0" as *const u8 as *const i8, test.field); + } + + update_num(&mut argc); + unsafe { + libc::printf(b"%ld\n\0" as *const u8 as *const i8, argc); + } + + let refe = &mut argc; + *refe = *refe + 5; + unsafe { + libc::printf(b"%ld\n\0" as *const u8 as *const i8, argc); + } + + 0 +} diff --git a/tests/run/operations.rs b/tests/run/operations.rs new file mode 100644 index 0000000000000..4dc375309e426 --- /dev/null +++ b/tests/run/operations.rs @@ -0,0 +1,221 @@ +// Compiler: +// +// Run-time: +// stdout: 41 +// 39 +// 10 + +#![allow(unused_attributes)] +#![feature(auto_traits, lang_items, no_core, start, intrinsics, arbitrary_self_types)] + +#![no_std] +#![no_core] + +/* + * Core + */ + +// Because we don't have core yet. +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +trait Copy { +} + +impl Copy for isize {} +impl Copy for *mut i32 {} +impl Copy for usize {} +impl Copy for u8 {} +impl Copy for i8 {} +impl Copy for i16 {} +impl Copy for i32 {} + +#[lang = "deref"] +pub trait Deref { + type Target: ?Sized; + + fn deref(&self) -> &Self::Target; +} + +#[lang = "receiver"] +trait Receiver { +} + +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +#[lang = "panic_location"] +struct PanicLocation { + file: &'static str, + line: u32, + column: u32, +} + +mod libc { + #[link(name = "c")] + extern "C" { + pub fn printf(format: *const i8, ...) -> i32; + pub fn puts(s: *const u8) -> i32; + pub fn fflush(stream: *mut i32) -> i32; + + pub static STDOUT: *mut i32; + } +} + +mod intrinsics { + extern "rust-intrinsic" { + pub fn abort() -> !; + } +} + +#[lang = "panic"] +#[track_caller] +#[no_mangle] +pub fn panic(_msg: &str) -> ! { + unsafe { + libc::puts("Panicking\0" as *const str as *const u8); + libc::fflush(libc::STDOUT); + intrinsics::abort(); + } +} + +#[lang = "add"] +trait Add { + 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 i32 { + 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 + } +} + +impl Add for isize { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + self + rhs + } +} + +#[lang = "sub"] +pub trait Sub { + 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 isize { + 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 = "mul"] +pub trait Mul { + 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 + } +} + +impl Mul for isize { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + self * rhs + } +} + +/* + * Code + */ + +#[start] +fn main(mut argc: isize, _argv: *const *const u8) -> isize { + unsafe { + libc::printf(b"%ld\n\0" as *const u8 as *const i8, 40 + argc); + libc::printf(b"%ld\n\0" as *const u8 as *const i8, 40 - argc); + libc::printf(b"%ld\n\0" as *const u8 as *const i8, 10 * argc); + } + 0 +} diff --git a/tests/run/ptr_cast.rs b/tests/run/ptr_cast.rs new file mode 100644 index 0000000000000..6ac099ea145c2 --- /dev/null +++ b/tests/run/ptr_cast.rs @@ -0,0 +1,222 @@ +// Compiler: +// +// Run-time: +// status: 0 +// stdout: 1 + +#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)] + +#![no_std] +#![no_core] + +/* + * Core + */ + +// Because we don't have core yet. +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +trait Copy { +} + +impl Copy for isize {} +impl Copy for usize {} +impl Copy for i32 {} +impl Copy for u8 {} +impl Copy for i8 {} +impl Copy for i16 {} + +#[lang = "receiver"] +trait Receiver { +} + +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +mod libc { + #[link(name = "c")] + extern "C" { + pub fn printf(format: *const i8, ...) -> i32; + pub fn puts(s: *const u8) -> i32; + } +} + +#[lang = "index"] +pub trait Index { + type Output: ?Sized; + fn index(&self, index: Idx) -> &Self::Output; +} + +impl Index for [T; 3] { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self[index] + } +} + +impl Index for [T] { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self[index] + } +} + +#[lang = "drop_in_place"] +#[allow(unconditional_recursion)] +pub unsafe fn drop_in_place(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 = "panic"] +#[track_caller] +#[no_mangle] +pub fn panic(_msg: &str) -> ! { + unsafe { + libc::puts("Panicking\0" as *const str as *const u8); + intrinsics::abort(); + } +} + +#[lang = "panic_location"] +struct PanicLocation { + file: &'static str, + line: u32, + column: u32, +} + +#[lang = "panic_bounds_check"] +#[track_caller] +#[no_mangle] +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(); + } +} + +mod intrinsics { + extern "rust-intrinsic" { + pub fn abort() -> !; + } +} + +#[lang = "add"] +trait Add { + 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 i32 { + 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 + } +} + +impl Add for isize { + type Output = Self; + + fn add(self, rhs: Self) -> Self { + self + rhs + } +} + +#[lang = "sub"] +pub trait Sub { + 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 isize { + 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 + } +} + + +/* + * Code + */ + +static mut ONE: usize = 1; + +fn make_array() -> [u8; 3] { + [42, 10, 5] +} + +#[start] +fn main(argc: isize, _argv: *const *const u8) -> isize { + unsafe { + let ptr = ONE as *mut usize; + let value = ptr as usize; + libc::printf(b"%ld\n\0" as *const u8 as *const i8, value); + } + 0 +} diff --git a/tests/run/return-tuple.rs b/tests/run/return-tuple.rs new file mode 100644 index 0000000000000..6fa10dca06f67 --- /dev/null +++ b/tests/run/return-tuple.rs @@ -0,0 +1,72 @@ +// Compiler: +// +// Run-time: +// status: 0 +// stdout: 10 +// 10 +// 42 + +#![feature(auto_traits, lang_items, no_core, start, intrinsics)] + +#![no_std] +#![no_core] + +#[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 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 {} + +mod libc { + #[link(name = "c")] + extern "C" { + pub fn printf(format: *const i8, ...) -> i32; + } +} + +/* + * Core + */ + +// Because we don't have core yet. +#[lang = "sized"] +pub trait Sized {} + +#[lang = "receiver"] +trait Receiver { +} + +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +/* + * Code + */ + +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, + ) +} + +#[start] +fn main(argc: isize, _argv: *const *const u8) -> isize { + let (a, b, c, d, e, f, g, h, i, j) = int_cast(10, 42); + unsafe { + libc::printf(b"%d\n\0" as *const u8 as *const i8, c); + libc::printf(b"%ld\n\0" as *const u8 as *const i8, d); + libc::printf(b"%ld\n\0" as *const u8 as *const i8, j); + } + 0 +} diff --git a/tests/run/slice.rs b/tests/run/slice.rs new file mode 100644 index 0000000000000..ad9258ed0bdeb --- /dev/null +++ b/tests/run/slice.rs @@ -0,0 +1,128 @@ +// Compiler: +// +// Run-time: +// status: 0 +// stdout: 5 + +#![feature(arbitrary_self_types, auto_traits, lang_items, no_core, start, intrinsics)] + +#![no_std] +#![no_core] + +/* + * Core + */ + +// Because we don't have core yet. +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +trait Copy { +} + +impl Copy for isize {} +impl Copy for usize {} +impl Copy for i32 {} +impl Copy for u32 {} + +#[lang = "receiver"] +trait Receiver { +} + +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +mod libc { + #[link(name = "c")] + extern "C" { + pub fn printf(format: *const i8, ...) -> i32; + } +} + +#[lang = "index"] +pub trait Index { + type Output: ?Sized; + fn index(&self, index: Idx) -> &Self::Output; +} + +impl Index for [T; 3] { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self[index] + } +} + +impl Index for [T] { + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + &self[index] + } +} + +#[lang = "unsize"] +pub trait Unsize {} + +#[lang = "coerce_unsized"] +pub trait CoerceUnsized {} + +impl<'a, 'b: 'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a U> for &'b T {} +impl<'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {} +impl, U: ?Sized> CoerceUnsized<*const U> for *const T {} +impl, U: ?Sized> CoerceUnsized<*mut U> for *mut T {} + +#[lang = "drop_in_place"] +#[allow(unconditional_recursion)] +pub unsafe fn drop_in_place(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 = "panic_location"] +struct PanicLocation { + file: &'static str, + line: u32, + column: u32, +} + +#[lang = "panic_bounds_check"] +#[track_caller] +#[no_mangle] +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(); + } +} + +mod intrinsics { + use super::Sized; + + extern "rust-intrinsic" { + pub fn abort() -> !; + } +} + +/* + * Code + */ + +static mut TWO: usize = 2; + +fn index_slice(s: &[u32]) -> u32 { + unsafe { + s[TWO] + } +} + +#[start] +fn main(mut argc: isize, _argv: *const *const u8) -> isize { + let array = [42, 7, 5]; + unsafe { + libc::printf(b"%ld\n\0" as *const u8 as *const i8, index_slice(&array)); + } + 0 +} diff --git a/tests/run/static.rs b/tests/run/static.rs new file mode 100644 index 0000000000000..ab89f6aff4b50 --- /dev/null +++ b/tests/run/static.rs @@ -0,0 +1,106 @@ +// Compiler: +// +// Run-time: +// status: 0 +// stdout: 10 +// 14 +// 1 +// 12 +// 12 +// 1 + +#![feature(auto_traits, lang_items, no_core, start, intrinsics)] + +#![no_std] +#![no_core] + +/* + * Core + */ + +// Because we don't have core yet. +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +trait Copy { +} + +impl Copy for isize {} + +#[lang = "receiver"] +trait Receiver { +} + +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +mod intrinsics { + use super::Sized; + + extern "rust-intrinsic" { + pub fn abort() -> !; + } +} + +mod libc { + #[link(name = "c")] + extern "C" { + pub fn printf(format: *const i8, ...) -> i32; + } +} + +#[lang = "structural_peq"] +pub trait StructuralPartialEq {} + +#[lang = "structural_teq"] +pub trait StructuralEq {} + +#[lang = "drop_in_place"] +#[allow(unconditional_recursion)] +pub unsafe fn drop_in_place(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); +} + +/* + * Code + */ + +struct Test { + field: isize, +} + +struct WithRef { + refe: &'static Test, +} + +static mut CONSTANT: isize = 10; + +static mut TEST: Test = Test { + field: 12, +}; + +static mut TEST2: Test = Test { + field: 14, +}; + +static mut WITH_REF: WithRef = WithRef { + refe: unsafe { &TEST }, +}; + +#[start] +fn main(mut argc: isize, _argv: *const *const u8) -> isize { + unsafe { + libc::printf(b"%ld\n\0" as *const u8 as *const i8, CONSTANT); + libc::printf(b"%ld\n\0" as *const u8 as *const i8, TEST2.field); + TEST2.field = argc; + libc::printf(b"%ld\n\0" as *const u8 as *const i8, TEST2.field); + libc::printf(b"%ld\n\0" as *const u8 as *const i8, WITH_REF.refe.field); + WITH_REF.refe = &TEST2; + libc::printf(b"%ld\n\0" as *const u8 as *const i8, TEST.field); + libc::printf(b"%ld\n\0" as *const u8 as *const i8, WITH_REF.refe.field); + } + 0 +} diff --git a/tests/run/structs.rs b/tests/run/structs.rs new file mode 100644 index 0000000000000..6c8884855ac35 --- /dev/null +++ b/tests/run/structs.rs @@ -0,0 +1,70 @@ +// Compiler: +// +// Run-time: +// status: 0 +// stdout: 1 +// 2 + +#![feature(auto_traits, lang_items, no_core, start, intrinsics)] + +#![no_std] +#![no_core] + +/* + * Core + */ + +// Because we don't have core yet. +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +trait Copy { +} + +impl Copy for isize {} + +#[lang = "receiver"] +trait Receiver { +} + +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +mod libc { + #[link(name = "c")] + extern "C" { + pub fn printf(format: *const i8, ...) -> i32; + } +} + +/* + * Code + */ + +struct Test { + field: isize, +} + +struct Two { + two: isize, +} + +fn one() -> isize { + 1 +} + +#[start] +fn main(mut argc: isize, _argv: *const *const u8) -> isize { + let test = Test { + field: one(), + }; + let two = Two { + two: 2, + }; + unsafe { + libc::printf(b"%ld\n\0" as *const u8 as *const i8, test.field); + libc::printf(b"%ld\n\0" as *const u8 as *const i8, two.two); + } + 0 +} diff --git a/tests/run/tuple.rs b/tests/run/tuple.rs new file mode 100644 index 0000000000000..0b670bf267424 --- /dev/null +++ b/tests/run/tuple.rs @@ -0,0 +1,51 @@ +// Compiler: +// +// Run-time: +// status: 0 +// stdout: 3 + +#![feature(auto_traits, lang_items, no_core, start, intrinsics)] + +#![no_std] +#![no_core] + +/* + * Core + */ + +// Because we don't have core yet. +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +trait Copy { +} + +impl Copy for isize {} + +#[lang = "receiver"] +trait Receiver { +} + +#[lang = "freeze"] +pub(crate) unsafe auto trait Freeze {} + +mod libc { + #[link(name = "c")] + extern "C" { + pub fn printf(format: *const i8, ...) -> i32; + } +} + +/* + * Code + */ + +#[start] +fn main(mut argc: isize, _argv: *const *const u8) -> isize { + let test: (isize, isize, isize) = (3, 1, 4); + unsafe { + libc::printf(b"%ld\n\0" as *const u8 as *const i8, test.0); + } + 0 +} From 8841e9e632c0c8c815e8f3ab4c57feb22a99ceb5 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Mon, 19 Jul 2021 19:08:08 -0400 Subject: [PATCH 023/621] Fix tidy --- rustfmt.toml | 1 + src/bootstrap/check.rs | 9 +++++++-- src/tools/tidy/src/lib.rs | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/rustfmt.toml b/rustfmt.toml index 480b19a5e9336..053f3e3ee583b 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -19,6 +19,7 @@ ignore = [ "library/backtrace", "library/stdarch", "compiler/rustc_codegen_cranelift", + "compiler/rustc_codegen_gcc", "src/doc/book", "src/doc/edition-guide", "src/doc/embedded-book", diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index bc106746e57e0..03f57dbf30173 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -237,11 +237,16 @@ impl Step for CodegenBackend { const DEFAULT: bool = true; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { - run.paths(&["compiler/rustc_codegen_cranelift", "rustc_codegen_cranelift"]) + run.paths(&[ + "compiler/rustc_codegen_cranelift", + "rustc_codegen_cranelift", + "compiler/rustc_codegen_gcc", + "rustc_codegen_gcc", + ]) } fn make_run(run: RunConfig<'_>) { - for &backend in &[INTERNER.intern_str("cranelift")] { + for &backend in &[INTERNER.intern_str("cranelift"), INTERNER.intern_str("gcc")] { run.builder.ensure(CodegenBackend { target: run.target, backend }); } } diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index a1c41eb99810e..003c3a4fd3630 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -56,6 +56,7 @@ fn filter_dirs(path: &Path) -> bool { let skip = [ "tidy-test-file", "compiler/rustc_codegen_cranelift", + "compiler/rustc_codegen_gcc", "src/llvm-project", "library/backtrace", "library/stdarch", From 7132ce63cf9b61618020eb321500795b455cfdc1 Mon Sep 17 00:00:00 2001 From: Antoni Boucher Date: Mon, 19 Jul 2021 19:13:02 -0400 Subject: [PATCH 024/621] Exclude rustc_codegen_gcc from namespace --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index dedfe45aca49b..94e78df50176f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ members = [ exclude = [ "build", "compiler/rustc_codegen_cranelift", + "compiler/rustc_codegen_gcc", "src/test/rustdoc-gui", # HACK(eddyb) This hardcodes the fact that our CI uses `/checkout/obj`. "obj", From 0c89065b934397b62838fe3e4ef6f6352fc52daf Mon Sep 17 00:00:00 2001 From: antoyo Date: Sat, 14 Aug 2021 10:05:49 -0400 Subject: [PATCH 025/621] Update to nightly-2021-08-12 (#61) --- rust-toolchain | 2 +- src/builder.rs | 34 ++++++++++++++++++++++------------ src/context.rs | 8 +++++--- src/intrinsic/mod.rs | 6 +++--- src/type_.rs | 5 +++++ src/type_of.rs | 6 ++++++ 6 files changed, 42 insertions(+), 19 deletions(-) diff --git a/rust-toolchain b/rust-toolchain index 3a29ac5ddb120..4e9771311efbd 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2021-07-21 +nightly-2021-08-12 diff --git a/src/builder.rs b/src/builder.rs index 8bdcb08bd3d6b..bb864c27e1b4d 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -25,6 +25,7 @@ use rustc_codegen_ssa::traits::{ BuilderMethods, ConstMethods, DerivedTypeMethods, + LayoutTypeMethods, HasCodegen, OverflowOp, StaticBuilderMethods, @@ -514,8 +515,12 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { self.block.expect("block").end_with_switch(None, value, default_block, &gcc_cases); } - fn invoke(&mut self, _func: RValue<'gcc>, _args: &[RValue<'gcc>], _then: Block<'gcc>, _catch: Block<'gcc>, _funclet: Option<&Funclet>) -> RValue<'gcc> { - unimplemented!(); + fn invoke(&mut self, _typ: Type<'gcc>, _func: RValue<'gcc>, _args: &[RValue<'gcc>], then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>) -> RValue<'gcc> { + let condition = self.context.new_rvalue_from_int(self.bool_type, 0); + self.llbb().end_with_conditional(None, condition, then, catch); + self.context.new_rvalue_from_int(self.int_type, 0) + + // TODO /*debug!("invoke {:?} with args ({:?})", func, args); let args = self.check_call("invoke", func, args); @@ -1001,9 +1006,10 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } else if let abi::Abi::ScalarPair(ref a, ref b) = place.layout.abi { let b_offset = a.value.size(self).align_to(b.value.align(self).abi); + let pair_type = place.layout.gcc_type(self, false); let mut load = |i, scalar: &abi::Scalar, align| { - let llptr = self.struct_gep(place.llval, i as u64); + let llptr = self.struct_gep(pair_type, place.llval, i as u64); let load = self.load(llptr.get_type(), llptr, align); scalar_load_metadata(self, load, scalar); if scalar.is_bool() { self.trunc(load, self.type_i1()) } else { load } @@ -1044,7 +1050,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { let align = dest.align.restrict_for_offset(dest.layout.field(self.cx(), 0).size); cg_elem.val.store(&mut body_bx, PlaceRef::new_sized_aligned(current_val, cg_elem.layout, align)); - let next = body_bx.inbounds_gep(current.to_rvalue(), &[self.const_usize(1)]); + let next = body_bx.inbounds_gep(self.backend_type(cg_elem.layout), current.to_rvalue(), &[self.const_usize(1)]); body_bx.llbb().add_assignment(None, current, next); body_bx.br(header_bx.llbb()); @@ -1130,7 +1136,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { .add_eval(None, self.context.new_call(None, atomic_store, &[ptr, value, ordering])); } - fn gep(&mut self, ptr: RValue<'gcc>, indices: &[RValue<'gcc>]) -> RValue<'gcc> { + fn gep(&mut self, _typ: Type<'gcc>, ptr: RValue<'gcc>, indices: &[RValue<'gcc>]) -> RValue<'gcc> { let mut result = ptr; for index in indices { result = self.context.new_array_access(None, result, *index).get_address(None).to_rvalue(); @@ -1138,7 +1144,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { result } - fn inbounds_gep(&mut self, ptr: RValue<'gcc>, indices: &[RValue<'gcc>]) -> RValue<'gcc> { + fn inbounds_gep(&mut self, _typ: Type<'gcc>, ptr: RValue<'gcc>, indices: &[RValue<'gcc>]) -> RValue<'gcc> { // FIXME: would be safer if doing the same thing (loop) as gep. // TODO: specify inbounds somehow. match indices.len() { @@ -1153,11 +1159,10 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } } - fn struct_gep(&mut self, ptr: RValue<'gcc>, idx: u64) -> RValue<'gcc> { + fn struct_gep(&mut self, value_type: Type<'gcc>, ptr: RValue<'gcc>, idx: u64) -> RValue<'gcc> { // FIXME: it would be better if the API only called this on struct, not on arrays. assert_eq!(idx as usize as u64, idx); let value = ptr.dereference(None).to_rvalue(); - let value_type = value.get_type(); if value_type.is_array().is_some() { let index = self.context.new_rvalue_from_long(self.u64_type, i64::try_from(idx).expect("i64::try_from")); @@ -1449,14 +1454,19 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn landing_pad(&mut self, _ty: Type<'gcc>, _pers_fn: RValue<'gcc>, _num_clauses: usize) -> RValue<'gcc> { - unimplemented!(); + let field1 = self.context.new_field(None, self.u8_type, "landing_pad_field_1"); + let field2 = self.context.new_field(None, self.i32_type, "landing_pad_field_1"); + let struct_type = self.context.new_struct_type(None, "landing_pad", &[field1, field2]); + self.current_func().new_local(None, struct_type.as_type(), "landing_pad") + .to_rvalue() + // TODO /*unsafe { llvm::LLVMBuildLandingPad(self.llbuilder, ty, pers_fn, num_clauses as c_uint, UNNAMED) }*/ } fn set_cleanup(&mut self, _landing_pad: RValue<'gcc>) { - unimplemented!(); + // TODO /*unsafe { llvm::LLVMSetCleanup(landing_pad, llvm::True); }*/ @@ -1527,7 +1537,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn set_personality_fn(&mut self, _personality: RValue<'gcc>) { - unimplemented!(); + // TODO /*unsafe { llvm::LLVMSetPersonalityFn(self.llfn(), personality); }*/ @@ -1620,7 +1630,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { //self.call_lifetime_intrinsic("llvm.lifetime.end.p0i8", ptr, size); } - fn call(&mut self, func: RValue<'gcc>, args: &[RValue<'gcc>], funclet: Option<&Funclet>) -> RValue<'gcc> { + fn call(&mut self, _typ: Type<'gcc>, func: RValue<'gcc>, args: &[RValue<'gcc>], funclet: Option<&Funclet>) -> RValue<'gcc> { // FIXME: remove when having a proper API. let gcc_func = unsafe { std::mem::transmute(func) }; if self.functions.borrow().values().find(|value| **value == gcc_func).is_some() { diff --git a/src/context.rs b/src/context.rs index 9cbbee772c5fb..19243b0cbce5d 100644 --- a/src/context.rs +++ b/src/context.rs @@ -14,7 +14,6 @@ use gccjit::{ use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::traits::{ BackendTypes, - BaseTypeMethods, MiscMethods, }; use rustc_data_structures::base_n; @@ -349,12 +348,15 @@ impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> { .unwrap().unwrap(), ), _ => { - let name = if wants_msvc_seh(self.sess()) { + let _name = if wants_msvc_seh(self.sess()) { "__CxxFrameHandler3" } else { "rust_eh_personality" }; - self.declare_func(name, self.type_i32(), &[], true) + //let func = self.declare_func(name, self.type_i32(), &[], true); + // FIXME: this hack should not be needed. That will probably be removed when + // unwinding support is added. + self.context.new_rvalue_from_int(self.int_type, 0) } }; //attributes::apply_target_cpu_attr(self, llfn); diff --git a/src/intrinsic/mod.rs b/src/intrinsic/mod.rs index 083f7e01c80d0..ad6dfbffbac96 100644 --- a/src/intrinsic/mod.rs +++ b/src/intrinsic/mod.rs @@ -98,7 +98,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { _ if simple.is_some() => { // FIXME: remove this cast when the API supports function. let func = unsafe { std::mem::transmute(simple.expect("simple")) }; - self.call(func, &args.iter().map(|arg| arg.immediate()).collect::>(), None) + self.call(self.type_void(), func, &args.iter().map(|arg| arg.immediate()).collect::>(), None) }, sym::likely => { self.expect(args[0].immediate(), true) @@ -392,7 +392,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { fn abort(&mut self) { let func = self.context.get_builtin_function("abort"); let func: RValue<'gcc> = unsafe { std::mem::transmute(func) }; - self.call(func, &[], None); + self.call(self.type_void(), func, &[], None); } fn assume(&mut self, value: Self::Value) { @@ -1075,7 +1075,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { fn try_intrinsic<'gcc, 'tcx>(bx: &mut Builder<'_, 'gcc, 'tcx>, try_func: RValue<'gcc>, data: RValue<'gcc>, _catch_func: RValue<'gcc>, dest: RValue<'gcc>) { if bx.sess().panic_strategy() == PanicStrategy::Abort { - bx.call(try_func, &[data], None); + bx.call(bx.type_void(), try_func, &[data], None); // Return 0 unconditionally from the intrinsic call; // we can never unwind. let ret_align = bx.tcx.data_layout.i32_align.abi; diff --git a/src/type_.rs b/src/type_.rs index 90f7d9d9bbace..2ef90bca5ac20 100644 --- a/src/type_.rs +++ b/src/type_.rs @@ -293,6 +293,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } } + // NOTE: see note above. Some other test uses usize::MAX. + if len == u64::MAX { + len = 0; + } + let len: i32 = len.try_into().expect("array len"); self.context.new_array_type(None, ty, len) diff --git a/src/type_of.rs b/src/type_of.rs index c5db0a1b2e459..010805808d2a3 100644 --- a/src/type_of.rs +++ b/src/type_of.rs @@ -363,4 +363,10 @@ impl<'gcc, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> { unimplemented!(); //ty.gcc_type(self) } + + fn fn_decl_backend_type(&self, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Type<'gcc> { + // FIXME: return correct type. + self.type_void() + //fn_abi.gcc_type(self) + } } From e228f0c16ea8c34794a6285bf57aab627c26b147 Mon Sep 17 00:00:00 2001 From: antoyo Date: Sun, 15 Aug 2021 08:28:46 -0400 Subject: [PATCH 026/621] Cleanup (#67) --- build_sysroot/prepare_sysroot_src.sh | 2 +- config.sh | 6 +- example/mini_core_hello_world.rs | 2 +- example/std_example.rs | 5 +- gcc_path | 1 + src/abi.rs | 130 +---- src/allocator.rs | 8 +- src/asm.rs | 200 +------ src/back/write.rs | 188 +----- src/base.rs | 19 +- src/builder.rs | 471 +++------------ src/callee.rs | 24 +- src/common.rs | 35 +- src/consts.rs | 137 +---- src/context.rs | 56 +- src/coverageinfo.rs | 79 +-- src/debuginfo.rs | 336 +---------- src/declare.rs | 55 +- src/intrinsic/llvm.rs | 6 +- src/intrinsic/mod.rs | 295 +--------- src/intrinsic/simd.rs | 836 +-------------------------- src/lib.rs | 49 +- src/mono_item.rs | 22 +- src/type_.rs | 98 +--- src/type_of.rs | 29 +- src/va_arg.rs | 179 ------ test.sh | 50 +- 27 files changed, 265 insertions(+), 3053 deletions(-) delete mode 100644 src/va_arg.rs diff --git a/build_sysroot/prepare_sysroot_src.sh b/build_sysroot/prepare_sysroot_src.sh index 4d18092419a4b..071e7ed1f85df 100755 --- a/build_sysroot/prepare_sysroot_src.sh +++ b/build_sysroot/prepare_sysroot_src.sh @@ -21,7 +21,7 @@ echo "[GIT] add" git add . echo "[GIT] commit" -# This is needed on virgin system where nothing is configured. +# This is needed on systems where nothing is configured. # git really needs something here, or it will fail. # Even using --author is not enough. git config user.email || git config user.email "none@example.com" diff --git a/config.sh b/config.sh index 2311600d1cf57..bd2d915f589a0 100644 --- a/config.sh +++ b/config.sh @@ -31,9 +31,8 @@ if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then fi export RUSTFLAGS=$linker' -Cpanic=abort -Cdebuginfo=2 -Zpanic-abort-tests -Zcodegen-backend='$(pwd)'/target/'$CHANNEL'/librustc_codegen_gcc.'$dylib_ext' --sysroot '$(pwd)'/build_sysroot/sysroot' -#export RUSTFLAGS=$linker' -Cpanic=abort -Cdebuginfo=2 -Zpanic-abort-tests -Zcodegen-backend='$(pwd)'/target/'$CHANNEL'/librustc_codegen_gcc.'$dylib_ext' --sysroot '$(pwd)'/build_sysroot/sysroot -Clto=fat -Cembed-bitcode=yes' -# FIXME remove once the atomic shim is gone +# FIXME(antoyo): remove once the atomic shim is gone if [[ `uname` == 'Darwin' ]]; then export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-undefined -Clink-arg=dynamic_lookup" fi @@ -43,6 +42,3 @@ export RUSTC_LOG=warn # display metadata load errors export LD_LIBRARY_PATH="$(pwd)/target/out:$(pwd)/build_sysroot/sysroot/lib/rustlib/$TARGET_TRIPLE/lib:$GCC_PATH" export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH - -export CG_CLIF_DISPLAY_CG_TIME=1 -export CG_CLIF_INCR_CACHE_DISABLED=1 diff --git a/example/mini_core_hello_world.rs b/example/mini_core_hello_world.rs index ccf4b53a702e5..69d591565acfa 100644 --- a/example/mini_core_hello_world.rs +++ b/example/mini_core_hello_world.rs @@ -253,7 +253,7 @@ fn main() { } } - // TODO: not sure about this assert. ABC is not defined, so should it be really 0? + // TODO(antoyo): to make this work, support weak linkage. //unsafe { assert_eq!(ABC as usize, 0); } &mut (|| Some(0 as *const ())) as &mut dyn FnMut() -> Option<*const ()>; diff --git a/example/std_example.rs b/example/std_example.rs index d99d6bb4b855e..eba0eb8289600 100644 --- a/example/std_example.rs +++ b/example/std_example.rs @@ -16,7 +16,6 @@ fn main() { let stderr = ::std::io::stderr(); let mut stderr = stderr.lock(); - // FIXME: this thread panics. std::thread::spawn(move || { println!("Hello from another thread!"); }); @@ -56,7 +55,7 @@ fn main() { assert_eq!(-32768i16, (-32768i16).saturating_add(-32768)); assert_eq!(32767i16, 32767i16.saturating_add(1)); - /*assert_eq!(0b0000000000000000000000000010000010000000000000000000000000000000_0000000000100000000000000000000000001000000000000100000000000000u128.leading_zeros(), 26); + assert_eq!(0b0000000000000000000000000010000010000000000000000000000000000000_0000000000100000000000000000000000001000000000000100000000000000u128.leading_zeros(), 26); assert_eq!(0b0000000000000000000000000010000000000000000000000000000000000000_0000000000000000000000000000000000001000000000000000000010000000u128.trailing_zeros(), 7); let _d = 0i128.checked_div(2i128); @@ -85,7 +84,7 @@ fn main() { 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);*/ + assert_eq!(houndred_f64 as i128, 100); let _a = 1u32 << 2u8; diff --git a/gcc_path b/gcc_path index e69de29bb2d1d..b3696e3cf885e 100644 --- a/gcc_path +++ b/gcc_path @@ -0,0 +1 @@ +/home/bouanto/Ordinateur/Programmation/Projets/gcc-build/build/gcc diff --git a/src/abi.rs b/src/abi.rs index 95d536dc51c5f..ce428c589a478 100644 --- a/src/abi.rs +++ b/src/abi.rs @@ -11,8 +11,7 @@ use crate::type_of::LayoutGccExt; impl<'a, 'gcc, 'tcx> AbiBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { fn apply_attrs_callsite(&mut self, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _callsite: Self::Value) { - // TODO - //fn_abi.apply_attrs_callsite(self, callsite) + // TODO(antoyo) } fn get_param(&self, index: usize) -> Self::Value { @@ -87,12 +86,9 @@ impl GccType for Reg { } pub trait FnAbiGccExt<'gcc, 'tcx> { - // TODO: return a function pointer type instead? + // TODO(antoyo): return a function pointer type instead? fn gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> (Type<'gcc>, Vec>, bool); fn ptr_to_gcc_type(&self, cx: &CodegenCx<'gcc, 'tcx>) -> Type<'gcc>; - /*fn llvm_cconv(&self) -> llvm::CallConv; - fn apply_attrs_llfn(&self, cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value); - fn apply_attrs_callsite(&self, bx: &mut Builder<'a, 'll, 'tcx>, callsite: &'ll Value);*/ } impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { @@ -145,12 +141,7 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { continue; } PassMode::Indirect { extra_attrs: Some(_), .. } => { - /*let ptr_ty = cx.tcx.mk_mut_ptr(arg.layout.ty); - let ptr_layout = cx.layout_of(ptr_ty); - argument_tys.push(ptr_layout.scalar_pair_element_gcc_type(cx, 0, true)); - argument_tys.push(ptr_layout.scalar_pair_element_gcc_type(cx, 1, true));*/ unimplemented!(); - //continue; } PassMode::Cast(cast) => cast.gcc_type(cx), PassMode::Indirect { extra_attrs: None, .. } => cx.type_ptr_to(arg.memory_ty(cx)), @@ -166,121 +157,4 @@ impl<'gcc, 'tcx> FnAbiGccExt<'gcc, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { let pointer_type = cx.context.new_function_pointer_type(None, return_type, ¶ms, variadic); pointer_type } - - /*fn llvm_cconv(&self) -> llvm::CallConv { - match self.conv { - Conv::C | Conv::Rust => llvm::CCallConv, - Conv::AmdGpuKernel => llvm::AmdGpuKernel, - Conv::ArmAapcs => llvm::ArmAapcsCallConv, - Conv::Msp430Intr => llvm::Msp430Intr, - Conv::PtxKernel => llvm::PtxKernel, - Conv::X86Fastcall => llvm::X86FastcallCallConv, - Conv::X86Intr => llvm::X86_Intr, - Conv::X86Stdcall => llvm::X86StdcallCallConv, - Conv::X86ThisCall => llvm::X86_ThisCall, - Conv::X86VectorCall => llvm::X86_VectorCall, - Conv::X86_64SysV => llvm::X86_64_SysV, - Conv::X86_64Win64 => llvm::X86_64_Win64, - } - } - - fn apply_attrs_llfn(&self, cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value) { - // FIXME(eddyb) can this also be applied to callsites? - if self.ret.layout.abi.is_uninhabited() { - llvm::Attribute::NoReturn.apply_llfn(llvm::AttributePlace::Function, llfn); - } - - // FIXME(eddyb, wesleywiser): apply this to callsites as well? - if !self.can_unwind { - llvm::Attribute::NoUnwind.apply_llfn(llvm::AttributePlace::Function, llfn); - } - - let mut i = 0; - let mut apply = |attrs: &ArgAttributes, ty: Option<&Type>| { - attrs.apply_llfn(llvm::AttributePlace::Argument(i), llfn, ty); - i += 1; - }; - match self.ret.mode { - PassMode::Direct(ref attrs) => { - attrs.apply_llfn(llvm::AttributePlace::ReturnValue, llfn, None); - } - PassMode::Indirect(ref attrs, _) => apply(attrs, Some(self.ret.layout.gcc_type(cx))), - _ => {} - } - for arg in &self.args { - if arg.pad.is_some() { - apply(&ArgAttributes::new(), None); - } - match arg.mode { - PassMode::Ignore => {} - PassMode::Direct(ref attrs) | PassMode::Indirect(ref attrs, None) => { - apply(attrs, Some(arg.layout.gcc_type(cx))) - } - PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => { - apply(attrs, None); - apply(extra_attrs, None); - } - PassMode::Pair(ref a, ref b) => { - apply(a, None); - apply(b, None); - } - PassMode::Cast(_) => apply(&ArgAttributes::new(), None), - } - } - } - - fn apply_attrs_callsite(&self, bx: &mut Builder<'a, 'll, 'tcx>, callsite: &'ll Value) { - // FIXME(wesleywiser, eddyb): We should apply `nounwind` and `noreturn` as appropriate to this callsite. - - let mut i = 0; - let mut apply = |attrs: &ArgAttributes, ty: Option<&Type>| { - attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite, ty); - i += 1; - }; - match self.ret.mode { - PassMode::Direct(ref attrs) => { - attrs.apply_callsite(llvm::AttributePlace::ReturnValue, callsite, None); - } - PassMode::Indirect(ref attrs, _) => apply(attrs, Some(self.ret.layout.gcc_type(bx))), - _ => {} - } - if let abi::Abi::Scalar(ref scalar) = self.ret.layout.abi { - // If the value is a boolean, the range is 0..2 and that ultimately - // become 0..0 when the type becomes i1, which would be rejected - // by the LLVM verifier. - if let Int(..) = scalar.value { - if !scalar.is_bool() { - let range = scalar.valid_range_exclusive(bx); - if range.start != range.end { - bx.range_metadata(callsite, range); - } - } - } - } - for arg in &self.args { - if arg.pad.is_some() { - apply(&ArgAttributes::new(), None); - } - match arg.mode { - PassMode::Ignore => {} - PassMode::Direct(ref attrs) | PassMode::Indirect(ref attrs, None) => { - apply(attrs, Some(arg.layout.gcc_type(bx))) - } - PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => { - apply(attrs, None); - apply(extra_attrs, None); - } - PassMode::Pair(ref a, ref b) => { - apply(a, None); - apply(b, None); - } - PassMode::Cast(_) => apply(&ArgAttributes::new(), None), - } - } - - let cconv = self.llvm_cconv(); - if cconv != llvm::CCallConv { - llvm::SetInstructionCallConv(callsite, cconv); - } - }*/ } diff --git a/src/allocator.rs b/src/allocator.rs index 11b2fad3e7eee..4e622efcaa0ba 100644 --- a/src/allocator.rs +++ b/src/allocator.rs @@ -1,4 +1,3 @@ -//use crate::attributes; use gccjit::{FunctionType, ToRValue}; use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS}; use rustc_middle::bug; @@ -50,11 +49,10 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, kind: Alloc let func = context.new_function(None, FunctionType::Exported, output.unwrap_or(void), &args, name, false); if tcx.sess.target.options.default_hidden_visibility { - //llvm::LLVMRustSetVisibility(func, llvm::Visibility::Hidden); + // TODO(antoyo): set visibility. } if tcx.sess.must_emit_unwind_tables() { - // TODO - //attributes::emit_uwtable(func, true); + // TODO(antoyo): emit unwind tables. } let callee = kind.fn_name(method.name); @@ -62,7 +60,7 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut GccContext, kind: Alloc .map(|(index, typ)| context.new_parameter(None, *typ, &format!("param{}", index))) .collect(); let callee = context.new_function(None, FunctionType::Extern, output.unwrap_or(void), &args, callee, false); - //llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden); + // TODO(antoyo): set visibility. let block = func.new_block("entry"); diff --git a/src/asm.rs b/src/asm.rs index 6616366235fcd..e4d57c39de48a 100644 --- a/src/asm.rs +++ b/src/asm.rs @@ -15,106 +15,8 @@ use crate::type_of::LayoutGccExt; impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { fn codegen_llvm_inline_asm(&mut self, _ia: &LlvmInlineAsmInner, _outputs: Vec>>, mut _inputs: Vec>, _span: Span) -> bool { - // TODO + // TODO(antoyo) return true; - - /*let mut ext_constraints = vec![]; - let mut output_types = vec![]; - - // Prepare the output operands - let mut indirect_outputs = vec![]; - for (i, (out, &place)) in ia.outputs.iter().zip(&outputs).enumerate() { - if out.is_rw { - let operand = self.load_operand(place); - if let OperandValue::Immediate(_) = operand.val { - inputs.push(operand.immediate()); - } - ext_constraints.push(i.to_string()); - } - if out.is_indirect { - let operand = self.load_operand(place); - if let OperandValue::Immediate(_) = operand.val { - indirect_outputs.push(operand.immediate()); - } - } else { - output_types.push(place.layout.gcc_type(self.cx())); - } - } - if !indirect_outputs.is_empty() { - indirect_outputs.extend_from_slice(&inputs); - inputs = indirect_outputs; - } - - let clobbers = ia.clobbers.iter().map(|s| format!("~{{{}}}", &s)); - - // Default per-arch clobbers - // Basically what clang does - let arch_clobbers = match &self.sess().target.target.arch[..] { - "x86" | "x86_64" => vec!["~{dirflag}", "~{fpsr}", "~{flags}"], - "mips" | "mips64" => vec!["~{$1}"], - _ => Vec::new(), - }; - - let all_constraints = ia - .outputs - .iter() - .map(|out| out.constraint.to_string()) - .chain(ia.inputs.iter().map(|s| s.to_string())) - .chain(ext_constraints) - .chain(clobbers) - .chain(arch_clobbers.iter().map(|s| (*s).to_string())) - .collect::>() - .join(","); - - debug!("Asm Constraints: {}", &all_constraints); - - // Depending on how many outputs we have, the return type is different - let num_outputs = output_types.len(); - let output_type = match num_outputs { - 0 => self.type_void(), - 1 => output_types[0], - _ => self.type_struct(&output_types, false), - }; - - let asm = ia.asm.as_str(); - let r = inline_asm_call( - self, - &asm, - &all_constraints, - &inputs, - output_type, - ia.volatile, - ia.alignstack, - ia.dialect, - ); - if r.is_none() { - return false; - } - let r = r.unwrap(); - - // Again, based on how many outputs we have - let outputs = ia.outputs.iter().zip(&outputs).filter(|&(ref o, _)| !o.is_indirect); - for (i, (_, &place)) in outputs.enumerate() { - let v = if num_outputs == 1 { r } else { self.extract_value(r, i as u64) }; - OperandValue::Immediate(v).store(self, place); - } - - // Store mark in a metadata node so we can map LLVM errors - // back to source locations. See #17552. - unsafe { - let key = "srcloc"; - let kind = llvm::LLVMGetMDKindIDInContext( - self.llcx, - key.as_ptr() as *const c_char, - key.len() as c_uint, - ); - - let val: &'ll Value = self.const_i32(span.ctxt().outer_expn().as_u32() as i32); - - llvm::LLVMSetMetadata(r, kind, llvm::LLVMMDNodeInContext(self.llcx, &val, 1)); - } - - true*/ } fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, _span: &[Span]) { @@ -127,7 +29,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { }; // Collect the types of output operands - // FIXME: we do this here instead of later because of a bug in libgccjit where creating the + // FIXME(antoyo): we do this here instead of later because of a bug in libgccjit where creating the // variable after the extended asm expression causes a segfault: // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100380 let mut output_vars = FxHashMap::default(); @@ -160,11 +62,6 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { match out_place { Some(place) => place.layout.gcc_type(self.cx, false), None => { - // If the output is discarded, we don't really care what - // type is used. We're just using this to tell GCC to - // reserve the register. - //dummy_output_type(self.cx, reg.reg_class()) - // NOTE: if no output value, we should not create one. continue; }, @@ -251,9 +148,9 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { template_str } else { - // FIXME: this might break the "m" memory constraint: + // FIXME(antoyo): this might break the "m" memory constraint: // https://stackoverflow.com/a/9347957/389119 - // TODO: only set on x86 platforms. + // TODO(antoyo): only set on x86 platforms. format!(".att_syntax noprefix\n\t{}\n\t.intel_syntax noprefix", template_str) }; let extended_asm = block.add_extended_asm(None, &template_str); @@ -274,7 +171,6 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { }, }; output_types.push(ty); - //op_idx.insert(idx, constraints.len()); let prefix = if late { "=" } else { "=&" }; let constraint = format!("{}{}", prefix, reg_to_gcc(reg)); @@ -295,14 +191,13 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { None => dummy_output_type(self.cx, reg.reg_class()) }; output_types.push(ty); - //op_idx.insert(idx, constraints.len()); - // TODO: prefix of "+" for reading and writing? + // TODO(antoyo): prefix of "+" for reading and writing? let prefix = if late { "=" } else { "=&" }; let constraint = format!("{}{}", prefix, reg_to_gcc(reg)); if out_place.is_some() { let var = output_vars[&idx]; - // TODO: also specify an output operand when out_place is none: that would + // TODO(antoyo): also specify an output operand when out_place is none: that would // be the clobber but clobbers do not support general constraint like reg; // they only support named registers. // Not sure how we can do this. And the LLVM backend does not seem to add a @@ -321,63 +216,6 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { } } - /*if !options.contains(InlineAsmOptions::PRESERVES_FLAGS) { - match asm_arch { - InlineAsmArch::AArch64 | InlineAsmArch::Arm => { - constraints.push("~{cc}".to_string()); - } - InlineAsmArch::X86 | InlineAsmArch::X86_64 => { - constraints.extend_from_slice(&[ - "~{dirflag}".to_string(), - "~{fpsr}".to_string(), - "~{flags}".to_string(), - ]); - } - InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {} - } - } - if !options.contains(InlineAsmOptions::NOMEM) { - // This is actually ignored by LLVM, but it's probably best to keep - // it just in case. LLVM instead uses the ReadOnly/ReadNone - // attributes on the call instruction to optimize. - constraints.push("~{memory}".to_string()); - } - let volatile = !options.contains(InlineAsmOptions::PURE); - let alignstack = !options.contains(InlineAsmOptions::NOSTACK); - let output_type = match &output_types[..] { - [] => self.type_void(), - [ty] => ty, - tys => self.type_struct(&tys, false), - };*/ - - /*let result = inline_asm_call( - self, - &template_str, - &constraints.join(","), - &inputs, - output_type, - volatile, - alignstack, - dialect, - span, - ) - .unwrap_or_else(|| span_bug!(span, "LLVM asm constraint validation failed")); - - if options.contains(InlineAsmOptions::PURE) { - if options.contains(InlineAsmOptions::NOMEM) { - llvm::Attribute::ReadNone.apply_callsite(llvm::AttributePlace::Function, result); - } else if options.contains(InlineAsmOptions::READONLY) { - llvm::Attribute::ReadOnly.apply_callsite(llvm::AttributePlace::Function, result); - } - } else { - if options.contains(InlineAsmOptions::NOMEM) { - llvm::Attribute::InaccessibleMemOnly - .apply_callsite(llvm::AttributePlace::Function, result); - } else { - // LLVM doesn't have an attribute to represent ReadOnly + SideEffect - } - }*/ - // Write results to outputs for (idx, op) in operands.iter().enumerate() { if let InlineAsmOperandRef::Out { place: Some(place), .. } @@ -390,12 +228,12 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { } /// Converts a register class to a GCC constraint code. -// TODO: return &'static str instead? +// TODO(antoyo): return &'static str instead? fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> String { match reg { // For vector registers LLVM wants the register name to match the type size. InlineAsmRegOrRegClass::Reg(reg) => { - // TODO: add support for vector register. + // TODO(antoyo): add support for vector register. let constraint = match reg.name() { "ax" => "a", @@ -404,11 +242,11 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> String { "dx" => "d", "si" => "S", "di" => "D", - // TODO: for registers like r11, we have to create a register variable: https://stackoverflow.com/a/31774784/389119 - // TODO: in this case though, it's a clobber, so it should work as r11. + // TODO(antoyo): for registers like r11, we have to create a register variable: https://stackoverflow.com/a/31774784/389119 + // TODO(antoyo): in this case though, it's a clobber, so it should work as r11. // Recent nightly supports clobber() syntax, so update to it. It does not seem // like it's implemented yet. - name => name, // FIXME: probably wrong. + name => name, // FIXME(antoyo): probably wrong. }; constraint.to_string() }, @@ -570,7 +408,6 @@ fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg) | InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16) => { unimplemented!() - //if modifier == Some('v') { None } else { modifier } } InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg_thumb) => unimplemented!(), @@ -583,11 +420,6 @@ fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low8) | InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => { unimplemented!() - /*if modifier.is_none() { - Some('q') - } else { - modifier - }*/ } InlineAsmRegClass::Bpf(_) => unimplemented!(), InlineAsmRegClass::Hexagon(_) => unimplemented!(), @@ -612,15 +444,7 @@ fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => unimplemented!(), InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg) | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) - | InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => unimplemented!() /*match (reg, modifier) { - (X86InlineAsmRegClass::xmm_reg, None) => Some('x'), - (X86InlineAsmRegClass::ymm_reg, None) => Some('t'), - (X86InlineAsmRegClass::zmm_reg, None) => Some('g'), - (_, Some('x')) => Some('x'), - (_, Some('y')) => Some('t'), - (_, Some('z')) => Some('g'), - _ => unreachable!(), - }*/, + | InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => unimplemented!(), InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg) => unimplemented!(), InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => unimplemented!(), InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(), diff --git a/src/back/write.rs b/src/back/write.rs index 5201e49427799..c3e3847823d91 100644 --- a/src/back/write.rs +++ b/src/back/write.rs @@ -15,132 +15,18 @@ pub(crate) unsafe fn codegen(cgcx: &CodegenContext, _diag_han { let context = &module.module_llvm.context; - //let llcx = &*module.module_llvm.llcx; - //let tm = &*module.module_llvm.tm; let module_name = module.name.clone(); let module_name = Some(&module_name[..]); - //let handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx); - - /*if cgcx.msvc_imps_needed { - create_msvc_imps(cgcx, llcx, llmod); - }*/ - - // A codegen-specific pass manager is used to generate object - // files for an GCC module. - // - // Apparently each of these pass managers is a one-shot kind of - // thing, so we create a new one for each type of output. The - // pass manager passed to the closure should be ensured to not - // escape the closure itself, and the manager should only be - // used once. - /*unsafe fn with_codegen<'ll, F, R>(tm: &'ll llvm::TargetMachine, llmod: &'ll llvm::Module, no_builtins: bool, f: F) -> R - where F: FnOnce(&'ll mut PassManager<'ll>) -> R, - { - let cpm = llvm::LLVMCreatePassManager(); - llvm::LLVMAddAnalysisPasses(tm, cpm); - llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins); - f(cpm) - }*/ - - // Two things to note: - // - If object files are just LLVM bitcode we write bitcode, copy it to - // the .o file, and delete the bitcode if it wasn't otherwise - // requested. - // - If we don't have the integrated assembler then we need to emit - // asm from LLVM and use `gcc` to create the object file. let _bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name); let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name); if config.bitcode_needed() { - // TODO - /*let _timer = cgcx - .prof - .generic_activity_with_arg("LLVM_module_codegen_make_bitcode", &module.name[..]); - let thin = ThinBuffer::new(llmod); - let data = thin.data(); - - if config.emit_bc || config.emit_obj == EmitObj::Bitcode { - let _timer = cgcx.prof.generic_activity_with_arg( - "LLVM_module_codegen_emit_bitcode", - &module.name[..], - ); - if let Err(e) = fs::write(&bc_out, data) { - let msg = format!("failed to write bytecode to {}: {}", bc_out.display(), e); - diag_handler.err(&msg); - } - } - - if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Full) { - let _timer = cgcx.prof.generic_activity_with_arg( - "LLVM_module_codegen_embed_bitcode", - &module.name[..], - ); - embed_bitcode(cgcx, llcx, llmod, Some(data)); - } - - if config.emit_bc_compressed { - let _timer = cgcx.prof.generic_activity_with_arg( - "LLVM_module_codegen_emit_compressed_bitcode", - &module.name[..], - ); - let dst = bc_out.with_extension(RLIB_BYTECODE_EXTENSION); - let data = bytecode::encode(&module.name, data); - if let Err(e) = fs::write(&dst, data) { - let msg = format!("failed to write bytecode to {}: {}", dst.display(), e); - diag_handler.err(&msg); - } - }*/ - } /*else if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Marker) { - unimplemented!(); - //embed_bitcode(cgcx, llcx, llmod, None); - }*/ + // TODO(antoyo) + } if config.emit_ir { unimplemented!(); - /*let _timer = cgcx - .prof - .generic_activity_with_arg("LLVM_module_codegen_emit_ir", &module.name[..]); - let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name); - let out_c = path_to_c_string(&out); - - extern "C" fn demangle_callback( - input_ptr: *const c_char, - input_len: size_t, - output_ptr: *mut c_char, - output_len: size_t, - ) -> size_t { - let input = - unsafe { slice::from_raw_parts(input_ptr as *const u8, input_len as usize) }; - - let input = match str::from_utf8(input) { - Ok(s) => s, - Err(_) => return 0, - }; - - let output = unsafe { - slice::from_raw_parts_mut(output_ptr as *mut u8, output_len as usize) - }; - let mut cursor = io::Cursor::new(output); - - let demangled = match rustc_demangle::try_demangle(input) { - Ok(d) => d, - Err(_) => return 0, - }; - - if write!(cursor, "{:#}", demangled).is_err() { - // Possible only if provided buffer is not big enough - return 0; - } - - cursor.position() as size_t - } - - let result = llvm::LLVMRustPrintModule(llmod, out_c.as_ptr(), demangle_callback); - result.into_result().map_err(|()| { - let msg = format!("failed to write LLVM IR to {}", out.display()); - llvm_err(diag_handler, &msg) - })?;*/ } if config.emit_asm { @@ -149,10 +35,6 @@ pub(crate) unsafe fn codegen(cgcx: &CodegenContext, _diag_han .generic_activity_with_arg("LLVM_module_codegen_emit_asm", &module.name[..]); let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); context.compile_to_file(OutputKind::Assembler, path.to_str().expect("path to str")); - - /*with_codegen(tm, llmod, config.no_builtins, |cpm| { - write_output_file(diag_handler, tm, cpm, llmod, &path, llvm::FileType::AssemblyFile) - })?;*/ } match config.emit_obj { @@ -160,47 +42,27 @@ pub(crate) unsafe fn codegen(cgcx: &CodegenContext, _diag_han let _timer = cgcx .prof .generic_activity_with_arg("LLVM_module_codegen_emit_obj", &module.name[..]); - //with_codegen(tm, llmod, config.no_builtins, |cpm| { - //println!("1: {}", module.name); - match &*module.name { - "std_example.7rcbfp3g-cgu.15" => { - println!("Dumping reproducer {}", module.name); - let _ = fs::create_dir("/tmp/reproducers"); - // FIXME: segfault in dump_reproducer_to_file() might be caused by - // transmuting an rvalue to an lvalue. - // Segfault is actually in gcc::jit::reproducer::get_identifier_as_lvalue - context.dump_reproducer_to_file(&format!("/tmp/reproducers/{}.c", module.name)); - println!("Dumped reproducer {}", module.name); - }, - _ => (), - } - /*let _ = fs::create_dir("/tmp/dumps"); - context.dump_to_file(&format!("/tmp/dumps/{}.c", module.name), true); - println!("Dumped {}", module.name);*/ - //println!("Compile module {}", module.name); - context.compile_to_file(OutputKind::ObjectFile, obj_out.to_str().expect("path to str")); - //})?; + match &*module.name { + "std_example.7rcbfp3g-cgu.15" => { + println!("Dumping reproducer {}", module.name); + let _ = fs::create_dir("/tmp/reproducers"); + // FIXME(antoyo): segfault in dump_reproducer_to_file() might be caused by + // transmuting an rvalue to an lvalue. + // Segfault is actually in gcc::jit::reproducer::get_identifier_as_lvalue + context.dump_reproducer_to_file(&format!("/tmp/reproducers/{}.c", module.name)); + println!("Dumped reproducer {}", module.name); + }, + _ => (), + } + context.compile_to_file(OutputKind::ObjectFile, obj_out.to_str().expect("path to str")); } EmitObj::Bitcode => { - //unimplemented!(); - /*debug!("copying bitcode {:?} to obj {:?}", bc_out, obj_out); - if let Err(e) = link_or_copy(&bc_out, &obj_out) { - diag_handler.err(&format!("failed to copy bitcode to object file: {}", e)); - } - - if !config.emit_bc { - debug!("removing_bitcode {:?}", bc_out); - if let Err(e) = fs::remove_file(&bc_out) { - diag_handler.err(&format!("failed to remove bitcode: {}", e)); - } - }*/ + // TODO(antoyo) } EmitObj::None => {} } - - //drop(handlers); } Ok(module.into_compiled_module( @@ -213,22 +75,4 @@ pub(crate) unsafe fn codegen(cgcx: &CodegenContext, _diag_han pub(crate) fn link(_cgcx: &CodegenContext, _diag_handler: &Handler, mut _modules: Vec>) -> Result, FatalError> { unimplemented!(); - /*use super::lto::{Linker, ModuleBuffer}; - // Sort the modules by name to ensure to ensure deterministic behavior. - modules.sort_by(|a, b| a.name.cmp(&b.name)); - let (first, elements) = - modules.split_first().expect("Bug! modules must contain at least one module."); - - let mut linker = Linker::new(first.module_llvm.llmod()); - for module in elements { - let _timer = - cgcx.prof.generic_activity_with_arg("LLVM_link_module", format!("{:?}", module.name)); - let buffer = ModuleBuffer::new(module.module_llvm.llmod()); - linker.add(&buffer.data()).map_err(|()| { - let msg = format!("failed to serialize module {:?}", module.name); - llvm_err(&diag_handler, &msg) - })?; - } - drop(linker); - Ok(modules.remove(0))*/ } diff --git a/src/base.rs b/src/base.rs index 7050f122a1765..2963a3d49a20e 100644 --- a/src/base.rs +++ b/src/base.rs @@ -35,7 +35,7 @@ pub fn global_linkage_to_gcc(linkage: Linkage) -> GlobalKind { Linkage::Appending => unimplemented!(), Linkage::Internal => GlobalKind::Internal, Linkage::Private => GlobalKind::Internal, - Linkage::ExternalWeak => GlobalKind::Imported, // TODO: should be weak linkage. + Linkage::ExternalWeak => GlobalKind::Imported, // TODO(antoyo): should be weak linkage. Linkage::Common => unimplemented!(), } } @@ -46,7 +46,7 @@ pub fn linkage_to_gcc(linkage: Linkage) -> FunctionType { Linkage::AvailableExternally => FunctionType::Extern, Linkage::LinkOnceAny => unimplemented!(), Linkage::LinkOnceODR => unimplemented!(), - Linkage::WeakAny => FunctionType::Exported, // FIXME: should be similar to linkonce. + Linkage::WeakAny => FunctionType::Exported, // FIXME(antoyo): should be similar to linkonce. Linkage::WeakODR => unimplemented!(), Linkage::Appending => unimplemented!(), Linkage::Internal => FunctionType::Internal, @@ -74,19 +74,25 @@ pub fn compile_codegen_unit<'tcx>(tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (Modul // Instantiate monomorphizations without filling out definitions yet... //let llvm_module = ModuleLlvm::new(tcx, &cgu_name.as_str()); let context = Context::default(); - // TODO: only set on x86 platforms. + // TODO(antoyo): only set on x86 platforms. context.add_command_line_option("-masm=intel"); for arg in &tcx.sess.opts.cg.llvm_args { context.add_command_line_option(arg); } context.add_command_line_option("-fno-semantic-interposition"); - //context.set_dump_code_on_compile(true); + if env::var("CG_GCCJIT_DUMP_CODE").as_deref() == Ok("1") { + context.set_dump_code_on_compile(true); + } if env::var("CG_GCCJIT_DUMP_GIMPLE").as_deref() == Ok("1") { context.set_dump_initial_gimple(true); } context.set_debug_info(true); - //context.set_dump_everything(true); - //context.set_keep_intermediates(true); + if env::var("CG_GCCJIT_DUMP_EVERYTHING").as_deref() == Ok("1") { + context.set_dump_everything(true); + } + if env::var("CG_GCCJIT_KEEP_INTERMEDIATES").as_deref() == Ok("1") { + context.set_keep_intermediates(true); + } { let cx = CodegenCx::new(&context, cgu, tcx); @@ -100,7 +106,6 @@ pub fn compile_codegen_unit<'tcx>(tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (Modul block.end_with_void_return(None); }); - //println!("module_codegen: {:?} {:?}", cgu_name, &cx.context as *const _); let mono_items = cgu.items_in_deterministic_order(tcx); for &(mono_item, (linkage, visibility)) in &mono_items { mono_item.predefine::>(&cx, linkage, visibility); diff --git a/src/builder.rs b/src/builder.rs index bb864c27e1b4d..0f7db911552a1 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -48,10 +48,10 @@ use crate::common::{SignType, TypeReflection, type_is_pointer}; use crate::context::CodegenCx; use crate::type_of::LayoutGccExt; -// TODO +// TODO(antoyo) type Funclet = (); -// TODO: remove this variable. +// TODO(antoyo): remove this variable. static mut RETURN_VALUE_COUNT: usize = 0; enum ExtremumOperation { @@ -99,7 +99,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { let load_ordering = match order { - // TODO: does this make sense? + // TODO(antoyo): does this make sense? AtomicOrdering::AcquireRelease | AtomicOrdering::Release => AtomicOrdering::Acquire, _ => order.clone(), }; @@ -162,26 +162,6 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { } fn check_call<'b>(&mut self, _typ: &str, func: Function<'gcc>, args: &'b [RValue<'gcc>]) -> Cow<'b, [RValue<'gcc>]> { - //let mut fn_ty = self.cx.val_ty(func); - // Strip off pointers - /*while self.cx.type_kind(fn_ty) == TypeKind::Pointer { - fn_ty = self.cx.element_type(fn_ty); - }*/ - - /*assert!( - self.cx.type_kind(fn_ty) == TypeKind::Function, - "builder::{} not passed a function, but {:?}", - typ, - fn_ty - ); - - let param_tys = self.cx.func_params_types(fn_ty); - - let all_args_match = param_tys - .iter() - .zip(args.iter().map(|&v| self.val_ty(v))) - .all(|(expected_ty, actual_ty)| *expected_ty == actual_ty);*/ - let mut all_args_match = true; let mut param_types = vec![]; let param_count = func.get_param_count(); @@ -205,16 +185,6 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { .map(|(_i, (expected_ty, &actual_val))| { let actual_ty = actual_val.get_type(); if expected_ty != actual_ty { - /*debug!( - "type mismatch in function call of {:?}. \ - Expected {:?} for param {}, got {:?}; injecting bitcast", - func, expected_ty, i, actual_ty - );*/ - /*println!( - "type mismatch in function call of {:?}. \ - Expected {:?} for param {}, got {:?}; injecting bitcast", - func, expected_ty, i, actual_ty - );*/ self.bitcast(actual_val, expected_ty) } else { @@ -227,26 +197,6 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { } fn check_ptr_call<'b>(&mut self, _typ: &str, func_ptr: RValue<'gcc>, args: &'b [RValue<'gcc>]) -> Cow<'b, [RValue<'gcc>]> { - //let mut fn_ty = self.cx.val_ty(func); - // Strip off pointers - /*while self.cx.type_kind(fn_ty) == TypeKind::Pointer { - fn_ty = self.cx.element_type(fn_ty); - }*/ - - /*assert!( - self.cx.type_kind(fn_ty) == TypeKind::Function, - "builder::{} not passed a function, but {:?}", - typ, - fn_ty - ); - - let param_tys = self.cx.func_params_types(fn_ty); - - let all_args_match = param_tys - .iter() - .zip(args.iter().map(|&v| self.val_ty(v))) - .all(|(expected_ty, actual_ty)| *expected_ty == actual_ty);*/ - let mut all_args_match = true; let mut param_types = vec![]; let gcc_func = func_ptr.get_type().is_function_ptr_type().expect("function ptr"); @@ -269,16 +219,6 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { .map(|(_i, (expected_ty, &actual_val))| { let actual_ty = actual_val.get_type(); if expected_ty != actual_ty { - /*debug!( - "type mismatch in function call of {:?}. \ - Expected {:?} for param {}, got {:?}; injecting bitcast", - func, expected_ty, i, actual_ty - );*/ - /*println!( - "type mismatch in function call of {:?}. \ - Expected {:?} for param {}, got {:?}; injecting bitcast", - func, expected_ty, i, actual_ty - );*/ self.bitcast(actual_val, expected_ty) } else { @@ -291,27 +231,14 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { } fn check_store(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>) -> RValue<'gcc> { - let dest_ptr_ty = self.cx.val_ty(ptr).make_pointer(); // TODO: make sure make_pointer() is okay here. + let dest_ptr_ty = self.cx.val_ty(ptr).make_pointer(); // TODO(antoyo): make sure make_pointer() is okay here. let stored_ty = self.cx.val_ty(val); let stored_ptr_ty = self.cx.type_ptr_to(stored_ty); - //assert_eq!(self.cx.type_kind(dest_ptr_ty), TypeKind::Pointer); - if dest_ptr_ty == stored_ptr_ty { ptr } else { - /*debug!( - "type mismatch in store. \ - Expected {:?}, got {:?}; inserting bitcast", - dest_ptr_ty, stored_ptr_ty - );*/ - /*println!( - "type mismatch in store. \ - Expected {:?}, got {:?}; inserting bitcast", - dest_ptr_ty, stored_ptr_ty - );*/ - //ptr self.bitcast(ptr, stored_ptr_ty) } } @@ -321,13 +248,9 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { } fn function_call(&mut self, func: RValue<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>) -> RValue<'gcc> { - //debug!("call {:?} with args ({:?})", func, args); - - // TODO: remove when the API supports a different type for functions. + // TODO(antoyo): remove when the API supports a different type for functions. let func: Function<'gcc> = self.cx.rvalue_as_function(func); let args = self.check_call("call", func, args); - //let bundle = funclet.map(|funclet| funclet.bundle()); - //let bundle = bundle.as_ref().map(|b| &*b.raw); // gccjit requires to use the result of functions, even when it's not used. // That's why we assign the result to a local or call add_eval(). @@ -349,11 +272,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { } fn function_ptr_call(&mut self, func_ptr: RValue<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>) -> RValue<'gcc> { - //debug!("func ptr call {:?} with args ({:?})", func, args); - let args = self.check_ptr_call("call", func_ptr, args); - //let bundle = funclet.map(|funclet| funclet.bundle()); - //let bundle = bundle.as_ref().map(|b| &*b.raw); // gccjit requires to use the result of functions, even when it's not used. // That's why we assign the result to a local or call add_eval(). @@ -363,7 +282,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { let void_type = self.context.new_type::<()>(); let current_func = current_block.get_function(); - // FIXME: As a temporary workaround for unsupported LLVM intrinsics. + // FIXME(antoyo): As a temporary workaround for unsupported LLVM intrinsics. if gcc_func.get_param_count() == 0 && format!("{:?}", func_ptr) == "__builtin_ia32_pmovmskb128" { return_type = self.int_type; } @@ -376,7 +295,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { } else { if gcc_func.get_param_count() == 0 { - // FIXME: As a temporary workaround for unsupported LLVM intrinsics. + // FIXME(antoyo): As a temporary workaround for unsupported LLVM intrinsics. current_block.add_eval(None, self.cx.context.new_call_through_ptr(None, func_ptr, &[])); } else { @@ -390,17 +309,12 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { } pub fn overflow_call(&mut self, func: Function<'gcc>, args: &[RValue<'gcc>], _funclet: Option<&Funclet>) -> RValue<'gcc> { - //debug!("overflow_call {:?} with args ({:?})", func, args); - - //let bundle = funclet.map(|funclet| funclet.bundle()); - //let bundle = bundle.as_ref().map(|b| &*b.raw); - // gccjit requires to use the result of functions, even when it's not used. // That's why we assign the result to a local. let return_type = self.context.new_type::(); let current_block = self.current_block.borrow().expect("block"); let current_func = current_block.get_function(); - // TODO: return the new_call() directly? Since the overflow function has no side-effects. + // TODO(antoyo): return the new_call() directly? Since the overflow function has no side-effects. unsafe { RETURN_VALUE_COUNT += 1 }; let result = current_func.new_local(None, return_type, &format!("returnValue{}", unsafe { RETURN_VALUE_COUNT })); current_block.add_assignment(None, result, self.cx.context.new_call(None, func, &args)); @@ -520,25 +434,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { self.llbb().end_with_conditional(None, condition, then, catch); self.context.new_rvalue_from_int(self.int_type, 0) - // TODO - /*debug!("invoke {:?} with args ({:?})", func, args); - - let args = self.check_call("invoke", func, args); - let bundle = funclet.map(|funclet| funclet.bundle()); - let bundle = bundle.as_ref().map(|b| &*b.raw); - - unsafe { - llvm::LLVMRustBuildInvoke( - self.llbuilder, - func, - args.as_ptr(), - args.len() as c_uint, - then, - catch, - bundle, - UNNAMED, - ) - }*/ + // TODO(antoyo) } fn unreachable(&mut self) { @@ -558,7 +454,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn add(&mut self, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> { - // FIXME: this should not be required. + // FIXME(antoyo): this should not be required. if format!("{:?}", a.get_type()) != format!("{:?}", b.get_type()) { b = self.context.new_cast(None, b, a.get_type()); } @@ -589,24 +485,24 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn udiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - // TODO: convert the arguments to unsigned? + // TODO(antoyo): convert the arguments to unsigned? a / b } fn exactudiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - // TODO: convert the arguments to unsigned? - // TODO: poison if not exact. + // TODO(antoyo): convert the arguments to unsigned? + // TODO(antoyo): poison if not exact. a / b } fn sdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - // TODO: convert the arguments to signed? + // TODO(antoyo): convert the arguments to signed? a / b } fn exactsdiv(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - // TODO: posion if not exact. - // FIXME: rustc_codegen_ssa::mir::intrinsic uses different types for a and b but they + // TODO(antoyo): posion if not exact. + // FIXME(antoyo): rustc_codegen_ssa::mir::intrinsic uses different types for a and b but they // should be the same. let typ = a.get_type().to_signed(self); let a = self.context.new_cast(None, a, typ); @@ -629,7 +525,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { fn frem(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { if a.get_type() == self.cx.float_type { let fmodf = self.context.get_builtin_function("fmodf"); - // FIXME: this seems to produce the wrong result. + // FIXME(antoyo): this seems to produce the wrong result. return self.context.new_call(None, fmodf, &[a, b]); } assert_eq!(a.get_type(), self.cx.double_type); @@ -639,18 +535,15 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn shl(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - // FIXME: remove the casts when libgccjit can shift an unsigned number by an unsigned number. + // FIXME(antoyo): remove the casts when libgccjit can shift an unsigned number by an unsigned number. let a_type = a.get_type(); let b_type = b.get_type(); if a_type.is_unsigned(self) && b_type.is_signed(self) { - //println!("shl: {:?} -> {:?}", a, b_type); let a = self.context.new_cast(None, a, b_type); let result = a << b; - //println!("shl: {:?} -> {:?}", result, a_type); self.context.new_cast(None, result, a_type) } else if a_type.is_signed(self) && b_type.is_unsigned(self) { - //println!("shl: {:?} -> {:?}", b, a_type); let b = self.context.new_cast(None, b, a_type); a << b } @@ -660,19 +553,16 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn lshr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - // FIXME: remove the casts when libgccjit can shift an unsigned number by an unsigned number. - // TODO: cast to unsigned to do a logical shift if that does not work. + // FIXME(antoyo): remove the casts when libgccjit can shift an unsigned number by an unsigned number. + // TODO(antoyo): cast to unsigned to do a logical shift if that does not work. let a_type = a.get_type(); let b_type = b.get_type(); if a_type.is_unsigned(self) && b_type.is_signed(self) { - //println!("lshl: {:?} -> {:?}", a, b_type); let a = self.context.new_cast(None, a, b_type); let result = a >> b; - //println!("lshl: {:?} -> {:?}", result, a_type); self.context.new_cast(None, result, a_type) } else if a_type.is_signed(self) && b_type.is_unsigned(self) { - //println!("lshl: {:?} -> {:?}", b, a_type); let b = self.context.new_cast(None, b, a_type); a >> b } @@ -682,19 +572,16 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn ashr(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - // TODO: check whether behavior is an arithmetic shift for >> . - // FIXME: remove the casts when libgccjit can shift an unsigned number by an unsigned number. + // TODO(antoyo): check whether behavior is an arithmetic shift for >> . + // FIXME(antoyo): remove the casts when libgccjit can shift an unsigned number by an unsigned number. let a_type = a.get_type(); let b_type = b.get_type(); if a_type.is_unsigned(self) && b_type.is_signed(self) { - //println!("ashl: {:?} -> {:?}", a, b_type); let a = self.context.new_cast(None, a, b_type); let result = a >> b; - //println!("ashl: {:?} -> {:?}", result, a_type); self.context.new_cast(None, result, a_type) } else if a_type.is_signed(self) && b_type.is_unsigned(self) { - //println!("ashl: {:?} -> {:?}", b, a_type); let b = self.context.new_cast(None, b, a_type); a >> b } @@ -704,7 +591,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn and(&mut self, a: RValue<'gcc>, mut b: RValue<'gcc>) -> RValue<'gcc> { - // FIXME: hack by putting the result in a variable to workaround this bug: + // FIXME(antoyo): hack by putting the result in a variable to workaround this bug: // https://gcc.gnu.org/bugzilla//show_bug.cgi?id=95498 if a.get_type() != b.get_type() { b = self.context.new_cast(None, b, a.get_type()); @@ -715,7 +602,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn or(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - // FIXME: hack by putting the result in a variable to workaround this bug: + // FIXME(antoyo): hack by putting the result in a variable to workaround this bug: // https://gcc.gnu.org/bugzilla//show_bug.cgi?id=95498 let res = self.current_func().new_local(None, b.get_type(), "orResult"); self.llbb().add_assignment(None, res, a | b); @@ -727,7 +614,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn neg(&mut self, a: RValue<'gcc>) -> RValue<'gcc> { - // TODO: use new_unary_op()? + // TODO(antoyo): use new_unary_op()? self.cx.context.new_rvalue_from_long(a.get_type(), 0) - a } @@ -759,7 +646,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn unchecked_usub(&mut self, a: RValue<'gcc>, b: RValue<'gcc>) -> RValue<'gcc> { - // TODO: should generate poison value? + // TODO(antoyo): should generate poison value? a - b } @@ -773,47 +660,22 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { fn fadd_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> { unimplemented!(); - /*unsafe { - let instr = llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - }*/ } fn fsub_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> { unimplemented!(); - /*unsafe { - let instr = llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - }*/ } fn fmul_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> { unimplemented!(); - /*unsafe { - let instr = llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - }*/ } fn fdiv_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> { unimplemented!(); - /*unsafe { - let instr = llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - }*/ } fn frem_fast(&mut self, _lhs: RValue<'gcc>, _rhs: RValue<'gcc>) -> RValue<'gcc> { unimplemented!(); - /*unsafe { - let instr = llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, UNNAMED); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - }*/ } fn checked_binop(&mut self, oop: OverflowOp, typ: Ty<'_>, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value) { @@ -827,7 +689,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { _ => panic!("tried to get overflow intrinsic for op applied to non-int type"), }; - // TODO: remove duplication with intrinsic? + // TODO(antoyo): remove duplication with intrinsic? let name = match oop { OverflowOp::Add => @@ -882,7 +744,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { let intrinsic = self.context.get_builtin_function(&name); let res = self.current_func() - // TODO: is it correct to use rhs type instead of the parameter typ? + // TODO(antoyo): is it correct to use rhs type instead of the parameter typ? .new_local(None, rhs.get_type(), "binopResult") .get_address(None); let overflow = self.overflow_call(intrinsic, &[lhs, rhs, res], None); @@ -890,7 +752,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn alloca(&mut self, ty: Type<'gcc>, align: Align) -> RValue<'gcc> { - // FIXME: this check that we don't call get_aligned() a second time on a time. + // FIXME(antoyo): this check that we don't call get_aligned() a second time on a type. // Ideally, we shouldn't need to do this check. let aligned_type = if ty == self.cx.u128_type || ty == self.cx.i128_type { @@ -899,37 +761,27 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { else { ty.get_aligned(align.bytes()) }; - // TODO: It might be better to return a LValue, but fixing the rustc API is non-trivial. + // TODO(antoyo): It might be better to return a LValue, but fixing the rustc API is non-trivial. self.stack_var_count.set(self.stack_var_count.get() + 1); self.current_func().new_local(None, aligned_type, &format!("stack_var_{}", self.stack_var_count.get())).get_address(None) } fn dynamic_alloca(&mut self, _ty: Type<'gcc>, _align: Align) -> RValue<'gcc> { unimplemented!(); - /*unsafe { - let alloca = llvm::LLVMBuildAlloca(self.llbuilder, ty, UNNAMED); - llvm::LLVMSetAlignment(alloca, align.bytes() as c_uint); - alloca - }*/ } fn array_alloca(&mut self, _ty: Type<'gcc>, _len: RValue<'gcc>, _align: Align) -> RValue<'gcc> { unimplemented!(); - /*unsafe { - let alloca = llvm::LLVMBuildArrayAlloca(self.llbuilder, ty, len, UNNAMED); - llvm::LLVMSetAlignment(alloca, align.bytes() as c_uint); - alloca - }*/ } fn load(&mut self, _ty: Type<'gcc>, ptr: RValue<'gcc>, _align: Align) -> RValue<'gcc> { - // TODO: use ty. + // TODO(antoyo): use ty. let block = self.llbb(); let function = block.get_function(); // NOTE: instead of returning the dereference here, we have to assign it to a variable in // the current basic block. Otherwise, it could be used in another basic block, causing a // dereference after a drop, for instance. - // TODO: handle align. + // TODO(antoyo): handle align. let deref = ptr.dereference(None).to_rvalue(); let value_type = deref.get_type(); unsafe { RETURN_VALUE_COUNT += 1 }; @@ -939,16 +791,14 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn volatile_load(&mut self, _ty: Type<'gcc>, ptr: RValue<'gcc>) -> RValue<'gcc> { - // TODO: use ty. - //println!("5: volatile load: {:?} to {:?}", ptr, ptr.get_type().make_volatile()); + // TODO(antoyo): use ty. let ptr = self.context.new_cast(None, ptr, ptr.get_type().make_volatile()); - //println!("6"); ptr.dereference(None).to_rvalue() } fn atomic_load(&mut self, _ty: Type<'gcc>, ptr: RValue<'gcc>, order: AtomicOrdering, size: Size) -> RValue<'gcc> { - // TODO: use ty. - // TODO: handle alignment. + // TODO(antoyo): use ty. + // TODO(antoyo): handle alignment. let atomic_load = self.context.get_builtin_function(&format!("__atomic_load_{}", size.bytes())); let ordering = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc()); @@ -958,8 +808,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn load_operand(&mut self, place: PlaceRef<'tcx, RValue<'gcc>>) -> OperandRef<'tcx, RValue<'gcc>> { - //debug!("PlaceRef::load: {:?}", place); - assert_eq!(place.llextra.is_some(), place.layout.is_unsized()); if place.layout.is_zst() { @@ -987,22 +835,11 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { OperandValue::Ref(place.llval, Some(llextra), place.align) } else if place.layout.is_gcc_immediate() { - let const_llval = None; - /*unsafe { - if let Some(global) = llvm::LLVMIsAGlobalVariable(place.llval) { - if llvm::LLVMIsGlobalConstant(global) == llvm::True { - const_llval = llvm::LLVMGetInitializer(global); - } - } - }*/ - let llval = const_llval.unwrap_or_else(|| { - let load = self.load(place.llval.get_type(), place.llval, place.align); - if let abi::Abi::Scalar(ref scalar) = place.layout.abi { - scalar_load_metadata(self, load, scalar); - } - load - }); - OperandValue::Immediate(self.to_immediate(llval, place.layout)) + let load = self.load(place.llval.get_type(), place.llval, place.align); + if let abi::Abi::Scalar(ref scalar) = place.layout.abi { + scalar_load_metadata(self, load, scalar); + } + OperandValue::Immediate(self.to_immediate(load, place.layout)) } else if let abi::Abi::ScalarPair(ref a, ref b) = place.layout.abi { let b_offset = a.value.size(self).align_to(b.value.align(self).abi); @@ -1058,39 +895,11 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn range_metadata(&mut self, _load: RValue<'gcc>, _range: Range) { - // TODO - /*if self.sess().target.target.arch == "amdgpu" { - // amdgpu/LLVM does something weird and thinks a i64 value is - // split into a v2i32, halving the bitwidth LLVM expects, - // tripping an assertion. So, for now, just disable this - // optimization. - return; - } - - unsafe { - let llty = self.cx.val_ty(load); - let v = [ - self.cx.const_uint_big(llty, range.start), - self.cx.const_uint_big(llty, range.end), - ]; - - llvm::LLVMSetMetadata( - load, - llvm::MD_range as c_uint, - llvm::LLVMMDNodeInContext(self.cx.llcx, v.as_ptr(), v.len() as c_uint), - ); - }*/ + // TODO(antoyo) } fn nonnull_metadata(&mut self, _load: RValue<'gcc>) { - // TODO - /*unsafe { - llvm::LLVMSetMetadata( - load, - llvm::MD_nonnull as c_uint, - llvm::LLVMMDNodeInContext(self.cx.llcx, ptr::null(), 0), - ); - }*/ + // TODO(antoyo) } fn store(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>, align: Align) -> RValue<'gcc> { @@ -1098,36 +907,21 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn store_with_flags(&mut self, val: RValue<'gcc>, ptr: RValue<'gcc>, _align: Align, _flags: MemFlags) -> RValue<'gcc> { - //debug!("Store {:?} -> {:?} ({:?})", val, ptr, flags); let ptr = self.check_store(val, ptr); self.llbb().add_assignment(None, ptr.dereference(None), val); - /*let align = - if flags.contains(MemFlags::UNALIGNED) { 1 } else { align.bytes() as c_uint }; - llvm::LLVMSetAlignment(store, align); - if flags.contains(MemFlags::VOLATILE) { - llvm::LLVMSetVolatile(store, llvm::True); - } - if flags.contains(MemFlags::NONTEMPORAL) { - // According to LLVM [1] building a nontemporal store must - // *always* point to a metadata value of the integer 1. - // - // [1]: http://llvm.org/docs/LangRef.html#store-instruction - let one = self.cx.const_i32(1); - let node = llvm::LLVMMDNodeInContext(self.cx.llcx, &one, 1); - llvm::LLVMSetMetadata(store, llvm::MD_nontemporal as c_uint, node); - }*/ - // NOTE: dummy value here since it's never used. FIXME: API should not return a value here? + // TODO(antoyo): handle align and flags. + // NOTE: dummy value here since it's never used. FIXME(antoyo): API should not return a value here? self.cx.context.new_rvalue_zero(self.type_i32()) } fn atomic_store(&mut self, value: RValue<'gcc>, ptr: RValue<'gcc>, order: AtomicOrdering, size: Size) { - // TODO: handle alignment. + // TODO(antoyo): handle alignment. let atomic_store = self.context.get_builtin_function(&format!("__atomic_store_{}", size.bytes())); let ordering = self.context.new_rvalue_from_int(self.i32_type, order.to_gcc()); let volatile_const_void_ptr_type = self.context.new_type::<*mut ()>().make_const().make_volatile(); let ptr = self.context.new_cast(None, ptr, volatile_const_void_ptr_type); - // FIXME: fix libgccjit to allow comparing an integer type with an aligned integer type because + // FIXME(antoyo): fix libgccjit to allow comparing an integer type with an aligned integer type because // the following cast is required to avoid this error: // gcc_jit_context_new_call: mismatching types for argument 2 of function "__atomic_store_4": assignment to param arg1 (type: int) from loadedValue3577 (type: unsigned int __attribute__((aligned(4)))) let int_type = atomic_store.get_param(1).to_rvalue().get_type(); @@ -1145,14 +939,14 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn inbounds_gep(&mut self, _typ: Type<'gcc>, ptr: RValue<'gcc>, indices: &[RValue<'gcc>]) -> RValue<'gcc> { - // FIXME: would be safer if doing the same thing (loop) as gep. - // TODO: specify inbounds somehow. + // FIXME(antoyo): would be safer if doing the same thing (loop) as gep. + // TODO(antoyo): specify inbounds somehow. match indices.len() { 1 => { self.context.new_array_access(None, ptr, indices[0]).get_address(None) }, 2 => { - let array = ptr.dereference(None); // TODO: assert that first index is 0? + let array = ptr.dereference(None); // TODO(antoyo): assert that first index is 0? self.context.new_array_access(None, array, indices[1]).get_address(None) }, _ => unimplemented!(), @@ -1160,7 +954,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn struct_gep(&mut self, value_type: Type<'gcc>, ptr: RValue<'gcc>, idx: u64) -> RValue<'gcc> { - // FIXME: it would be better if the API only called this on struct, not on arrays. + // FIXME(antoyo): it would be better if the API only called this on struct, not on arrays. assert_eq!(idx as usize as u64, idx); let value = ptr.dereference(None).to_rvalue(); @@ -1186,31 +980,21 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { /* Casts */ fn trunc(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { - // TODO: check that it indeed truncate the value. - //println!("trunc: {:?} -> {:?}", value, dest_ty); + // TODO(antoyo): check that it indeed truncate the value. self.context.new_cast(None, value, dest_ty) } fn sext(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { - // TODO: check that it indeed sign extend the value. - //println!("Sext {:?} to {:?}", value, dest_ty); - //if let Some(vector_type) = value.get_type().is_vector() { + // TODO(antoyo): check that it indeed sign extend the value. if dest_ty.is_vector().is_some() { - // TODO: nothing to do as it is only for LLVM? + // TODO(antoyo): nothing to do as it is only for LLVM? return value; - /*let dest_type = self.context.new_vector_type(dest_ty, vector_type.get_num_units() as u64); - println!("Casting {:?} to {:?}", value, dest_type); - return self.context.new_cast(None, value, dest_type);*/ } self.context.new_cast(None, value, dest_ty) } fn fptoui(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { - //println!("7: fptoui: {:?} to {:?}", value, dest_ty); - let ret = self.context.new_cast(None, value, dest_ty); - //println!("8"); - ret - //unsafe { llvm::LLVMBuildFPToUI(self.llbuilder, val, dest_ty, UNNAMED) } + self.context.new_cast(None, value, dest_ty) } fn fptosi(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { @@ -1218,21 +1002,15 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn uitofp(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { - //println!("1: uitofp: {:?} -> {:?}", value, dest_ty); - let ret = self.context.new_cast(None, value, dest_ty); - //println!("2"); - ret + self.context.new_cast(None, value, dest_ty) } fn sitofp(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { - //println!("3: sitofp: {:?} -> {:?}", value, dest_ty); - let ret = self.context.new_cast(None, value, dest_ty); - //println!("4"); - ret + self.context.new_cast(None, value, dest_ty) } fn fptrunc(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { - // TODO: make sure it trancates. + // TODO(antoyo): make sure it truncates. self.context.new_cast(None, value, dest_ty) } @@ -1254,12 +1032,10 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { fn intcast(&mut self, value: RValue<'gcc>, dest_typ: Type<'gcc>, _is_signed: bool) -> RValue<'gcc> { // NOTE: is_signed is for value, not dest_typ. - //println!("intcast: {:?} ({:?}) -> {:?}", value, value.get_type(), dest_typ); self.cx.context.new_cast(None, value, dest_typ) } fn pointercast(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { - //println!("pointercast: {:?} ({:?}) -> {:?}", value, value.get_type(), dest_ty); let val_type = value.get_type(); match (type_is_pointer(val_type), type_is_pointer(dest_ty)) { (false, true) => { @@ -1269,7 +1045,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { }, (false, false) => { // When they are not pointers, we want a transmute (or reinterpret_cast). - //self.cx.context.new_cast(None, value, dest_ty) self.bitcast(value, dest_ty) }, (true, true) => self.cx.context.new_cast(None, value, dest_ty), @@ -1307,7 +1082,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { let src = self.pointercast(src, self.type_ptr_to(self.type_void())); let memcpy = self.context.get_builtin_function("memcpy"); let block = self.block.expect("block"); - // TODO: handle aligns and is_volatile. + // TODO(antoyo): handle aligns and is_volatile. block.add_eval(None, self.context.new_call(None, memcpy, &[dst, src, size])); } @@ -1326,7 +1101,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { let memmove = self.context.get_builtin_function("memmove"); let block = self.block.expect("block"); - // TODO: handle is_volatile. + // TODO(antoyo): handle is_volatile. block.add_eval(None, self.context.new_call(None, memmove, &[dst, src, size])); } @@ -1335,8 +1110,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { let ptr = self.pointercast(ptr, self.type_i8p()); let memset = self.context.get_builtin_function("memset"); let block = self.block.expect("block"); - // TODO: handle aligns and is_volatile. - //println!("memset: {:?} -> {:?}", fill_byte, self.i32_type); + // TODO(antoyo): handle align and is_volatile. let fill_byte = self.context.new_cast(None, fill_byte, self.i32_type); let size = self.intcast(size, self.type_size_t(), false); block.add_eval(None, self.context.new_call(None, memset, &[ptr, fill_byte, size])); @@ -1370,27 +1144,18 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { #[allow(dead_code)] fn va_arg(&mut self, _list: RValue<'gcc>, _ty: Type<'gcc>) -> RValue<'gcc> { unimplemented!(); - //unsafe { llvm::LLVMBuildVAArg(self.llbuilder, list, ty, UNNAMED) } } fn extract_element(&mut self, _vec: RValue<'gcc>, _idx: RValue<'gcc>) -> RValue<'gcc> { unimplemented!(); - //unsafe { llvm::LLVMBuildExtractElement(self.llbuilder, vec, idx, UNNAMED) } } fn vector_splat(&mut self, _num_elts: usize, _elt: RValue<'gcc>) -> RValue<'gcc> { unimplemented!(); - /*unsafe { - let elt_ty = self.cx.val_ty(elt); - let undef = llvm::LLVMGetUndef(self.type_vector(elt_ty, num_elts as u64)); - let vec = self.insert_element(undef, elt, self.cx.const_i32(0)); - let vec_i32_ty = self.type_vector(self.type_i32(), num_elts as u64); - self.shuffle_vector(vec, undef, self.const_null(vec_i32_ty)) - }*/ } fn extract_value(&mut self, aggregate_value: RValue<'gcc>, idx: u64) -> RValue<'gcc> { - // FIXME: it would be better if the API only called this on struct, not on arrays. + // FIXME(antoyo): it would be better if the API only called this on struct, not on arrays. assert_eq!(idx as usize as u64, idx); let value_type = aggregate_value.get_type(); @@ -1418,12 +1183,10 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { else { panic!("Unexpected type {:?}", value_type); } - /*assert_eq!(idx as c_uint as u64, idx); - unsafe { llvm::LLVMBuildExtractValue(self.llbuilder, agg_val, idx as c_uint, UNNAMED) }*/ } fn insert_value(&mut self, aggregate_value: RValue<'gcc>, value: RValue<'gcc>, idx: u64) -> RValue<'gcc> { - // FIXME: it would be better if the API only called this on struct, not on arrays. + // FIXME(antoyo): it would be better if the API only called this on struct, not on arrays. assert_eq!(idx as usize as u64, idx); let value_type = aggregate_value.get_type(); @@ -1459,88 +1222,41 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { let struct_type = self.context.new_struct_type(None, "landing_pad", &[field1, field2]); self.current_func().new_local(None, struct_type.as_type(), "landing_pad") .to_rvalue() - // TODO - /*unsafe { - llvm::LLVMBuildLandingPad(self.llbuilder, ty, pers_fn, num_clauses as c_uint, UNNAMED) - }*/ + // TODO(antoyo): Properly implement unwinding. + // the above is just to make the compilation work as it seems + // rustc_codegen_ssa now calls the unwinding builder methods even on panic=abort. } fn set_cleanup(&mut self, _landing_pad: RValue<'gcc>) { - // TODO - /*unsafe { - llvm::LLVMSetCleanup(landing_pad, llvm::True); - }*/ + // TODO(antoyo) } fn resume(&mut self, _exn: RValue<'gcc>) -> RValue<'gcc> { unimplemented!(); - //unsafe { llvm::LLVMBuildResume(self.llbuilder, exn) } } fn cleanup_pad(&mut self, _parent: Option>, _args: &[RValue<'gcc>]) -> Funclet { unimplemented!(); - /*let name = const_cstr!("cleanuppad"); - let ret = unsafe { - llvm::LLVMRustBuildCleanupPad( - self.llbuilder, - parent, - args.len() as c_uint, - args.as_ptr(), - name.as_ptr(), - ) - }; - Funclet::new(ret.expect("LLVM does not have support for cleanuppad"))*/ } fn cleanup_ret(&mut self, _funclet: &Funclet, _unwind: Option>) -> RValue<'gcc> { unimplemented!(); - /*let ret = - unsafe { llvm::LLVMRustBuildCleanupRet(self.llbuilder, funclet.cleanuppad(), unwind) }; - ret.expect("LLVM does not have support for cleanupret")*/ } fn catch_pad(&mut self, _parent: RValue<'gcc>, _args: &[RValue<'gcc>]) -> Funclet { unimplemented!(); - /*let name = const_cstr!("catchpad"); - let ret = unsafe { - llvm::LLVMRustBuildCatchPad( - self.llbuilder, - parent, - args.len() as c_uint, - args.as_ptr(), - name.as_ptr(), - ) - }; - Funclet::new(ret.expect("LLVM does not have support for catchpad"))*/ } fn catch_switch(&mut self, _parent: Option>, _unwind: Option>, _num_handlers: usize) -> RValue<'gcc> { unimplemented!(); - /*let name = const_cstr!("catchswitch"); - let ret = unsafe { - llvm::LLVMRustBuildCatchSwitch( - self.llbuilder, - parent, - unwind, - num_handlers as c_uint, - name.as_ptr(), - ) - }; - ret.expect("LLVM does not have support for catchswitch")*/ } fn add_handler(&mut self, _catch_switch: RValue<'gcc>, _handler: Block<'gcc>) { unimplemented!(); - /*unsafe { - llvm::LLVMRustAddHandler(catch_switch, handler); - }*/ } fn set_personality_fn(&mut self, _personality: RValue<'gcc>) { - // TODO - /*unsafe { - llvm::LLVMSetPersonalityFn(self.llfn(), personality); - }*/ + // TODO(antoyo) } // Atomic Operations @@ -1551,7 +1267,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { let pair_type = self.cx.type_struct(&[src.get_type(), self.bool_type], false); let result = self.current_func().new_local(None, pair_type, "atomic_cmpxchg_result"); - let align = Align::from_bits(64).expect("align"); // TODO: use good align. + let align = Align::from_bits(64).expect("align"); // TODO(antoyo): use good align. let value_type = result.to_rvalue().get_type(); if let Some(struct_type) = value_type.is_struct() { @@ -1560,7 +1276,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { // expected so that we store expected after the call. self.store(expected.to_rvalue(), result.access_field(None, struct_type.get_field(0)).get_address(None), align); } - // TODO: handle when value is not a struct. + // TODO(antoyo): handle when value is not a struct. result.to_rvalue() } @@ -1589,7 +1305,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { let void_ptr_type = self.context.new_type::<*mut ()>(); let volatile_void_ptr_type = void_ptr_type.make_volatile(); let dst = self.context.new_cast(None, dst, volatile_void_ptr_type); - // NOTE: not sure why, but we have the wrong type here. + // FIXME(antoyo): not sure why, but we have the wrong type here. let new_src_type = atomic_function.get_param(1).to_rvalue().get_type(); let src = self.context.new_cast(None, src, new_src_type); let res = self.context.new_call(None, atomic_function, &[dst, src, order]); @@ -1610,28 +1326,19 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { fn set_invariant_load(&mut self, load: RValue<'gcc>) { // NOTE: Hack to consider vtable function pointer as non-global-variable function pointer. self.normal_function_addresses.borrow_mut().insert(load); - // TODO - /*unsafe { - llvm::LLVMSetMetadata( - load, - llvm::MD_invariant_load as c_uint, - llvm::LLVMMDNodeInContext(self.cx.llcx, ptr::null(), 0), - ); - }*/ + // TODO(antoyo) } fn lifetime_start(&mut self, _ptr: RValue<'gcc>, _size: Size) { - // TODO - //self.call_lifetime_intrinsic("llvm.lifetime.start.p0i8", ptr, size); + // TODO(antoyo) } fn lifetime_end(&mut self, _ptr: RValue<'gcc>, _size: Size) { - // TODO - //self.call_lifetime_intrinsic("llvm.lifetime.end.p0i8", ptr, size); + // TODO(antoyo) } fn call(&mut self, _typ: Type<'gcc>, func: RValue<'gcc>, args: &[RValue<'gcc>], funclet: Option<&Funclet>) -> RValue<'gcc> { - // FIXME: remove when having a proper API. + // FIXME(antoyo): remove when having a proper API. let gcc_func = unsafe { std::mem::transmute(func) }; if self.functions.borrow().values().find(|value| **value == gcc_func).is_some() { self.function_call(func, args, funclet) @@ -1643,13 +1350,12 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { } fn zext(&mut self, value: RValue<'gcc>, dest_typ: Type<'gcc>) -> RValue<'gcc> { - // FIXME: this does not zero-extend. + // FIXME(antoyo): this does not zero-extend. if value.get_type().is_bool() && dest_typ.is_i8(&self.cx) { - // FIXME: hack because base::from_immediate converts i1 to i8. + // FIXME(antoyo): hack because base::from_immediate converts i1 to i8. // Fix the code in codegen_ssa::base::from_immediate. return value; } - //println!("zext: {:?} -> {:?}", value, dest_typ); self.context.new_cast(None, value, dest_typ) } @@ -1659,7 +1365,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { fn do_not_inline(&mut self, _llret: RValue<'gcc>) { unimplemented!(); - //llvm::Attribute::NoInline.apply_callsite(llvm::AttributePlace::Function, llret); } fn set_span(&mut self, _span: Span) {} @@ -1690,24 +1395,6 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { fn instrprof_increment(&mut self, _fn_name: RValue<'gcc>, _hash: RValue<'gcc>, _num_counters: RValue<'gcc>, _index: RValue<'gcc>) { unimplemented!(); - /*debug!( - "instrprof_increment() with args ({:?}, {:?}, {:?}, {:?})", - fn_name, hash, num_counters, index - ); - - let llfn = unsafe { llvm::LLVMRustGetInstrProfIncrementIntrinsic(self.cx().llmod) }; - let args = &[fn_name, hash, num_counters, index]; - let args = self.check_call("call", llfn, args); - - unsafe { - let _ = llvm::LLVMRustBuildCall( - self.llbuilder, - llfn, - args.as_ptr() as *const &llvm::Value, - args.len() as c_uint, - None, - ); - }*/ } } @@ -1766,7 +1453,7 @@ impl ToGccComp for IntPredicate { impl ToGccComp for RealPredicate { fn to_gcc_comparison(&self) -> ComparisonOp { - // TODO: check that ordered vs non-ordered is respected. + // TODO(antoyo): check that ordered vs non-ordered is respected. match *self { RealPredicate::RealPredicateFalse => unreachable!(), RealPredicate::RealOEQ => ComparisonOp::Equals, @@ -1809,9 +1496,9 @@ impl ToGccOrdering for AtomicOrdering { let ordering = match self { - AtomicOrdering::NotAtomic => __ATOMIC_RELAXED, // TODO: check if that's the same. + AtomicOrdering::NotAtomic => __ATOMIC_RELAXED, // TODO(antoyo): check if that's the same. AtomicOrdering::Unordered => __ATOMIC_RELAXED, - AtomicOrdering::Monotonic => __ATOMIC_RELAXED, // TODO: check if that's the same. + AtomicOrdering::Monotonic => __ATOMIC_RELAXED, // TODO(antoyo): check if that's the same. AtomicOrdering::Acquire => __ATOMIC_ACQUIRE, AtomicOrdering::Release => __ATOMIC_RELEASE, AtomicOrdering::AcquireRelease => __ATOMIC_ACQ_REL, diff --git a/src/callee.rs b/src/callee.rs index d0ae96adcedaf..4ea084ef729d3 100644 --- a/src/callee.rs +++ b/src/callee.rs @@ -17,8 +17,6 @@ use crate::context::CodegenCx; pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>) -> RValue<'gcc> { let tcx = cx.tcx(); - //debug!("get_fn(instance={:?})", instance); - assert!(!instance.substs.needs_infer()); assert!(!instance.substs.has_escaping_bound_vars()); assert!(!instance.substs.has_param_types_or_consts()); @@ -28,11 +26,9 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>) } let sym = tcx.symbol_name(instance).name; - //debug!("get_fn({:?}: {:?}) => {}", instance, instance.monomorphic_ty(cx.tcx()), sym); let fn_abi = FnAbi::of_instance(cx, instance, &[]); - // TODO let func = if let Some(func) = cx.get_declared_value(&sym) { // Create a fn pointer with the new signature. @@ -62,34 +58,18 @@ pub fn get_fn<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, instance: Instance<'tcx>) // reference. It also occurs when testing libcore and in some // other weird situations. Annoying. if cx.val_ty(func) != ptrty { - //debug!("get_fn: casting {:?} to {:?}", func, ptrty); - // TODO - //cx.const_ptrcast(func, ptrty) + // TODO(antoyo): cast the pointer. func } else { - //debug!("get_fn: not casting pointer!"); func } } else { cx.linkage.set(FunctionType::Extern); let func = cx.declare_fn(&sym, &fn_abi); - //cx.linkage.set(FunctionType::Internal); - //debug!("get_fn: not casting pointer!"); - - // TODO - //attributes::from_fn_attrs(cx, func, instance); - - //let instance_def_id = instance.def_id(); - - // TODO - /*if cx.use_dll_storage_attrs && tcx.is_dllimport_foreign_item(instance_def_id) { - unsafe { - llvm::LLVMSetDLLStorageClass(func, llvm::DLLStorageClass::DllImport); - } - }*/ + // TODO(antoyo): set linkage and attributes. func }; diff --git a/src/common.rs b/src/common.rs index 3178ada9ec3e2..752ba99af9ce3 100644 --- a/src/common.rs +++ b/src/common.rs @@ -27,7 +27,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } fn const_cstr(&self, symbol: Symbol, _null_terminated: bool) -> RValue<'gcc> { - // TODO: handle null_terminated. + // TODO(antoyo): handle null_terminated. if let Some(&value) = self.const_cstr_cache.borrow().get(&symbol) { return value.to_rvalue(); } @@ -39,7 +39,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } fn global_string(&self, string: &str) -> RValue<'gcc> { - // TODO: handle non-null-terminated strings. + // TODO(antoyo): handle non-null-terminated strings. let string = self.context.new_string_literal(&*string); let sym = self.generate_local_symbol_name("str"); // NOTE: TLS is always off for a string litteral. @@ -48,7 +48,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { .unwrap_or_else(|| bug!("symbol `{}` is already defined", sym)); self.global_init_block.add_assignment(None, global.dereference(None), string); global.to_rvalue() - //llvm::LLVMRustSetLinkage(global, llvm::Linkage::InternalLinkage); + // TODO(antoyo): set linkage. } pub fn inttoptr(&self, block: Block<'gcc>, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { @@ -62,7 +62,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } pub fn ptrtoint(&self, block: Block<'gcc>, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> { - // TODO: when libgccjit allow casting from pointer to int, remove this. + // TODO(antoyo): when libgccjit allow casting from pointer to int, remove this. let func = block.get_function(); let local = func.new_local(None, value.get_type(), "ptrLocal"); block.add_assignment(None, local, value); @@ -71,10 +71,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let ptr = self.context.new_cast(None, ptr_address, dest_ty.make_pointer()); ptr.dereference(None).to_rvalue() } - - /*pub fn const_vector(&self, elements: &[RValue<'gcc>]) -> RValue<'gcc> { - self.context.new_rvalue_from_vector(None, elements[0].get_type(), elements) - }*/ } pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> RValue<'gcc> { @@ -125,13 +121,13 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn const_uint_big(&self, typ: Type<'gcc>, num: u128) -> RValue<'gcc> { let num64: Result = num.try_into(); if let Ok(num) = num64 { - // FIXME: workaround for a bug where libgccjit is expecting a constant. + // FIXME(antoyo): workaround for a bug where libgccjit is expecting a constant. // The operations >> 64 and | low are making the normal case a non-constant. return self.context.new_rvalue_from_long(typ, num as i64); } if num >> 64 != 0 { - // FIXME: use a new function new_rvalue_from_unsigned_long()? + // FIXME(antoyo): use a new function new_rvalue_from_unsigned_long()? let low = self.context.new_rvalue_from_long(self.u64_type, num as u64 as i64); let high = self.context.new_rvalue_from_long(typ, (num >> 64) as u64 as i64); @@ -175,12 +171,10 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn const_u8(&self, _i: u8) -> RValue<'gcc> { unimplemented!(); - //self.const_uint(self.type_i8(), i as u64) } fn const_real(&self, _t: Type<'gcc>, _val: f64) -> RValue<'gcc> { unimplemented!(); - //unsafe { llvm::LLVMConstReal(t, val) } } fn const_str(&self, s: Symbol) -> (RValue<'gcc>, RValue<'gcc>) { @@ -195,7 +189,7 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { let fields: Vec<_> = values.iter() .map(|value| value.get_type()) .collect(); - // TODO: cache the type? It's anonymous, so probably not. + // TODO(antoyo): cache the type? It's anonymous, so probably not. let name = fields.iter().map(|typ| format!("{:?}", typ)).collect::>().join("_"); let typ = self.type_struct(&fields, packed); let structure = self.global_init_func.new_local(None, typ, &name); @@ -209,19 +203,13 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } fn const_to_opt_uint(&self, _v: RValue<'gcc>) -> Option { - // TODO + // TODO(antoyo) None - //try_as_const_integral(v).map(|v| unsafe { llvm::LLVMConstIntGetZExtValue(v) }) } fn const_to_opt_u128(&self, _v: RValue<'gcc>, _sign_ext: bool) -> Option { - // TODO + // TODO(antoyo) None - /*try_as_const_integral(v).and_then(|v| unsafe { - let (mut lo, mut hi) = (0u64, 0u64); - let success = llvm::LLVMRustConstInt128Get(v, sign_ext, &mut hi, &mut lo); - success.then_some(hi_lo_to_u128(lo, hi)) - })*/ } fn scalar_to_backend(&self, cv: Scalar, layout: &abi::Scalar, ty: Type<'gcc>) -> RValue<'gcc> { @@ -234,7 +222,7 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { Scalar::Int(int) => { let data = int.assert_bits(layout.value.size(self)); - // FIXME: there's some issues with using the u128 code that follows, so hard-code + // FIXME(antoyo): there's some issues with using the u128 code that follows, so hard-code // the paths for floating-point values. if ty == self.float_type { return self.context.new_rvalue_from_double(ty, f32::from_bits(data as u32) as f64); @@ -262,8 +250,7 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> { _ => self.static_addr_of(init, alloc.align, None), }; if !self.sess().fewer_names() { - // TODO - //llvm::set_value_name(value, format!("{:?}", ptr.alloc_id).as_bytes()); + // TODO(antoyo): set value name. } value }, diff --git a/src/consts.rs b/src/consts.rs index 2cdd7fcab8b46..9b7959503ab1b 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -32,22 +32,11 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> { fn static_addr_of(&self, cv: RValue<'gcc>, align: Align, kind: Option<&str>) -> RValue<'gcc> { if let Some(global_value) = self.const_globals.borrow().get(&cv) { - // TODO - /*unsafe { - // Upgrade the alignment in cases where the same constant is used with different - // alignment requirements - let llalign = align.bytes() as u32; - if llalign > llvm::LLVMGetAlignment(gv) { - llvm::LLVMSetAlignment(gv, llalign); - } - }*/ + // TODO(antoyo): upgrade alignment. return *global_value; } let global_value = self.static_addr_of_mut(cv, align, kind); - // TODO - /*unsafe { - llvm::LLVMSetGlobalConstant(global_value, True); - }*/ + // TODO(antoyo): set global constant. self.const_globals.borrow_mut().insert(cv, global_value); global_value } @@ -73,9 +62,7 @@ impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> { let val_llty = self.val_ty(value); let value = if val_llty == self.type_i1() { - //val_llty = self.type_i8(); unimplemented!(); - //llvm::LLVMConstZExt(value, val_llty) } else { value @@ -92,26 +79,17 @@ impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> { else { // If we created the global with the wrong type, // correct the type. - /*let name = llvm::get_value_name(global).to_vec(); - llvm::set_value_name(global, b""); - - let linkage = llvm::LLVMRustGetLinkage(global); - let visibility = llvm::LLVMRustGetVisibility(global);*/ + // TODO(antoyo): set value name, linkage and visibility. let new_global = self.get_or_insert_global(&name, val_llty, is_tls, attrs.link_section); - /*llvm::LLVMRustSetLinkage(new_global, linkage); - llvm::LLVMRustSetVisibility(new_global, visibility);*/ - // To avoid breaking any invariants, we leave around the old // global for the moment; we'll replace all references to it // with the new global later. (See base::codegen_backend.) //self.statics_to_rauw.borrow_mut().push((global, new_global)); new_global }; - // TODO - //set_global_alignment(&self, global, self.align_of(ty)); - //llvm::LLVMSetInitializer(global, value); + // TODO(antoyo): set alignment and initializer. let value = self.rvalue_as_lvalue(value); let value = value.get_address(None); let dest_typ = global.get_type(); @@ -119,14 +97,14 @@ impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> { // NOTE: do not init the variables related to argc/argv because it seems we cannot // overwrite those variables. - // FIXME: correctly support global variable initialization. + // FIXME(antoyo): correctly support global variable initialization. let skip_init = [ ARGV_INIT_ARRAY, ARGC, ARGV, ]; if !skip_init.iter().any(|symbol_name| name.starts_with(symbol_name)) { - // TODO: switch to set_initializer when libgccjit supports that. + // TODO(antoyo): switch to set_initializer when libgccjit supports that. let memcpy = self.context.get_builtin_function("memcpy"); let dst = self.context.new_cast(None, global, self.type_i8p()); let src = self.context.new_cast(None, value, self.type_ptr_to(self.type_void())); @@ -138,13 +116,10 @@ impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> { // mutability are placed into read-only memory. if !is_mutable { if self.type_is_freeze(ty) { - // TODO - //llvm::LLVMSetGlobalConstant(global, llvm::True); + // TODO(antoyo): set global constant. } } - //debuginfo::create_global_var_metadata(&self, def_id, global); - if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) { // Do not allow LLVM to change the alignment of a TLS on macOS. // @@ -184,19 +159,7 @@ impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> { // happens to be zero. Instead, we should only check the value of defined bytes // and set all undefined bytes to zero if this allocation is headed for the // BSS. - /*let all_bytes_are_zero = alloc.relocations().is_empty() - && alloc - .inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()) - .iter() - .all(|&byte| byte == 0); - - let sect_name = if all_bytes_are_zero { - CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_bss\0") - } else { - CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_data\0") - };*/ unimplemented!(); - //llvm::LLVMSetSection(global, sect_name.as_ptr()); } } @@ -205,34 +168,9 @@ impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> { if self.tcx.sess.opts.target_triple.triple().starts_with("wasm32") { if let Some(_section) = attrs.link_section { unimplemented!(); - /*let section = llvm::LLVMMDStringInContext( - self.llcx, - section.as_str().as_ptr().cast(), - section.as_str().len() as c_uint, - ); - assert!(alloc.relocations().is_empty()); - - // The `inspect` method is okay here because we checked relocations, and - // because we are doing this access to inspect the final interpreter state (not - // as part of the interpreter execution). - let bytes = - alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()); - let alloc = llvm::LLVMMDStringInContext( - self.llcx, - bytes.as_ptr().cast(), - bytes.len() as c_uint, - ); - let data = [section, alloc]; - let meta = llvm::LLVMMDNodeInContext(self.llcx, data.as_ptr(), 2); - llvm::LLVMAddNamedMetadataOperand( - self.llmod, - "wasm.custom_sections\0".as_ptr().cast(), - meta, - );*/ } } else { - // TODO - //base::set_link_section(global, &attrs); + // TODO(antoyo): set link section. } if attrs.flags.contains(CodegenFnAttrFlags::USED) { @@ -242,9 +180,7 @@ impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> { /// Add a global value to a list to be stored in the `llvm.used` variable, an array of i8*. fn add_used_global(&self, _global: RValue<'gcc>) { - // TODO - //let cast = self.context.new_cast(None, global, self.type_i8p()); - //self.used_statics.borrow_mut().push(cast); + // TODO(antoyo) } } @@ -254,13 +190,13 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { match kind { Some(kind) if !self.tcx.sess.fewer_names() => { let name = self.generate_local_symbol_name(kind); - // TODO: check if it's okay that TLS is off here. - // TODO: check if it's okay that link_section is None here. - // TODO: set alignment here as well. + // TODO(antoyo): check if it's okay that TLS is off here. + // TODO(antoyo): check if it's okay that link_section is None here. + // TODO(antoyo): set alignment here as well. let gv = self.define_global(&name[..], self.val_ty(cv), false, None).unwrap_or_else(|| { bug!("symbol `{}` is already defined", name); }); - //llvm::LLVMRustSetLinkage(gv, llvm::Linkage::PrivateLinkage); + // TODO(antoyo): set linkage. (name, gv) } _ => { @@ -271,13 +207,13 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { (name, global) }, }; - // FIXME: I think the name coming from generate_local_symbol_name() above cannot be used + // FIXME(antoyo): I think the name coming from generate_local_symbol_name() above cannot be used // globally. // NOTE: global seems to only be global in a module. So save the name instead of the value // to import it later. self.global_names.borrow_mut().insert(cv, name); self.global_init_block.add_assignment(None, gv.dereference(None), cv); - //llvm::SetUnnamedAddress(gv, llvm::UnnamedAddr::Global); + // TODO(antoyo): set unnamed address. gv } @@ -285,19 +221,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let instance = Instance::mono(self.tcx, def_id); let fn_attrs = self.tcx.codegen_fn_attrs(def_id); if let Some(&global) = self.instances.borrow().get(&instance) { - /*let attrs = self.tcx.codegen_fn_attrs(def_id); - let name = &*self.tcx.symbol_name(instance).name; - let name = - if let Some(linkage) = attrs.linkage { - // This is to match what happens in check_and_apply_linkage. - Cow::from(format!("_rust_extern_with_linkage_{}", name)) - } - else { - Cow::from(name) - }; - let global = self.context.new_global(None, GlobalKind::Imported, global.get_type(), &name) - .get_address(None); - self.global_names.borrow_mut().insert(global, name.to_string());*/ return global; } @@ -313,8 +236,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all()); let sym = self.tcx.symbol_name(instance).name; - //debug!("get_static: sym={} instance={:?}", sym, instance); - let global = if let Some(def_id) = def_id.as_local() { let id = self.tcx.hir().local_def_id_to_hir_id(def_id); @@ -332,9 +253,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let global = self.declare_global(&sym, llty, is_tls, fn_attrs.link_section); if !self.tcx.is_reachable_non_generic(def_id) { - /*unsafe { - llvm::LLVMRustSetVisibility(global, llvm::Visibility::Hidden); - }*/ + // TODO(antoyo): set visibility. } global @@ -352,8 +271,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { item => bug!("get_static: expected static, found {:?}", item), }; - //debug!("get_static: sym={} attrs={:?}", sym, attrs); - global } else { @@ -364,11 +281,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let span = self.tcx.def_span(def_id); let global = check_and_apply_linkage(&self, &attrs, ty, sym, span); - let needs_dll_storage_attr = false; /*self.use_dll_storage_attrs && !self.tcx.is_foreign_item(def_id) && - // ThinLTO can't handle this workaround in all cases, so we don't - // emit the attrs. Instead we make them unnecessary by disallowing - // dynamic linking when linker plugin based LTO is enabled. - !self.tcx.sess.opts.cg.linker_plugin_lto.enabled();*/ + let needs_dll_storage_attr = false; // TODO(antoyo) // If this assertion triggers, there's something wrong with commandline // argument validation. @@ -391,20 +304,12 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { // is_codegened_item query. if !self.tcx.is_codegened_item(def_id) { unimplemented!(); - /*unsafe { - llvm::LLVMSetDLLStorageClass(global, llvm::DLLStorageClass::DllImport); - }*/ } } global }; - /*if self.use_dll_storage_attrs && self.tcx.is_dllimport_foreign_item(def_id) { - // For foreign (native) libs we know the exact storage type to use. - unsafe { - llvm::LLVMSetDLLStorageClass(global, llvm::DLLStorageClass::DllImport); - } - }*/ + // TODO(antoyo): set dll storage class. self.instances.borrow_mut().insert(instance, global); global @@ -474,8 +379,6 @@ fn check_and_apply_linkage<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, attrs: &Codeg let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL); let llty = cx.layout_of(ty).gcc_type(cx, true); if let Some(linkage) = attrs.linkage { - //debug!("get_static: sym={} linkage={:?}", sym, linkage); - // If this is a static with a linkage specified, then we need to handle // it a little specially. The typesystem prevents things like &T and // extern "C" fn() from being non-null, so we can't just declare a @@ -506,10 +409,10 @@ fn check_and_apply_linkage<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, attrs: &Codeg cx.define_global(&real_name, llty, is_tls, attrs.link_section).unwrap_or_else(|| { cx.sess().span_fatal(span, &format!("symbol `{}` is already defined", &sym)) }); - //llvm::LLVMRustSetLinkage(global2, llvm::Linkage::InternalLinkage); + // TODO(antoyo): set linkage. let lvalue = global2.dereference(None); cx.global_init_block.add_assignment(None, lvalue, global1); - //llvm::LLVMSetInitializer(global2, global1); + // TODO(antoyo): use global_set_initializer() when it will work. global2 } else { diff --git a/src/context.rs b/src/context.rs index 19243b0cbce5d..7ab1ca0d771c1 100644 --- a/src/context.rs +++ b/src/context.rs @@ -41,7 +41,7 @@ pub struct CodegenCx<'gcc, 'tcx> { pub codegen_unit: &'tcx CodegenUnit<'tcx>, pub context: &'gcc Context<'gcc>, - // TODO: First set it to a dummy block to avoid using Option? + // TODO(antoyo): First set it to a dummy block to avoid using Option? pub current_block: RefCell>>, pub current_func: RefCell>>, pub normal_function_addresses: RefCell>>, @@ -104,7 +104,7 @@ pub struct CodegenCx<'gcc, 'tcx> { /// Cache of globals. pub globals: RefCell>>, - // TODO: remove global_names. + // TODO(antoyo): remove global_names. pub global_names: RefCell, String>>, /// A counter that is used for generating local symbol names @@ -119,13 +119,13 @@ pub struct CodegenCx<'gcc, 'tcx> { /// `const_undef()` returns struct as pointer so that they can later be assigned a value. /// As such, this set remembers which of these pointers were returned by this function so that /// they can be derefered later. - /// FIXME: fix the rustc API to avoid having this hack. + /// FIXME(antoyo): fix the rustc API to avoid having this hack. pub structs_as_pointer: RefCell>>, /// Store the pointer of different types for safety. /// When casting the values back to their original types, check that they are indeed that type /// with these sets. - /// FIXME: remove when the API supports more types. + /// FIXME(antoyo): remove when the API supports more types. #[cfg(debug_assertions)] lvalues: RefCell>>, } @@ -133,22 +133,20 @@ pub struct CodegenCx<'gcc, 'tcx> { impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { pub fn new(context: &'gcc Context<'gcc>, codegen_unit: &'tcx CodegenUnit<'tcx>, tcx: TyCtxt<'tcx>) -> Self { let check_overflow = tcx.sess.overflow_checks(); - // TODO: fix this mess. libgccjit seems to return random type when using new_int_type(). - //let isize_type = context.new_int_type((tcx.data_layout.pointer_size.bits() / 8) as i32, true); + // TODO(antoyo): fix this mess. libgccjit seems to return random type when using new_int_type(). let isize_type = context.new_c_type(CType::LongLong); - //let usize_type = context.new_int_type((tcx.data_layout.pointer_size.bits() / 8) as i32, false); let usize_type = context.new_c_type(CType::ULongLong); let bool_type = context.new_type::(); let i8_type = context.new_type::(); let i16_type = context.new_type::(); let i32_type = context.new_type::(); let i64_type = context.new_c_type(CType::LongLong); - let i128_type = context.new_c_type(CType::Int128t).get_aligned(8); // TODO: should this be hard-coded? + let i128_type = context.new_c_type(CType::Int128t).get_aligned(8); // TODO(antoyo): should the alignment be hard-coded? let u8_type = context.new_type::(); let u16_type = context.new_type::(); let u32_type = context.new_type::(); let u64_type = context.new_c_type(CType::ULongLong); - let u128_type = context.new_c_type(CType::UInt128t).get_aligned(8); // TODO: should this be hard-coded? + let u128_type = context.new_c_type(CType::UInt128t).get_aligned(8); // TODO(antoyo): should the alignment be hard-coded? let tls_model = to_gcc_tls_mode(tcx.sess.tls_model()); @@ -261,7 +259,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { pub fn rvalue_as_lvalue(&self, value: RValue<'gcc>) -> LValue<'gcc> { let lvalue: LValue<'gcc> = unsafe { std::mem::transmute(value) }; - //debug_assert!(self.lvalues.borrow().contains(&lvalue), "{:?} is not an lvalue", value); lvalue } @@ -276,11 +273,11 @@ impl<'gcc, 'tcx> BackendTypes for CodegenCx<'gcc, 'tcx> { type BasicBlock = Block<'gcc>; type Type = Type<'gcc>; - type Funclet = (); // TODO + type Funclet = (); // TODO(antoyo) - type DIScope = (); // TODO - type DILocation = (); // TODO - type DIVariable = (); // TODO + type DIScope = (); // TODO(antoyo) + type DILocation = (); // TODO(antoyo) + type DIVariable = (); // TODO(antoyo) } impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> { @@ -295,17 +292,12 @@ impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } fn get_fn_addr(&self, instance: Instance<'tcx>) -> RValue<'gcc> { - //let symbol = self.tcx.symbol_name(instance).name; - let func = get_fn(self, instance); let func = self.rvalue_as_function(func); let ptr = func.get_address(None); - // TODO: don't do this twice: i.e. in declare_fn and here. - //let fn_abi = FnAbi::of_instance(self, instance, &[]); - //let (return_type, params, _) = fn_abi.gcc_type(self); - // FIXME: the rustc API seems to call get_fn_addr() when not needed (e.g. for FFI). - //let pointer_type = ptr.get_type(); + // TODO(antoyo): don't do this twice: i.e. in declare_fn and here. + // FIXME(antoyo): the rustc API seems to call get_fn_addr() when not needed (e.g. for FFI). self.normal_function_addresses.borrow_mut().insert(ptr); @@ -354,12 +346,12 @@ impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> { "rust_eh_personality" }; //let func = self.declare_func(name, self.type_i32(), &[], true); - // FIXME: this hack should not be needed. That will probably be removed when + // FIXME(antoyo): this hack should not be needed. That will probably be removed when // unwinding support is added. self.context.new_rvalue_from_int(self.int_type, 0) } }; - //attributes::apply_target_cpu_attr(self, llfn); + // TODO(antoyo): apply target cpu attributes. self.eh_personality.set(Some(llfn)); llfn } @@ -378,32 +370,18 @@ impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn used_statics(&self) -> &RefCell>> { unimplemented!(); - //&self.used_statics } fn set_frame_pointer_type(&self, _llfn: RValue<'gcc>) { - // TODO - //attributes::set_frame_pointer_type(self, llfn) + // TODO(antoyo) } fn apply_target_cpu_attr(&self, _llfn: RValue<'gcc>) { - // TODO - //attributes::apply_target_cpu_attr(self, llfn) + // TODO(antoyo) } fn create_used_variable(&self) { unimplemented!(); - /*let name = const_cstr!("llvm.used"); - let section = const_cstr!("llvm.metadata"); - let array = - self.const_array(&self.type_ptr_to(self.type_i8()), &*self.used_statics.borrow()); - - unsafe { - let g = llvm::LLVMAddGlobal(self.llmod, self.val_ty(array), name.as_ptr()); - llvm::LLVMSetInitializer(g, array); - llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage); - llvm::LLVMSetSection(g, section.as_ptr()); - }*/ } fn declare_c_main(&self, fn_type: Self::Type) -> Option { diff --git a/src/coverageinfo.rs b/src/coverageinfo.rs index f966f6e75336f..872fc2472e223 100644 --- a/src/coverageinfo.rs +++ b/src/coverageinfo.rs @@ -20,99 +20,31 @@ impl<'a, 'gcc, 'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx _function_source_hash: u64, ) -> bool { unimplemented!(); - /*if let Some(coverage_context) = self.coverage_context() { - debug!( - "ensuring function source hash is set for instance={:?}; function_source_hash={}", - instance, function_source_hash, - ); - let mut coverage_map = coverage_context.function_coverage_map.borrow_mut(); - coverage_map - .entry(instance) - .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) - .set_function_source_hash(function_source_hash); - true - } else { - false - }*/ } fn add_coverage_counter(&mut self, _instance: Instance<'tcx>, _id: CounterValueReference, _region: CodeRegion) -> bool { - /*if let Some(coverage_context) = self.coverage_context() { - debug!( - "adding counter to coverage_regions: instance={:?}, function_source_hash={}, id={:?}, \ - at {:?}", - instance, function_source_hash, id, region, - ); - let mut coverage_regions = coverage_context.function_coverage_map.borrow_mut(); - coverage_regions - .entry(instance) - .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) - .add_counter(function_source_hash, id, region); - true - } else { - false - }*/ - // TODO + // TODO(antoyo) false } fn add_coverage_counter_expression(&mut self, _instance: Instance<'tcx>, _id: InjectedExpressionId, _lhs: ExpressionOperandId, _op: Op, _rhs: ExpressionOperandId, _region: Option) -> bool { - /*if let Some(coverage_context) = self.coverage_context() { - debug!( - "adding counter expression to coverage_regions: instance={:?}, id={:?}, {:?} {:?} {:?}, \ - at {:?}", - instance, id, lhs, op, rhs, region, - ); - let mut coverage_regions = coverage_context.function_coverage_map.borrow_mut(); - coverage_regions - .entry(instance) - .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) - .add_counter_expression(id, lhs, op, rhs, region); - true - } else { - false - }*/ - // TODO + // TODO(antoyo) false } fn add_coverage_unreachable(&mut self, _instance: Instance<'tcx>, _region: CodeRegion) -> bool { - /*if let Some(coverage_context) = self.coverage_context() { - debug!( - "adding unreachable code to coverage_regions: instance={:?}, at {:?}", - instance, region, - ); - let mut coverage_regions = coverage_context.function_coverage_map.borrow_mut(); - coverage_regions - .entry(instance) - .or_insert_with(|| FunctionCoverage::new(self.tcx, instance)) - .add_unreachable_region(region); - true - } else { - false - }*/ - // TODO + // TODO(antoyo) false } } impl<'gcc, 'tcx> CoverageInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn coverageinfo_finalize(&self) { - // TODO - //mapgen::finalize(self) + // TODO(antoyo) } fn get_pgo_func_name_var(&self, _instance: Instance<'tcx>) -> RValue<'gcc> { unimplemented!(); - /*if let Some(coverage_context) = self.coverage_context() { - debug!("getting pgo_func_name_var for instance={:?}", instance); - let mut pgo_func_name_var_map = coverage_context.pgo_func_name_var_map.borrow_mut(); - pgo_func_name_var_map - .entry(instance) - .or_insert_with(|| create_pgo_func_name_var(self, instance)) - } else { - bug!("Could not get the `coverage_context`"); - }*/ } /// Functions with MIR-based coverage are normally codegenned _only_ if @@ -133,8 +65,5 @@ impl<'gcc, 'tcx> CoverageInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> { /// added as `unreachable_region`s. fn define_unused_fn(&self, _def_id: DefId) { unimplemented!(); - /*let instance = declare_unused_fn(self, &def_id); - codegen_unused_fn_and_counter(self, instance); - add_unused_function_coverage(self, instance, def_id);*/ } } diff --git a/src/debuginfo.rs b/src/debuginfo.rs index a8902e6966d53..8661532a3595e 100644 --- a/src/debuginfo.rs +++ b/src/debuginfo.rs @@ -17,58 +17,10 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> { // names (choose between `dbg`, `debug`, `debuginfo`, `debug_info` etc.). fn dbg_var_addr(&mut self, _dbg_var: Self::DIVariable, _scope_metadata: Self::DIScope, _variable_alloca: Self::Value, _direct_offset: Size, _indirect_offsets: &[Size]) { unimplemented!(); - /*let cx = self.cx(); - - // Convert the direct and indirect offsets to address ops. - // FIXME(eddyb) use `const`s instead of getting the values via FFI, - // the values should match the ones in the DWARF standard anyway. - let op_deref = || unsafe { llvm::LLVMRustDIBuilderCreateOpDeref() }; - let op_plus_uconst = || unsafe { llvm::LLVMRustDIBuilderCreateOpPlusUconst() }; - let mut addr_ops = SmallVec::<[_; 8]>::new(); - - if direct_offset.bytes() > 0 { - addr_ops.push(op_plus_uconst()); - addr_ops.push(direct_offset.bytes() as i64); - } - for &offset in indirect_offsets { - addr_ops.push(op_deref()); - if offset.bytes() > 0 { - addr_ops.push(op_plus_uconst()); - addr_ops.push(offset.bytes() as i64); - } - } - - // FIXME(eddyb) maybe this information could be extracted from `dbg_var`, - // to avoid having to pass it down in both places? - // NB: `var` doesn't seem to know about the column, so that's a limitation. - let dbg_loc = cx.create_debug_loc(scope_metadata, span); - unsafe { - // FIXME(eddyb) replace `llvm.dbg.declare` with `llvm.dbg.addr`. - llvm::LLVMRustDIBuilderInsertDeclareAtEnd( - DIB(cx), - variable_alloca, - dbg_var, - addr_ops.as_ptr(), - addr_ops.len() as c_uint, - dbg_loc, - self.llbb(), - ); - }*/ } - /*fn set_source_location(&mut self, scope: Self::DIScope, span: Span) { - unimplemented!(); - /*debug!("set_source_location: {}", self.sess().source_map().span_to_string(span)); - - let dbg_loc = self.cx().create_debug_loc(scope, span); - - unsafe { - llvm::LLVMSetCurrentDebugLocation(self.llbuilder, dbg_loc); - }*/ - }*/ - fn insert_reference_to_gdb_debug_scripts_section_global(&mut self) { - // TODO: replace with gcc_jit_context_new_global_with_initializer() if it's added: + // TODO(antoyo): replace with gcc_jit_context_new_global_with_initializer() if it's added: // https://gcc.gnu.org/pipermail/jit/2020q3/001225.html // // Call the function to initialize global values here. @@ -76,7 +28,7 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> { use std::iter; for crate_num in self.cx.tcx.crates(()).iter().copied().chain(iter::once(LOCAL_CRATE)) { - // FIXME: better way to find if a crate is of proc-macro type? + // FIXME(antoyo): better way to find if a crate is of proc-macro type? if crate_num == LOCAL_CRATE || self.cx.tcx.dep_kind(crate_num) != CrateDepKind::MacrosOnly { // NOTE: proc-macro crates are not included in the executable, so don't call their // initialization routine. @@ -87,50 +39,25 @@ impl<'a, 'gcc, 'tcx> DebugInfoBuilderMethods for Builder<'a, 'gcc, 'tcx> { } } - // TODO - //gdb::insert_reference_to_gdb_debug_scripts_section_global(self) + // TODO(antoyo): insert reference to gdb debug scripts section global. } fn set_var_name(&mut self, _value: RValue<'gcc>, _name: &str) { unimplemented!(); - // Avoid wasting time if LLVM value names aren't even enabled. - /*if self.sess().fewer_names() { - return; - } - - // Only function parameters and instructions are local to a function, - // don't change the name of anything else (e.g. globals). - let param_or_inst = unsafe { - llvm::LLVMIsAArgument(value).is_some() || llvm::LLVMIsAInstruction(value).is_some() - }; - if !param_or_inst { - return; - } - - // Avoid replacing the name if it already exists. - // While we could combine the names somehow, it'd - // get noisy quick, and the usefulness is dubious. - if llvm::get_value_name(value).is_empty() { - llvm::set_value_name(value, name.as_bytes()); - }*/ } fn set_dbg_loc(&mut self, _dbg_loc: Self::DILocation) { unimplemented!(); - /*unsafe { - let dbg_loc_as_llval = llvm::LLVMRustMetadataAsValue(self.cx().llcx, dbg_loc); - llvm::LLVMSetCurrentDebugLocation(self.llbuilder, dbg_loc_as_llval); - }*/ } } impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn create_vtable_metadata(&self, _ty: Ty<'tcx>, _vtable: Self::Value) { - //metadata::create_vtable_metadata(self, ty, vtable) + // TODO(antoyo) } fn create_function_debug_context(&self, _instance: Instance<'tcx>, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _llfn: RValue<'gcc>, _mir: &mir::Body<'tcx>) -> Option> { - // TODO + // TODO(antoyo) None } @@ -139,7 +66,7 @@ impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> { } fn debuginfo_finalize(&self) { - //unimplemented!(); + // TODO(antoyo) } fn create_dbg_var(&self, _variable_name: Symbol, _variable_type: Ty<'tcx>, _scope_metadata: Self::DIScope, _variable_kind: VariableKind, _span: Span) -> Self::DIVariable { @@ -148,260 +75,9 @@ impl<'gcc, 'tcx> DebugInfoMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn dbg_scope_fn(&self, _instance: Instance<'tcx>, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>, _maybe_definition_llfn: Option>) -> Self::DIScope { unimplemented!(); - /*let def_id = instance.def_id(); - let containing_scope = get_containing_scope(self, instance); - let span = self.tcx.def_span(def_id); - let loc = self.lookup_debug_loc(span.lo()); - let file_metadata = file_metadata(self, &loc.file); - - let function_type_metadata = unsafe { - let fn_signature = get_function_signature(self, fn_abi); - llvm::LLVMRustDIBuilderCreateSubroutineType(DIB(self), fn_signature) - }; - - // Find the enclosing function, in case this is a closure. - let def_key = self.tcx().def_key(def_id); - let mut name = def_key.disambiguated_data.data.to_string(); - - let enclosing_fn_def_id = self.tcx().closure_base_def_id(def_id); - - // Get_template_parameters() will append a `<...>` clause to the function - // name if necessary. - let generics = self.tcx().generics_of(enclosing_fn_def_id); - let substs = instance.substs.truncate_to(self.tcx(), generics); - let template_parameters = get_template_parameters(self, &generics, substs, &mut name); - - let linkage_name = &mangled_name_of_instance(self, instance).name; - // Omit the linkage_name if it is the same as subprogram name. - let linkage_name = if &name == linkage_name { "" } else { linkage_name }; - - // FIXME(eddyb) does this need to be separate from `loc.line` for some reason? - let scope_line = loc.line; - - let mut flags = DIFlags::FlagPrototyped; - - if fn_abi.ret.layout.abi.is_uninhabited() { - flags |= DIFlags::FlagNoReturn; - } - - let mut spflags = DISPFlags::SPFlagDefinition; - if is_node_local_to_unit(self, def_id) { - spflags |= DISPFlags::SPFlagLocalToUnit; - } - if self.sess().opts.optimize != config::OptLevel::No { - spflags |= DISPFlags::SPFlagOptimized; - } - if let Some((id, _)) = self.tcx.entry_fn(LOCAL_CRATE) { - if id.to_def_id() == def_id { - spflags |= DISPFlags::SPFlagMainSubprogram; - } - } - - unsafe { - return llvm::LLVMRustDIBuilderCreateFunction( - DIB(self), - containing_scope, - name.as_ptr().cast(), - name.len(), - linkage_name.as_ptr().cast(), - linkage_name.len(), - file_metadata, - loc.line.unwrap_or(UNKNOWN_LINE_NUMBER), - function_type_metadata, - scope_line.unwrap_or(UNKNOWN_LINE_NUMBER), - flags, - spflags, - maybe_definition_llfn, - template_parameters, - None, - ); - } - - fn get_function_signature<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - ) -> &'ll DIArray { - if cx.sess().opts.debuginfo == DebugInfo::Limited { - return create_DIArray(DIB(cx), &[]); - } - - let mut signature = Vec::with_capacity(fn_abi.args.len() + 1); - - // Return type -- llvm::DIBuilder wants this at index 0 - signature.push(if fn_abi.ret.is_ignore() { - None - } else { - Some(type_metadata(cx, fn_abi.ret.layout.ty, rustc_span::DUMMY_SP)) - }); - - // Arguments types - if cx.sess().target.options.is_like_msvc { - // FIXME(#42800): - // There is a bug in MSDIA that leads to a crash when it encounters - // a fixed-size array of `u8` or something zero-sized in a - // function-type (see #40477). - // As a workaround, we replace those fixed-size arrays with a - // pointer-type. So a function `fn foo(a: u8, b: [u8; 4])` would - // appear as `fn foo(a: u8, b: *const u8)` in debuginfo, - // and a function `fn bar(x: [(); 7])` as `fn bar(x: *const ())`. - // This transformed type is wrong, but these function types are - // already inaccurate due to ABI adjustments (see #42800). - signature.extend(fn_abi.args.iter().map(|arg| { - let t = arg.layout.ty; - let t = match t.kind() { - ty::Array(ct, _) - if (*ct == cx.tcx.types.u8) || cx.layout_of(ct).is_zst() => - { - cx.tcx.mk_imm_ptr(ct) - } - _ => t, - }; - Some(type_metadata(cx, t, rustc_span::DUMMY_SP)) - })); - } else { - signature.extend( - fn_abi - .args - .iter() - .map(|arg| Some(type_metadata(cx, arg.layout.ty, rustc_span::DUMMY_SP))), - ); - } - - create_DIArray(DIB(cx), &signature[..]) - } - - fn get_template_parameters<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - generics: &ty::Generics, - substs: SubstsRef<'tcx>, - name_to_append_suffix_to: &mut String, - ) -> &'ll DIArray { - if substs.types().next().is_none() { - return create_DIArray(DIB(cx), &[]); - } - - name_to_append_suffix_to.push('<'); - for (i, actual_type) in substs.types().enumerate() { - if i != 0 { - name_to_append_suffix_to.push(','); - } - - let actual_type = - cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), actual_type); - // Add actual type name to <...> clause of function name - let actual_type_name = compute_debuginfo_type_name(cx.tcx(), actual_type, true); - name_to_append_suffix_to.push_str(&actual_type_name[..]); - } - name_to_append_suffix_to.push('>'); - - // Again, only create type information if full debuginfo is enabled - let template_params: Vec<_> = if cx.sess().opts.debuginfo == DebugInfo::Full { - let names = get_parameter_names(cx, generics); - substs - .iter() - .zip(names) - .filter_map(|(kind, name)| { - if let GenericArgKind::Type(ty) = kind.unpack() { - let actual_type = - cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); - let actual_type_metadata = - type_metadata(cx, actual_type, rustc_span::DUMMY_SP); - let name = name.as_str(); - Some(unsafe { - Some(llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( - DIB(cx), - None, - name.as_ptr().cast(), - name.len(), - actual_type_metadata, - )) - }) - } else { - None - } - }) - .collect() - } else { - vec![] - }; - - create_DIArray(DIB(cx), &template_params[..]) - } - - fn get_parameter_names(cx: &CodegenCx<'_, '_>, generics: &ty::Generics) -> Vec { - let mut names = generics - .parent - .map_or(vec![], |def_id| get_parameter_names(cx, cx.tcx.generics_of(def_id))); - names.extend(generics.params.iter().map(|param| param.name)); - names - } - - fn get_containing_scope<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - instance: Instance<'tcx>, - ) -> &'ll DIScope { - // First, let's see if this is a method within an inherent impl. Because - // if yes, we want to make the result subroutine DIE a child of the - // subroutine's self-type. - let self_type = cx.tcx.impl_of_method(instance.def_id()).and_then(|impl_def_id| { - // If the method does *not* belong to a trait, proceed - if cx.tcx.trait_id_of_impl(impl_def_id).is_none() { - let impl_self_ty = cx.tcx.subst_and_normalize_erasing_regions( - instance.substs, - ty::ParamEnv::reveal_all(), - &cx.tcx.type_of(impl_def_id), - ); - - // Only "class" methods are generally understood by LLVM, - // so avoid methods on other types (e.g., `<*mut T>::null`). - match impl_self_ty.kind() { - ty::Adt(def, ..) if !def.is_box() => { - // Again, only create type information if full debuginfo is enabled - if cx.sess().opts.debuginfo == DebugInfo::Full - && !impl_self_ty.needs_subst() - { - Some(type_metadata(cx, impl_self_ty, rustc_span::DUMMY_SP)) - } else { - Some(namespace::item_namespace(cx, def.did)) - } - } - _ => None, - } - } else { - // For trait method impls we still use the "parallel namespace" - // strategy - None - } - }); - - self_type.unwrap_or_else(|| { - namespace::item_namespace( - cx, - DefId { - krate: instance.def_id().krate, - index: cx - .tcx - .def_key(instance.def_id()) - .parent - .expect("get_containing_scope: missing parent?"), - }, - ) - }) - }*/ } fn dbg_loc(&self, _scope: Self::DIScope, _inlined_at: Option, _span: Span) -> Self::DILocation { unimplemented!(); - /*let DebugLoc { line, col, .. } = self.lookup_debug_loc(span.lo()); - - unsafe { - llvm::LLVMRustDIBuilderCreateDebugLocation( - utils::debug_context(self).llcontext, - line.unwrap_or(UNKNOWN_LINE_NUMBER), - col.unwrap_or(UNKNOWN_COLUMN_NUMBER), - scope, - inlined_at, - ) - }*/ } } diff --git a/src/declare.rs b/src/declare.rs index 339a613c09661..c1382bf2f4a71 100644 --- a/src/declare.rs +++ b/src/declare.rs @@ -35,7 +35,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } pub fn declare_global_with_linkage(&self, name: &str, ty: Type<'gcc>, linkage: GlobalKind) -> RValue<'gcc> { - //debug!("declare_global_with_linkage(name={:?})", name); let global = self.context.new_global(None, linkage, ty, name) .get_address(None); self.globals.borrow_mut().insert(name.to_string(), global); @@ -48,13 +47,12 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { pub fn declare_func(&self, name: &str, return_type: Type<'gcc>, params: &[Type<'gcc>], variadic: bool) -> RValue<'gcc> { self.linkage.set(FunctionType::Exported); let func = declare_raw_fn(self, name, () /*llvm::CCallConv*/, return_type, params, variadic); - // FIXME: this is a wrong cast. That requires changing the compiler API. + // FIXME(antoyo): this is a wrong cast. That requires changing the compiler API. unsafe { std::mem::transmute(func) } } pub fn declare_global(&self, name: &str, ty: Type<'gcc>, is_tls: bool, link_section: Option) -> RValue<'gcc> { - //debug!("declare_global(name={:?})", name); - // FIXME: correctly support global variable initialization. + // FIXME(antoyo): correctly support global variable initialization. if name.starts_with(ARGV_INIT_ARRAY) { // NOTE: hack to avoid having to update the names in mangled_std_symbols: we save the // name of the variable now to actually declare it later. @@ -82,7 +80,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } pub fn declare_cfn(&self, name: &str, _fn_type: Type<'gcc>) -> RValue<'gcc> { - // TODO: use the fn_type parameter. + // TODO(antoyo): use the fn_type parameter. let const_string = self.context.new_type::().make_pointer().make_pointer(); let return_type = self.type_i32(); let variadic = false; @@ -91,7 +89,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { // NOTE: it is needed to set the current_func here as well, because get_fn() is not called // for the main function. *self.current_func.borrow_mut() = Some(func); - // FIXME: this is a wrong cast. That requires changing the compiler API. + // FIXME(antoyo): this is a wrong cast. That requires changing the compiler API. unsafe { std::mem::transmute(func) } } @@ -128,11 +126,9 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { self.global_names.borrow_mut().insert(global, global_name.to_string()); self.argv_initialized.set(true); } - //debug!("declare_rust_fn(name={:?}, fn_abi={:?})", name, fn_abi); let (return_type, params, variadic) = fn_abi.gcc_type(self); let func = declare_raw_fn(self, name, () /*fn_abi.llvm_cconv()*/, return_type, ¶ms, variadic); - //fn_abi.apply_attrs_llfn(self, func); - // FIXME: this is a wrong cast. That requires changing the compiler API. + // FIXME(antoyo): this is a wrong cast. That requires changing the compiler API. unsafe { std::mem::transmute(func) } } @@ -146,19 +142,9 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } pub fn get_declared_value(&self, name: &str) -> Option> { - //debug!("get_declared_value(name={:?})", name); - // TODO: use a different field than globals, because this seems to return a function? + // TODO(antoyo): use a different field than globals, because this seems to return a function? self.globals.borrow().get(name).cloned() } - - /*fn get_defined_value(&self, name: &str) -> Option> { - // TODO: gcc does not allow global initialization. - None - /*self.get_declared_value(name).and_then(|val| { - let declaration = unsafe { llvm::LLVMIsDeclaration(val) != 0 }; - if !declaration { Some(val) } else { None } - })*/ - }*/ } /// Declare a function. @@ -166,11 +152,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { /// If there’s a value with the same name already declared, the function will /// update the declaration and return existing Value instead. fn declare_raw_fn<'gcc>(cx: &CodegenCx<'gcc, '_>, name: &str, _callconv: () /*llvm::CallConv*/, return_type: Type<'gcc>, param_types: &[Type<'gcc>], variadic: bool) -> Function<'gcc> { - //debug!("declare_raw_fn(name={:?}, ty={:?})", name, ty); - /*let llfn = unsafe { - llvm::LLVMRustGetOrInsertFunction(cx.llmod, name.as_ptr().cast(), name.len(), ty) - };*/ - if name.starts_with("llvm.") { return llvm::intrinsic(name, cx); } @@ -180,32 +161,24 @@ fn declare_raw_fn<'gcc>(cx: &CodegenCx<'gcc, '_>, name: &str, _callconv: () /*ll } else { let params: Vec<_> = param_types.into_iter().enumerate() - .map(|(index, param)| cx.context.new_parameter(None, *param, &format!("param{}", index))) // TODO: set name. + .map(|(index, param)| cx.context.new_parameter(None, *param, &format!("param{}", index))) // TODO(antoyo): set name. .collect(); let func = cx.context.new_function(None, cx.linkage.get(), return_type, ¶ms, mangle_name(name), variadic); cx.functions.borrow_mut().insert(name.to_string(), func); func }; - //llvm::SetFunctionCallConv(llfn, callconv); // TODO - // Function addresses in Rust are never significant, allowing functions to - // be merged. - //llvm::SetUnnamedAddress(llfn, llvm::UnnamedAddr::Global); // TODO - - /*if cx.tcx.sess.opts.cg.no_redzone.unwrap_or(cx.tcx.sess.target.target.options.disable_redzone) { - llvm::Attribute::NoRedZone.apply_llfn(Function, llfn); - }*/ - - //attributes::default_optimisation_attrs(cx.tcx.sess, llfn); - //attributes::non_lazy_bind(cx.sess(), llfn); + // TODO(antoyo): set function calling convention. + // TODO(antoyo): set unnamed address. + // TODO(antoyo): set no red zone function attribute. + // TODO(antoyo): set attributes for optimisation. + // TODO(antoyo): set attributes for non lazy bind. - // FIXME: invalid cast. - // TODO: is this line useful? - //cx.globals.borrow_mut().insert(name.to_string(), unsafe { std::mem::transmute(func) }); + // FIXME(antoyo): invalid cast. func } -// FIXME: this is a hack because libgccjit currently only supports alpha, num and _. +// FIXME(antoyo): this is a hack because libgccjit currently only supports alpha, num and _. // Unsupported characters: `$` and `.`. pub fn mangle_name(name: &str) -> String { name.replace(|char: char| { diff --git a/src/intrinsic/llvm.rs b/src/intrinsic/llvm.rs index bf9472d3ea942..b074febc521eb 100644 --- a/src/intrinsic/llvm.rs +++ b/src/intrinsic/llvm.rs @@ -11,16 +11,12 @@ pub fn intrinsic<'gcc, 'tcx>(name: &str, cx: &CodegenCx<'gcc, 'tcx>) -> Function cx.functions.borrow_mut().insert(gcc_name.to_string(), func); return func; }, - // TODO: this doc specifies the equivalent GCC builtins: http://huonw.github.io/llvmint/llvmint/x86/index.html + // NOTE: this doc specifies the equivalent GCC builtins: http://huonw.github.io/llvmint/llvmint/x86/index.html "llvm.x86.sse2.cmp.pd" => "__builtin_ia32_cmppd", "llvm.x86.sse2.movmsk.pd" => "__builtin_ia32_movmskpd", "llvm.x86.sse2.pmovmskb.128" => "__builtin_ia32_pmovmskb128", _ => unimplemented!("unsupported LLVM intrinsic {}", name) }; - println!("Get target builtin"); unimplemented!(); - /*let func = cx.context.get_target_builtin_function(gcc_name); - cx.functions.borrow_mut().insert(gcc_name.to_string(), func); - func*/ } diff --git a/src/intrinsic/mod.rs b/src/intrinsic/mod.rs index ad6dfbffbac96..a79be7cfc74c8 100644 --- a/src/intrinsic/mod.rs +++ b/src/intrinsic/mod.rs @@ -96,7 +96,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { let llval = match name { _ if simple.is_some() => { - // FIXME: remove this cast when the API supports function. + // FIXME(antoyo): remove this cast when the API supports function. let func = unsafe { std::mem::transmute(simple.expect("simple")) }; self.call(self.type_void(), func, &args.iter().map(|arg| arg.immediate()).collect::>(), None) }, @@ -118,40 +118,12 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { } sym::breakpoint => { unimplemented!(); - /*let llfn = self.get_intrinsic(&("llvm.debugtrap")); - self.call(llfn, &[], None)*/ } sym::va_copy => { unimplemented!(); - /*let intrinsic = self.cx().get_intrinsic(&("llvm.va_copy")); - self.call(intrinsic, &[args[0].immediate(), args[1].immediate()], None)*/ } sym::va_arg => { unimplemented!(); - /*match fn_abi.ret.layout.abi { - abi::Abi::Scalar(ref scalar) => { - match scalar.value { - Primitive::Int(..) => { - if self.cx().size_of(ret_ty).bytes() < 4 { - // `va_arg` should not be called on a integer type - // less than 4 bytes in length. If it is, promote - // the integer to a `i32` and truncate the result - // back to the smaller type. - let promoted_result = emit_va_arg(self, args[0], tcx.types.i32); - self.trunc(promoted_result, llret_ty) - } else { - emit_va_arg(self, args[0], ret_ty) - } - } - Primitive::F64 | Primitive::Pointer => { - emit_va_arg(self, args[0], ret_ty) - } - // `va_arg` should never be used with the return type f32. - Primitive::F32 => bug!("the va_arg intrinsic does not work with `f32`"), - } - } - _ => bug!("the va_arg intrinsic does not work with non-scalar types"), - }*/ } sym::volatile_load | sym::unaligned_volatile_load => { @@ -161,15 +133,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { ptr = self.pointercast(ptr, self.type_ptr_to(ty.gcc_type(self))); } let load = self.volatile_load(ptr.get_type(), ptr); - // TODO - /*let align = if name == sym::unaligned_volatile_load { - 1 - } else { - self.align_of(tp_ty).bytes() as u32 - }; - unsafe { - llvm::LLVMSetAlignment(load, align); - }*/ + // TODO(antoyo): set alignment. self.to_immediate(load, self.layout_of(tp_ty)) } sym::volatile_store => { @@ -187,24 +151,6 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { | sym::prefetch_read_instruction | sym::prefetch_write_instruction => { unimplemented!(); - /*let expect = self.get_intrinsic(&("llvm.prefetch")); - let (rw, cache_type) = match name { - sym::prefetch_read_data => (0, 1), - sym::prefetch_write_data => (1, 1), - sym::prefetch_read_instruction => (0, 0), - sym::prefetch_write_instruction => (1, 0), - _ => bug!(), - }; - self.call( - expect, - &[ - args[0].immediate(), - self.const_i32(rw), - args[1].immediate(), - self.const_i32(cache_type), - ], - None, - )*/ } sym::ctlz | sym::ctlz_nonzero @@ -257,10 +203,6 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { self.block = Some(after_block); result.to_rvalue() - - /*let y = self.const_bool(false); - let llfn = self.get_intrinsic(&format!("llvm.{}.i{}", name, width)); - self.call(llfn, &[args[0].immediate(), y], None)*/ } sym::ctlz_nonzero => { self.count_leading_zeroes(width, args[0].immediate()) @@ -274,11 +216,11 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { args[0].immediate() // byte swap a u8/i8 is just a no-op } else { - // TODO: check if it's faster to use string literals and a + // TODO(antoyo): check if it's faster to use string literals and a // match instead of format!. let bswap = self.cx.context.get_builtin_function(&format!("__builtin_bswap{}", width)); let mut arg = args[0].immediate(); - // FIXME: this cast should not be necessary. Remove + // FIXME(antoyo): this cast should not be necessary. Remove // when having proper sized integer types. let param_type = bswap.get_param(0).to_rvalue().get_type(); if param_type != arg.get_type() { @@ -289,7 +231,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { }, sym::bitreverse => self.bit_reverse(width, args[0].immediate()), sym::rotate_left | sym::rotate_right => { - // TODO: implement using algorithm from: + // TODO(antoyo): implement using algorithm from: // https://blog.regehr.org/archives/1063 // for other platforms. let is_left = name == sym::rotate_left; @@ -346,7 +288,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { self.const_bool(true) } /*else if use_integer_compare { - let integer_ty = self.type_ix(layout.size.bits()); // FIXME: LLVM creates an integer of 96 bits for [i32; 3], but gcc doesn't support this, so it creates an integer of 128 bits. + let integer_ty = self.type_ix(layout.size.bits()); // FIXME(antoyo): LLVM creates an integer of 96 bits for [i32; 3], but gcc doesn't support this, so it creates an integer of 128 bits. let ptr_ty = self.type_ptr_to(integer_ty); let a_ptr = self.bitcast(a, ptr_ty); let a_val = self.load(integer_ty, a_ptr, layout.align.abi); @@ -396,38 +338,27 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { } fn assume(&mut self, value: Self::Value) { - // TODO: switch to asumme when it exists. + // TODO(antoyo): switch to asumme when it exists. // Or use something like this: // #define __assume(cond) do { if (!(cond)) __builtin_unreachable(); } while (0) self.expect(value, true); } fn expect(&mut self, cond: Self::Value, _expected: bool) -> Self::Value { - // TODO - /*let expect = self.context.get_builtin_function("__builtin_expect"); - let expect: RValue<'gcc> = unsafe { std::mem::transmute(expect) }; - self.call(expect, &[cond, self.const_bool(expected)], None)*/ + // TODO(antoyo) cond } fn sideeffect(&mut self) { - // TODO - /*if self.tcx().sess.opts.debugging_opts.insert_sideeffect { - let fnname = self.get_intrinsic(&("llvm.sideeffect")); - self.call(fnname, &[], None); - }*/ + // TODO(antoyo) } fn va_start(&mut self, _va_list: RValue<'gcc>) -> RValue<'gcc> { unimplemented!(); - /*let intrinsic = self.cx().get_intrinsic("llvm.va_start"); - self.call(intrinsic, &[va_list], None)*/ } fn va_end(&mut self, _va_list: RValue<'gcc>) -> RValue<'gcc> { unimplemented!(); - /*let intrinsic = self.cx().get_intrinsic("llvm.va_end"); - self.call(intrinsic, &[va_list], None)*/ } } @@ -634,7 +565,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { step4 }, 32 => { - // TODO: Refactor with other implementations. + // TODO(antoyo): Refactor with other implementations. // First step. let left = self.and(value, context.new_rvalue_from_long(typ, 0x55555555)); let left = self.shl(left, context.new_rvalue_from_long(typ, 1)); @@ -681,7 +612,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { // Second step. let left = self.and(step1, context.new_rvalue_from_long(typ, 0x0001FFFF0001FFFF)); let left = self.shl(left, context.new_rvalue_from_long(typ, 15)); - let right = self.and(step1, context.new_rvalue_from_long(typ, 0xFFFE0000FFFE0000u64 as i64)); // TODO: transmute the number instead? + let right = self.and(step1, context.new_rvalue_from_long(typ, 0xFFFE0000FFFE0000u64 as i64)); // TODO(antoyo): transmute the number instead? let right = self.lshr(right, context.new_rvalue_from_long(typ, 17)); let step2 = self.or(left, right); @@ -715,7 +646,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { step5 }, 128 => { - // TODO: find a more efficient implementation? + // TODO(antoyo): find a more efficient implementation? let sixty_four = self.context.new_rvalue_from_long(typ, 64); let high = self.context.new_cast(None, value >> sixty_four, self.u64_type); let low = self.context.new_cast(None, value, self.u64_type); @@ -735,7 +666,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { } fn count_leading_zeroes(&self, width: u64, arg: RValue<'gcc>) -> RValue<'gcc> { - // TODO: use width? + // TODO(antoyo): use width? let arg_type = arg.get_type(); let count_leading_zeroes = if arg_type.is_uint(&self.cx) { @@ -873,11 +804,11 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> { } fn pop_count(&self, value: RValue<'gcc>) -> RValue<'gcc> { - // TODO: use the optimized version with fewer operations. + // TODO(antoyo): use the optimized version with fewer operations. let value_type = value.get_type(); if value_type.is_u128(&self.cx) { - // TODO: implement in the normal algorithm below to have a more efficient + // TODO(antoyo): implement in the normal algorithm below to have a more efficient // implementation (that does not require a call to __popcountdi2). let popcount = self.context.get_builtin_function("__builtin_popcountll"); let sixty_four = self.context.new_rvalue_from_long(value_type, 64); @@ -1083,204 +1014,8 @@ fn try_intrinsic<'gcc, 'tcx>(bx: &mut Builder<'_, 'gcc, 'tcx>, try_func: RValue< } else if wants_msvc_seh(bx.sess()) { unimplemented!(); - //codegen_msvc_try(bx, try_func, data, catch_func, dest); } else { unimplemented!(); - //codegen_gnu_try(bx, try_func, data, catch_func, dest); } } - -// MSVC's definition of the `rust_try` function. -// -// This implementation uses the new exception handling instructions in LLVM -// which have support in LLVM for SEH on MSVC targets. Although these -// instructions are meant to work for all targets, as of the time of this -// writing, however, LLVM does not recommend the usage of these new instructions -// as the old ones are still more optimized. -/*fn codegen_msvc_try<'a, 'gcc, 'tcx>(_bx: &mut Builder<'a, 'gcc, 'tcx>, _try_func: RValue<'gcc>, _data: RValue<'gcc>, _catch_func: RValue<'gcc>, _dest: RValue<'gcc>) { - unimplemented!(); - /*let llfn = get_rust_try_fn(bx, &mut |mut bx| { - bx.set_personality_fn(bx.eh_personality()); - bx.sideeffect(); - - let mut normal = bx.build_sibling_block("normal"); - let mut catchswitch = bx.build_sibling_block("catchswitch"); - let mut catchpad = bx.build_sibling_block("catchpad"); - let mut caught = bx.build_sibling_block("caught"); - - let try_func = llvm::get_param(bx.llfn(), 0); - let data = llvm::get_param(bx.llfn(), 1); - let catch_func = llvm::get_param(bx.llfn(), 2); - - // We're generating an IR snippet that looks like: - // - // declare i32 @rust_try(%try_func, %data, %catch_func) { - // %slot = alloca u8* - // invoke %try_func(%data) to label %normal unwind label %catchswitch - // - // normal: - // ret i32 0 - // - // catchswitch: - // %cs = catchswitch within none [%catchpad] unwind to caller - // - // catchpad: - // %tok = catchpad within %cs [%type_descriptor, 0, %slot] - // %ptr = load %slot - // call %catch_func(%data, %ptr) - // catchret from %tok to label %caught - // - // caught: - // ret i32 1 - // } - // - // This structure follows the basic usage of throw/try/catch in LLVM. - // For example, compile this C++ snippet to see what LLVM generates: - // - // #include - // - // struct rust_panic { - // rust_panic(const rust_panic&); - // ~rust_panic(); - // - // uint64_t x[2]; - // }; - // - // int __rust_try( - // void (*try_func)(void*), - // void *data, - // void (*catch_func)(void*, void*) noexcept - // ) { - // try { - // try_func(data); - // return 0; - // } catch(rust_panic& a) { - // catch_func(data, &a); - // return 1; - // } - // } - // - // More information can be found in libstd's seh.rs implementation. - let ptr_align = bx.tcx().data_layout.pointer_align.abi; - let slot = bx.alloca(bx.type_i8p(), ptr_align); - bx.invoke(try_func, &[data], normal.llbb(), catchswitch.llbb(), None); - - normal.ret(bx.const_i32(0)); - - let cs = catchswitch.catch_switch(None, None, 1); - catchswitch.add_handler(cs, catchpad.llbb()); - - // We can't use the TypeDescriptor defined in libpanic_unwind because it - // might be in another DLL and the SEH encoding only supports specifying - // a TypeDescriptor from the current module. - // - // However this isn't an issue since the MSVC runtime uses string - // comparison on the type name to match TypeDescriptors rather than - // pointer equality. - // - // So instead we generate a new TypeDescriptor in each module that uses - // `try` and let the linker merge duplicate definitions in the same - // module. - // - // When modifying, make sure that the type_name string exactly matches - // the one used in src/libpanic_unwind/seh.rs. - let type_info_vtable = bx.declare_global("??_7type_info@@6B@", bx.type_i8p()); - let type_name = bx.const_bytes(b"rust_panic\0"); - let type_info = - bx.const_struct(&[type_info_vtable, bx.const_null(bx.type_i8p()), type_name], false); - let tydesc = bx.declare_global("__rust_panic_type_info", bx.val_ty(type_info)); - unsafe { - llvm::LLVMRustSetLinkage(tydesc, llvm::Linkage::LinkOnceODRLinkage); - llvm::SetUniqueComdat(bx.llmod, tydesc); - llvm::LLVMSetInitializer(tydesc, type_info); - } - - // The flag value of 8 indicates that we are catching the exception by - // reference instead of by value. We can't use catch by value because - // that requires copying the exception object, which we don't support - // since our exception object effectively contains a Box. - // - // Source: MicrosoftCXXABI::getAddrOfCXXCatchHandlerType in clang - let flags = bx.const_i32(8); - let funclet = catchpad.catch_pad(cs, &[tydesc, flags, slot]); - let ptr = catchpad.load(slot, ptr_align); - catchpad.call(catch_func, &[data, ptr], Some(&funclet)); - - catchpad.catch_ret(&funclet, caught.llbb()); - - caught.ret(bx.const_i32(1)); - }); - - // Note that no invoke is used here because by definition this function - // can't panic (that's what it's catching). - let ret = bx.call(llfn, &[try_func, data, catch_func], None); - let i32_align = bx.tcx().data_layout.i32_align.abi; - bx.store(ret, dest, i32_align);*/ -}*/ - -// Definition of the standard `try` function for Rust using the GNU-like model -// of exceptions (e.g., the normal semantics of LLVM's `landingpad` and `invoke` -// instructions). -// -// This codegen is a little surprising because we always call a shim -// function instead of inlining the call to `invoke` manually here. This is done -// because in LLVM we're only allowed to have one personality per function -// definition. The call to the `try` intrinsic is being inlined into the -// function calling it, and that function may already have other personality -// functions in play. By calling a shim we're guaranteed that our shim will have -// the right personality function. -/*fn codegen_gnu_try<'a, 'gcc, 'tcx>(_bx: &mut Builder<'a, 'gcc, 'tcx>, _try_func: RValue<'gcc>, _data: RValue<'gcc>, _catch_func: RValue<'gcc>, _dest: RValue<'gcc>) { - unimplemented!(); - /*let llfn = get_rust_try_fn(bx, &mut |mut bx| { - // Codegens the shims described above: - // - // bx: - // invoke %try_func(%data) normal %normal unwind %catch - // - // normal: - // ret 0 - // - // catch: - // (%ptr, _) = landingpad - // call %catch_func(%data, %ptr) - // ret 1 - - bx.sideeffect(); - - let mut then = bx.build_sibling_block("then"); - let mut catch = bx.build_sibling_block("catch"); - - let try_func = llvm::get_param(bx.llfn(), 0); - let data = llvm::get_param(bx.llfn(), 1); - let catch_func = llvm::get_param(bx.llfn(), 2); - bx.invoke(try_func, &[data], then.llbb(), catch.llbb(), None); - then.ret(bx.const_i32(0)); - - // Type indicator for the exception being thrown. - // - // The first value in this tuple is a pointer to the exception object - // being thrown. The second value is a "selector" indicating which of - // the landing pad clauses the exception's type had been matched to. - // rust_try ignores the selector. - let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false); - let vals = catch.landing_pad(lpad_ty, bx.eh_personality(), 1); - let tydesc = match bx.tcx().lang_items().eh_catch_typeinfo() { - Some(tydesc) => { - let tydesc = bx.get_static(tydesc); - bx.bitcast(tydesc, bx.type_i8p()) - } - None => bx.const_null(bx.type_i8p()), - }; - catch.add_clause(vals, tydesc); - let ptr = catch.extract_value(vals, 0); - catch.call(catch_func, &[data, ptr], None); - catch.ret(bx.const_i32(1)); - }); - - // Note that no invoke is used here because by definition this function - // can't panic (that's what it's catching). - let ret = bx.call(llfn, &[try_func, data, catch_func], None); - let i32_align = bx.tcx().data_layout.i32_align.abi; - bx.store(ret, dest, i32_align);*/ -}*/ diff --git a/src/intrinsic/simd.rs b/src/intrinsic/simd.rs index 71cf5cce9f4e1..26a42217e4c56 100644 --- a/src/intrinsic/simd.rs +++ b/src/intrinsic/simd.rs @@ -12,8 +12,6 @@ use rustc_span::{Span, Symbol, sym}; use crate::builder::Builder; pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, name: Symbol, callee_ty: Ty<'tcx>, args: &[OperandRef<'tcx, RValue<'gcc>>], ret_ty: Ty<'tcx>, llret_ty: Type<'gcc>, span: Span) -> Result, ()> { - //println!("Generic simd: {}", name); - // macros for error handling: macro_rules! emit_error { ($msg: tt) => { @@ -56,33 +54,6 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, let arg_tys = sig.inputs(); let name_str = &*name.as_str(); - /*if name == sym::simd_select_bitmask { - let in_ty = arg_tys[0]; - let m_len = match in_ty.kind() { - // Note that this `.unwrap()` crashes for isize/usize, that's sort - // of intentional as there's not currently a use case for that. - ty::Int(i) => i.bit_width().unwrap(), - ty::Uint(i) => i.bit_width().unwrap(), - _ => return_error!("`{}` is not an integral type", in_ty), - }; - require_simd!(arg_tys[1], "argument"); - let (v_len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); - require!( - // Allow masks for vectors with fewer than 8 elements to be - // represented with a u8 or i8. - m_len == v_len || (m_len == 8 && v_len < 8), - "mismatched lengths: mask length `{}` != other vector length `{}`", - m_len, - v_len - ); - let i1 = bx.type_i1(); - let im = bx.type_ix(v_len); - let i1xn = bx.type_vector(i1, v_len); - let m_im = bx.trunc(args[0].immediate(), im); - let m_i1s = bx.bitcast(m_im, i1xn); - return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate())); - }*/ - // every intrinsic below takes a SIMD vector as its first argument require_simd!(arg_tys[0], "input"); let in_ty = arg_tys[0]; @@ -153,37 +124,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, out_ty ); - //let total_len = u128::from(in_len) * 2; - let vector = args[2].immediate(); - // TODO: - /*let indices: Option> = (0..n) - .map(|i| { - let arg_idx = i; - let val = bx.const_get_vector_element(vector, i as u64); - match bx.const_to_opt_u128(val, true) { - None => { - emit_error!("shuffle index #{} is not a constant", arg_idx); - None - } - Some(idx) if idx >= total_len => { - emit_error!( - "shuffle index #{} is out of bounds (limit {})", - arg_idx, - total_len - ); - None - } - Some(idx) => Some(bx.const_i32(idx as i32)), - } - }) - .collect(); - let indices = match indices { - Some(i) => i, - None => return Ok(bx.const_null(llret_ty)), - };*/ - return Ok(bx.shuffle_vector( args[0].immediate(), args[1].immediate(), @@ -191,723 +133,6 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, )); } - /*if name == sym::simd_insert { - require!( - in_elem == arg_tys[2], - "expected inserted type `{}` (element of input `{}`), found `{}`", - in_elem, - in_ty, - arg_tys[2] - ); - return Ok(bx.insert_element( - args[0].immediate(), - args[2].immediate(), - args[1].immediate(), - )); - } - if name == sym::simd_extract { - require!( - ret_ty == in_elem, - "expected return type `{}` (element of input `{}`), found `{}`", - in_elem, - in_ty, - ret_ty - ); - return Ok(bx.extract_element(args[0].immediate(), args[1].immediate())); - } - - if name == sym::simd_select { - let m_elem_ty = in_elem; - let m_len = in_len; - require_simd!(arg_tys[1], "argument"); - let (v_len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); - require!( - m_len == v_len, - "mismatched lengths: mask length `{}` != other vector length `{}`", - m_len, - v_len - ); - match m_elem_ty.kind() { - ty::Int(_) => {} - _ => return_error!("mask element type is `{}`, expected `i_`", m_elem_ty), - } - // truncate the mask to a vector of i1s - let i1 = bx.type_i1(); - let i1xn = bx.type_vector(i1, m_len as u64); - let m_i1s = bx.trunc(args[0].immediate(), i1xn); - return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate())); - } - - if name == sym::simd_bitmask { - // The `fn simd_bitmask(vector) -> unsigned integer` intrinsic takes a - // vector mask and returns an unsigned integer containing the most - // significant bit (MSB) of each lane. - - // If the vector has less than 8 lanes, an u8 is returned with zeroed - // trailing bits. - let expected_int_bits = in_len.max(8); - match ret_ty.kind() { - ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => (), - _ => return_error!("bitmask `{}`, expected `u{}`", ret_ty, expected_int_bits), - } - - // Integer vector : - let (i_xn, in_elem_bitwidth) = match in_elem.kind() { - ty::Int(i) => ( - args[0].immediate(), - i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()), - ), - ty::Uint(i) => ( - args[0].immediate(), - i.bit_width().unwrap_or_else(|| bx.data_layout().pointer_size.bits()), - ), - _ => return_error!( - "vector argument `{}`'s element type `{}`, expected integer element type", - in_ty, - in_elem - ), - }; - - // Shift the MSB to the right by "in_elem_bitwidth - 1" into the first bit position. - let shift_indices = - vec![ - bx.cx.const_int(bx.type_ix(in_elem_bitwidth), (in_elem_bitwidth - 1) as _); - in_len as _ - ]; - let i_xn_msb = bx.lshr(i_xn, bx.const_vector(shift_indices.as_slice())); - // Truncate vector to an - let i1xn = bx.trunc(i_xn_msb, bx.type_vector(bx.type_i1(), in_len)); - // Bitcast to iN: - let i_ = bx.bitcast(i1xn, bx.type_ix(in_len)); - // Zero-extend iN to the bitmask type: - return Ok(bx.zext(i_, bx.type_ix(expected_int_bits))); - } - - fn simd_simple_float_intrinsic<'a, 'gcc, 'tcx>( - name: Symbol, - in_elem: &::rustc_middle::ty::TyS<'_>, - in_ty: &::rustc_middle::ty::TyS<'_>, - in_len: u64, - bx: &mut Builder<'a, 'gcc, 'tcx>, - span: Span, - args: &[OperandRef<'tcx, RValue<'gcc>>], - ) -> Result, ()> { - macro_rules! emit_error { - ($msg: tt) => { - emit_error!($msg, ) - }; - ($msg: tt, $($fmt: tt)*) => { - span_invalid_monomorphization_error( - bx.sess(), span, - &format!(concat!("invalid monomorphization of `{}` intrinsic: ", $msg), - name, $($fmt)*)); - } - } - macro_rules! return_error { - ($($fmt: tt)*) => { - { - emit_error!($($fmt)*); - return Err(()); - } - } - } - - let (elem_ty_str, elem_ty) = if let ty::Float(f) = in_elem.kind() { - let elem_ty = bx.cx.type_float_from_ty(*f); - match f.bit_width() { - 32 => ("f32", elem_ty), - 64 => ("f64", elem_ty), - _ => { - return_error!( - "unsupported element type `{}` of floating-point vector `{}`", - f.name_str(), - in_ty - ); - } - } - } else { - return_error!("`{}` is not a floating-point type", in_ty); - }; - - let vec_ty = bx.type_vector(elem_ty, in_len); - - let (intr_name, fn_ty) = match name { - sym::simd_ceil => ("ceil", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_fabs => ("fabs", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_fcos => ("cos", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_fexp2 => ("exp2", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_fexp => ("exp", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_flog10 => ("log10", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_flog2 => ("log2", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_flog => ("log", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_floor => ("floor", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_fma => ("fma", bx.type_func(&[vec_ty, vec_ty, vec_ty], vec_ty)), - sym::simd_fpowi => ("powi", bx.type_func(&[vec_ty, bx.type_i32()], vec_ty)), - sym::simd_fpow => ("pow", bx.type_func(&[vec_ty, vec_ty], vec_ty)), - sym::simd_fsin => ("sin", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_fsqrt => ("sqrt", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_round => ("round", bx.type_func(&[vec_ty], vec_ty)), - sym::simd_trunc => ("trunc", bx.type_func(&[vec_ty], vec_ty)), - _ => return_error!("unrecognized intrinsic `{}`", name), - }; - let llvm_name = &format!("llvm.{0}.v{1}{2}", intr_name, in_len, elem_ty_str); - let f = bx.declare_cfn(&llvm_name, fn_ty); - let c = bx.call(f, &args.iter().map(|arg| arg.immediate()).collect::>(), None); - Ok(c) - } - - if std::matches!( - name, - sym::simd_ceil - | sym::simd_fabs - | sym::simd_fcos - | sym::simd_fexp2 - | sym::simd_fexp - | sym::simd_flog10 - | sym::simd_flog2 - | sym::simd_flog - | sym::simd_floor - | sym::simd_fma - | sym::simd_fpow - | sym::simd_fpowi - | sym::simd_fsin - | sym::simd_fsqrt - | sym::simd_round - | sym::simd_trunc - ) { - return simd_simple_float_intrinsic(name, in_elem, in_ty, in_len, bx, span, args); - } - - // FIXME: use: - // https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Function.h#L182 - // https://github.com/llvm-mirror/llvm/blob/master/include/llvm/IR/Intrinsics.h#L81 - fn llvm_vector_str(elem_ty: Ty<'_>, vec_len: u64, no_pointers: usize) -> String { - let p0s: String = "p0".repeat(no_pointers); - match *elem_ty.kind() { - ty::Int(v) => format!("v{}{}i{}", vec_len, p0s, v.bit_width().unwrap()), - ty::Uint(v) => format!("v{}{}i{}", vec_len, p0s, v.bit_width().unwrap()), - ty::Float(v) => format!("v{}{}f{}", vec_len, p0s, v.bit_width()), - _ => unreachable!(), - } - } - - fn gcc_vector_ty<'gcc>( - cx: &CodegenCx<'gcc, '_>, - elem_ty: Ty<'_>, - vec_len: u64, - mut no_pointers: usize, - ) -> Type<'gcc> { - // FIXME: use cx.layout_of(ty).llvm_type() ? - let mut elem_ty = match *elem_ty.kind() { - ty::Int(v) => cx.type_int_from_ty(v), - ty::Uint(v) => cx.type_uint_from_ty(v), - ty::Float(v) => cx.type_float_from_ty(v), - _ => unreachable!(), - }; - while no_pointers > 0 { - elem_ty = cx.type_ptr_to(elem_ty); - no_pointers -= 1; - } - cx.type_vector(elem_ty, vec_len) - } - - if name == sym::simd_gather { - // simd_gather(values: , pointers: , - // mask: ) -> - // * N: number of elements in the input vectors - // * T: type of the element to load - // * M: any integer width is supported, will be truncated to i1 - - // All types must be simd vector types - require_simd!(in_ty, "first"); - require_simd!(arg_tys[1], "second"); - require_simd!(arg_tys[2], "third"); - require_simd!(ret_ty, "return"); - - // Of the same length: - let (out_len, _) = arg_tys[1].simd_size_and_type(bx.tcx()); - let (out_len2, _) = arg_tys[2].simd_size_and_type(bx.tcx()); - require!( - in_len == out_len, - "expected {} argument with length {} (same as input type `{}`), \ - found `{}` with length {}", - "second", - in_len, - in_ty, - arg_tys[1], - out_len - ); - require!( - in_len == out_len2, - "expected {} argument with length {} (same as input type `{}`), \ - found `{}` with length {}", - "third", - in_len, - in_ty, - arg_tys[2], - out_len2 - ); - - // The return type must match the first argument type - require!(ret_ty == in_ty, "expected return type `{}`, found `{}`", in_ty, ret_ty); - - // This counts how many pointers - fn ptr_count(t: Ty<'_>) -> usize { - match t.kind() { - ty::RawPtr(p) => 1 + ptr_count(p.ty), - _ => 0, - } - } - - // Non-ptr type - fn non_ptr(t: Ty<'_>) -> Ty<'_> { - match t.kind() { - ty::RawPtr(p) => non_ptr(p.ty), - _ => t, - } - } - - // The second argument must be a simd vector with an element type that's a pointer - // to the element type of the first argument - let (_, element_ty0) = arg_tys[0].simd_size_and_type(bx.tcx()); - let (_, element_ty1) = arg_tys[1].simd_size_and_type(bx.tcx()); - let (pointer_count, underlying_ty) = match element_ty1.kind() { - ty::RawPtr(p) if p.ty == in_elem => (ptr_count(element_ty1), non_ptr(element_ty1)), - _ => { - require!( - false, - "expected element type `{}` of second argument `{}` \ - to be a pointer to the element type `{}` of the first \ - argument `{}`, found `{}` != `*_ {}`", - element_ty1, - arg_tys[1], - in_elem, - in_ty, - element_ty1, - in_elem - ); - unreachable!(); - } - }; - assert!(pointer_count > 0); - assert_eq!(pointer_count - 1, ptr_count(element_ty0)); - assert_eq!(underlying_ty, non_ptr(element_ty0)); - - // The element type of the third argument must be a signed integer type of any width: - let (_, element_ty2) = arg_tys[2].simd_size_and_type(bx.tcx()); - match element_ty2.kind() { - ty::Int(_) => (), - _ => { - require!( - false, - "expected element type `{}` of third argument `{}` \ - to be a signed integer type", - element_ty2, - arg_tys[2] - ); - } - } - - // Alignment of T, must be a constant integer value: - let alignment_ty = bx.type_i32(); - let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32); - - // Truncate the mask vector to a vector of i1s: - let (mask, mask_ty) = { - let i1 = bx.type_i1(); - let i1xn = bx.type_vector(i1, in_len); - (bx.trunc(args[2].immediate(), i1xn), i1xn) - }; - - // Type of the vector of pointers: - let llvm_pointer_vec_ty = gcc_vector_ty(bx, underlying_ty, in_len, pointer_count); - let llvm_pointer_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count); - - // Type of the vector of elements: - let llvm_elem_vec_ty = gcc_vector_ty(bx, underlying_ty, in_len, pointer_count - 1); - let llvm_elem_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count - 1); - - let llvm_intrinsic = - format!("llvm.masked.gather.{}.{}", llvm_elem_vec_str, llvm_pointer_vec_str); - let f = bx.declare_cfn( - &llvm_intrinsic, - bx.type_func( - &[llvm_pointer_vec_ty, alignment_ty, mask_ty, llvm_elem_vec_ty], - llvm_elem_vec_ty, - ), - ); - let v = bx.call(f, &[args[1].immediate(), alignment, mask, args[0].immediate()], None); - return Ok(v); - } - - if name == sym::simd_scatter { - // simd_scatter(values: , pointers: , - // mask: ) -> () - // * N: number of elements in the input vectors - // * T: type of the element to load - // * M: any integer width is supported, will be truncated to i1 - - // All types must be simd vector types - require_simd!(in_ty, "first"); - require_simd!(arg_tys[1], "second"); - require_simd!(arg_tys[2], "third"); - - // Of the same length: - let (element_len1, _) = arg_tys[1].simd_size_and_type(bx.tcx()); - let (element_len2, _) = arg_tys[2].simd_size_and_type(bx.tcx()); - require!( - in_len == element_len1, - "expected {} argument with length {} (same as input type `{}`), \ - found `{}` with length {}", - "second", - in_len, - in_ty, - arg_tys[1], - element_len1 - ); - require!( - in_len == element_len2, - "expected {} argument with length {} (same as input type `{}`), \ - found `{}` with length {}", - "third", - in_len, - in_ty, - arg_tys[2], - element_len2 - ); - - // This counts how many pointers - fn ptr_count(t: Ty<'_>) -> usize { - match t.kind() { - ty::RawPtr(p) => 1 + ptr_count(p.ty), - _ => 0, - } - } - - // Non-ptr type - fn non_ptr(t: Ty<'_>) -> Ty<'_> { - match t.kind() { - ty::RawPtr(p) => non_ptr(p.ty), - _ => t, - } - } - - // The second argument must be a simd vector with an element type that's a pointer - // to the element type of the first argument - let (_, element_ty0) = arg_tys[0].simd_size_and_type(bx.tcx()); - let (_, element_ty1) = arg_tys[1].simd_size_and_type(bx.tcx()); - let (_, element_ty2) = arg_tys[2].simd_size_and_type(bx.tcx()); - let (pointer_count, underlying_ty) = match element_ty1.kind() { - ty::RawPtr(p) if p.ty == in_elem && p.mutbl == hir::Mutability::Mut => { - (ptr_count(element_ty1), non_ptr(element_ty1)) - } - _ => { - require!( - false, - "expected element type `{}` of second argument `{}` \ - to be a pointer to the element type `{}` of the first \ - argument `{}`, found `{}` != `*mut {}`", - element_ty1, - arg_tys[1], - in_elem, - in_ty, - element_ty1, - in_elem - ); - unreachable!(); - } - }; - assert!(pointer_count > 0); - assert_eq!(pointer_count - 1, ptr_count(element_ty0)); - assert_eq!(underlying_ty, non_ptr(element_ty0)); - - // The element type of the third argument must be a signed integer type of any width: - match element_ty2.kind() { - ty::Int(_) => (), - _ => { - require!( - false, - "expected element type `{}` of third argument `{}` \ - be a signed integer type", - element_ty2, - arg_tys[2] - ); - } - } - - // Alignment of T, must be a constant integer value: - let alignment_ty = bx.type_i32(); - let alignment = bx.const_i32(bx.align_of(in_elem).bytes() as i32); - - // Truncate the mask vector to a vector of i1s: - let (mask, mask_ty) = { - let i1 = bx.type_i1(); - let i1xn = bx.type_vector(i1, in_len); - (bx.trunc(args[2].immediate(), i1xn), i1xn) - }; - - let ret_t = bx.type_void(); - - // Type of the vector of pointers: - let llvm_pointer_vec_ty = gcc_vector_ty(bx, underlying_ty, in_len, pointer_count); - let llvm_pointer_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count); - - // Type of the vector of elements: - let llvm_elem_vec_ty = gcc_vector_ty(bx, underlying_ty, in_len, pointer_count - 1); - let llvm_elem_vec_str = llvm_vector_str(underlying_ty, in_len, pointer_count - 1); - - let llvm_intrinsic = - format!("llvm.masked.scatter.{}.{}", llvm_elem_vec_str, llvm_pointer_vec_str); - let f = bx.declare_cfn( - &llvm_intrinsic, - bx.type_func(&[llvm_elem_vec_ty, llvm_pointer_vec_ty, alignment_ty, mask_ty], ret_t), - ); - let v = bx.call(f, &[args[0].immediate(), args[1].immediate(), alignment, mask], None); - return Ok(v); - } - - macro_rules! arith_red { - ($name:ident : $integer_reduce:ident, $float_reduce:ident, $ordered:expr, $op:ident, - $identity:expr) => { - if name == sym::$name { - require!( - ret_ty == in_elem, - "expected return type `{}` (element of input `{}`), found `{}`", - in_elem, - in_ty, - ret_ty - ); - return match in_elem.kind() { - ty::Int(_) | ty::Uint(_) => { - let r = bx.$integer_reduce(args[0].immediate()); - if $ordered { - // if overflow occurs, the result is the - // mathematical result modulo 2^n: - Ok(bx.$op(args[1].immediate(), r)) - } else { - Ok(bx.$integer_reduce(args[0].immediate())) - } - } - ty::Float(f) => { - let acc = if $ordered { - // ordered arithmetic reductions take an accumulator - args[1].immediate() - } else { - // unordered arithmetic reductions use the identity accumulator - match f.bit_width() { - 32 => bx.const_real(bx.type_f32(), $identity), - 64 => bx.const_real(bx.type_f64(), $identity), - v => return_error!( - r#" -unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, - sym::$name, - in_ty, - in_elem, - v, - ret_ty - ), - } - }; - Ok(bx.$float_reduce(acc, args[0].immediate())) - } - _ => return_error!( - "unsupported {} from `{}` with element `{}` to `{}`", - sym::$name, - in_ty, - in_elem, - ret_ty - ), - }; - } - }; - } - - arith_red!(simd_reduce_add_ordered: vector_reduce_add, vector_reduce_fadd, true, add, 0.0); - arith_red!(simd_reduce_mul_ordered: vector_reduce_mul, vector_reduce_fmul, true, mul, 1.0); - arith_red!( - simd_reduce_add_unordered: vector_reduce_add, - vector_reduce_fadd_fast, - false, - add, - 0.0 - ); - arith_red!( - simd_reduce_mul_unordered: vector_reduce_mul, - vector_reduce_fmul_fast, - false, - mul, - 1.0 - ); - - macro_rules! minmax_red { - ($name:ident: $int_red:ident, $float_red:ident) => { - if name == sym::$name { - require!( - ret_ty == in_elem, - "expected return type `{}` (element of input `{}`), found `{}`", - in_elem, - in_ty, - ret_ty - ); - return match in_elem.kind() { - ty::Int(_i) => Ok(bx.$int_red(args[0].immediate(), true)), - ty::Uint(_u) => Ok(bx.$int_red(args[0].immediate(), false)), - ty::Float(_f) => Ok(bx.$float_red(args[0].immediate())), - _ => return_error!( - "unsupported {} from `{}` with element `{}` to `{}`", - sym::$name, - in_ty, - in_elem, - ret_ty - ), - }; - } - }; - } - - minmax_red!(simd_reduce_min: vector_reduce_min, vector_reduce_fmin); - minmax_red!(simd_reduce_max: vector_reduce_max, vector_reduce_fmax); - - minmax_red!(simd_reduce_min_nanless: vector_reduce_min, vector_reduce_fmin_fast); - minmax_red!(simd_reduce_max_nanless: vector_reduce_max, vector_reduce_fmax_fast); - - macro_rules! bitwise_red { - ($name:ident : $red:ident, $boolean:expr) => { - if name == sym::$name { - let input = if !$boolean { - require!( - ret_ty == in_elem, - "expected return type `{}` (element of input `{}`), found `{}`", - in_elem, - in_ty, - ret_ty - ); - args[0].immediate() - } else { - match in_elem.kind() { - ty::Int(_) | ty::Uint(_) => {} - _ => return_error!( - "unsupported {} from `{}` with element `{}` to `{}`", - sym::$name, - in_ty, - in_elem, - ret_ty - ), - } - - // boolean reductions operate on vectors of i1s: - let i1 = bx.type_i1(); - let i1xn = bx.type_vector(i1, in_len as u64); - bx.trunc(args[0].immediate(), i1xn) - }; - return match in_elem.kind() { - ty::Int(_) | ty::Uint(_) => { - let r = bx.$red(input); - Ok(if !$boolean { r } else { bx.zext(r, bx.type_bool()) }) - } - _ => return_error!( - "unsupported {} from `{}` with element `{}` to `{}`", - sym::$name, - in_ty, - in_elem, - ret_ty - ), - }; - } - }; - } - - bitwise_red!(simd_reduce_and: vector_reduce_and, false); - bitwise_red!(simd_reduce_or: vector_reduce_or, false); - bitwise_red!(simd_reduce_xor: vector_reduce_xor, false); - bitwise_red!(simd_reduce_all: vector_reduce_and, true); - bitwise_red!(simd_reduce_any: vector_reduce_or, true); - - if name == sym::simd_cast { - require_simd!(ret_ty, "return"); - let (out_len, out_elem) = ret_ty.simd_size_and_type(bx.tcx()); - require!( - in_len == out_len, - "expected return type with length {} (same as input type `{}`), \ - found `{}` with length {}", - in_len, - in_ty, - ret_ty, - out_len - ); - // casting cares about nominal type, not just structural type - if in_elem == out_elem { - return Ok(args[0].immediate()); - } - - enum Style { - Float, - Int(/* is signed? */ bool), - Unsupported, - } - - let (in_style, in_width) = match in_elem.kind() { - // vectors of pointer-sized integers should've been - // disallowed before here, so this unwrap is safe. - ty::Int(i) => (Style::Int(true), i.bit_width().unwrap()), - ty::Uint(u) => (Style::Int(false), u.bit_width().unwrap()), - ty::Float(f) => (Style::Float, f.bit_width()), - _ => (Style::Unsupported, 0), - }; - let (out_style, out_width) = match out_elem.kind() { - ty::Int(i) => (Style::Int(true), i.bit_width().unwrap()), - ty::Uint(u) => (Style::Int(false), u.bit_width().unwrap()), - ty::Float(f) => (Style::Float, f.bit_width()), - _ => (Style::Unsupported, 0), - }; - - match (in_style, out_style) { - (Style::Int(in_is_signed), Style::Int(_)) => { - return Ok(match in_width.cmp(&out_width) { - Ordering::Greater => bx.trunc(args[0].immediate(), llret_ty), - Ordering::Equal => args[0].immediate(), - Ordering::Less => { - if in_is_signed { - bx.sext(args[0].immediate(), llret_ty) - } else { - bx.zext(args[0].immediate(), llret_ty) - } - } - }); - } - (Style::Int(in_is_signed), Style::Float) => { - return Ok(if in_is_signed { - bx.sitofp(args[0].immediate(), llret_ty) - } else { - bx.uitofp(args[0].immediate(), llret_ty) - }); - } - (Style::Float, Style::Int(out_is_signed)) => { - return Ok(if out_is_signed { - bx.fptosi(args[0].immediate(), llret_ty) - } else { - bx.fptoui(args[0].immediate(), llret_ty) - }); - } - (Style::Float, Style::Float) => { - return Ok(match in_width.cmp(&out_width) { - Ordering::Greater => bx.fptrunc(args[0].immediate(), llret_ty), - Ordering::Equal => args[0].immediate(), - Ordering::Less => bx.fpext(args[0].immediate(), llret_ty), - }); - } - _ => { /* Unsupported. Fallthrough. */ } - } - require!( - false, - "unsupported cast from `{}` with element `{}` to `{}` with element `{}`", - in_ty, - in_elem, - ret_ty, - out_elem - ); - }*/ - macro_rules! arith_binary { ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => { $(if name == sym::$name { @@ -934,68 +159,9 @@ unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, simd_shl: Uint, Int => shl; simd_shr: Uint => lshr, Int => ashr; simd_and: Uint, Int => and; - simd_or: Uint, Int => or; // FIXME: calling or might not work on vectors. + simd_or: Uint, Int => or; // FIXME(antoyo): calling `or` might not work on vectors. simd_xor: Uint, Int => xor; - /*simd_fmax: Float => maxnum; - simd_fmin: Float => minnum;*/ } - /*macro_rules! arith_unary { - ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => { - $(if name == sym::$name { - match in_elem.kind() { - $($(ty::$p(_))|* => { - return Ok(bx.$call(args[0].immediate())) - })* - _ => {}, - } - require!(false, - "unsupported operation on `{}` with element `{}`", - in_ty, - in_elem) - })* - } - } - - arith_unary! { - simd_neg: Int => neg, Float => fneg; - } - - if name == sym::simd_saturating_add || name == sym::simd_saturating_sub { - let lhs = args[0].immediate(); - let rhs = args[1].immediate(); - let is_add = name == sym::simd_saturating_add; - let ptr_bits = bx.tcx().data_layout.pointer_size.bits() as _; - let (signed, elem_width, elem_ty) = match *in_elem.kind() { - ty::Int(i) => (true, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_int_from_ty(i)), - ty::Uint(i) => (false, i.bit_width().unwrap_or(ptr_bits), bx.cx.type_uint_from_ty(i)), - _ => { - return_error!( - "expected element type `{}` of vector type `{}` \ - to be a signed or unsigned integer type", - arg_tys[0].simd_size_and_type(bx.tcx()).1, - arg_tys[0] - ); - } - }; - let llvm_intrinsic = &format!( - "llvm.{}{}.sat.v{}i{}", - if signed { 's' } else { 'u' }, - if is_add { "add" } else { "sub" }, - in_len, - elem_width - ); - let vec_ty = bx.cx.type_vector(elem_ty, in_len as u64); - - let f = bx.declare_cfn( - &llvm_intrinsic, - bx.type_func(&[vec_ty, vec_ty], vec_ty), - ); - let v = bx.call(f, &[lhs, rhs], None); - return Ok(v); - }*/ - unimplemented!("simd {}", name); - - //span_bug!(span, "unknown SIMD intrinsic"); } diff --git a/src/lib.rs b/src/lib.rs index 797251814d7f4..6febccff1ffac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,8 @@ /* - * TODO: support #[inline] attributes. - * TODO: support LTO. + * TODO(antoyo): support #[inline] attributes. + * TODO(antoyo): support LTO. * - * TODO: remove the local gccjit LD_LIBRARY_PATH in config.sh. - * TODO: remove the object dependency. - * TODO: remove the patches. + * TODO(antoyo): remove the patches. */ #![feature(rustc_private, decl_macro, associated_type_bounds, never_type, trusted_len)] @@ -13,13 +11,10 @@ #![warn(rust_2018_idioms)] #![warn(unused_lifetimes)] -/*extern crate flate2; -extern crate libc;*/ extern crate rustc_ast; extern crate rustc_codegen_ssa; extern crate rustc_data_structures; extern crate rustc_errors; -//extern crate rustc_fs_util; extern crate rustc_hir; extern crate rustc_metadata; extern crate rustc_middle; @@ -53,7 +48,6 @@ mod mangled_std_symbols; mod mono_item; mod type_; mod type_of; -mod va_arg; use std::any::Any; use std::sync::Arc; @@ -119,7 +113,7 @@ impl CodegenBackend for GccCodegenBackend { fn link(&self, sess: &Session, mut codegen_results: CodegenResults, outputs: &OutputFilenames) -> Result<(), ErrorReported> { use rustc_codegen_ssa::back::link::link_binary; if let Some(symbols) = codegen_results.crate_info.exported_symbols.get_mut(&CrateType::Dylib) { - // TODO: remove when global initializer work without calling a function at runtime. + // TODO:(antoyo): remove when global initializer work without calling a function at runtime. // HACK: since this codegen add some symbols (e.g. __gccGlobalCrateInit) and the UI // tests load libstd.so as a dynamic library, and rustc use a version-script to specify // the symbols visibility, we add * to export all symbols. @@ -159,7 +153,7 @@ impl ExtraBackendMethods for GccCodegenBackend { } fn target_machine_factory(&self, _sess: &Session, _opt_level: OptLevel) -> TargetMachineFactoryFn { - // TODO: set opt level. + // TODO(antoyo): set opt level. Arc::new(|_| { Ok(()) }) @@ -171,8 +165,7 @@ impl ExtraBackendMethods for GccCodegenBackend { fn tune_cpu<'b>(&self, _sess: &'b Session) -> Option<&'b str> { None - // TODO - //llvm_util::tune_cpu(sess) + // TODO(antoyo) } } @@ -197,7 +190,7 @@ pub struct GccContext { } unsafe impl Send for GccContext {} -// FIXME: that shouldn't be Sync. Parallel compilation is currently disabled with "-Zno-parallel-llvm". Try to disable it here. +// FIXME(antoyo): that shouldn't be Sync. Parallel compilation is currently disabled with "-Zno-parallel-llvm". Try to disable it here. unsafe impl Sync for GccContext {} impl WriteBackendMethods for GccCodegenBackend { @@ -209,16 +202,13 @@ impl WriteBackendMethods for GccCodegenBackend { type ThinBuffer = ThinBuffer; fn run_fat_lto(_cgcx: &CodegenContext, mut modules: Vec>, _cached_modules: Vec<(SerializedModule, WorkProduct)>) -> Result, FatalError> { - // TODO: implement LTO by sending -flto to libgccjit and adding the appropriate gcc linker plugins. + // TODO(antoyo): implement LTO by sending -flto to libgccjit and adding the appropriate gcc linker plugins. // NOTE: implemented elsewhere. let module = match modules.remove(0) { FatLTOInput::InMemory(module) => module, FatLTOInput::Serialized { .. } => { unimplemented!(); - /*info!("pushing serialized module {:?}", name); - let buffer = SerializedModule::Local(buffer); - serialized_modules.push((buffer, CString::new(name).unwrap()));*/ } }; Ok(LtoModuleCodegen::Fat { module: Some(module), _serialized_bitcode: vec![] }) @@ -233,9 +223,6 @@ impl WriteBackendMethods for GccCodegenBackend { } unsafe fn optimize(_cgcx: &CodegenContext, _diag_handler: &Handler, module: &ModuleCodegen, config: &ModuleConfig) -> Result<(), FatalError> { - //if cgcx.lto == Lto::Fat { - //module.module_llvm.context.add_driver_option("-flto"); - //} module.module_llvm.context.set_optimization_level(to_gcc_opt_level(config.opt_level)); Ok(()) } @@ -257,7 +244,7 @@ impl WriteBackendMethods for GccCodegenBackend { } fn run_lto_pass_manager(_cgcx: &CodegenContext, _module: &ModuleCodegen, _config: &ModuleConfig, _thin: bool) -> Result<(), FatalError> { - // TODO + // TODO(antoyo) Ok(()) } @@ -266,10 +253,6 @@ impl WriteBackendMethods for GccCodegenBackend { } } -/*fn target_triple(sess: &Session) -> target_lexicon::Triple { - sess.target.llvm_target.parse().unwrap() -}*/ - /// This is the entrypoint for a hot plugged rustc_codegen_gccjit #[no_mangle] pub fn __rustc_codegen_backend() -> Box { @@ -306,11 +289,6 @@ fn handle_native(name: &str) -> &str { } unimplemented!(); - /*unsafe { - let mut len = 0; - let ptr = llvm::LLVMRustGetHostCPUName(&mut len); - str::from_utf8(slice::from_raw_parts(ptr as *const u8, len)).unwrap() - }*/ } pub fn target_cpu(sess: &Session) -> &str { @@ -327,14 +305,7 @@ pub fn target_features(sess: &Session) -> Vec { }, ) .filter(|_feature| { - /*if feature.starts_with("sse") { - return true; - }*/ - // TODO: implement a way to get enabled feature in libgccjit. - //println!("Feature: {}", feature); - /*let llvm_feature = to_llvm_feature(sess, feature); - let cstr = CString::new(llvm_feature).unwrap(); - unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) }*/ + // TODO(antoyo): implement a way to get enabled feature in libgccjit. false }) .map(|feature| Symbol::intern(feature)) diff --git a/src/mono_item.rs b/src/mono_item.rs index c261efbbc559f..f9ec933dd3abc 100644 --- a/src/mono_item.rs +++ b/src/mono_item.rs @@ -26,12 +26,7 @@ impl<'gcc, 'tcx> PreDefineMethods<'tcx> for CodegenCx<'gcc, 'tcx> { ) }); - // TODO - /*unsafe { - llvm::LLVMRustSetLinkage(global, base::linkage_to_llvm(linkage)); - llvm::LLVMRustSetVisibility(global, base::visibility_to_llvm(visibility)); - }*/ - + // TODO(antoyo): set linkage and visibility. self.instances.borrow_mut().insert(instance, global); } @@ -43,17 +38,8 @@ impl<'gcc, 'tcx> PreDefineMethods<'tcx> for CodegenCx<'gcc, 'tcx> { let _decl = self.declare_fn(symbol_name, &fn_abi); //let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); - // TODO: call set_link_section() to allow initializing argc/argv. - //base::set_link_section(decl, &attrs); - /*if linkage == Linkage::LinkOnceODR || linkage == Linkage::WeakODR { - llvm::SetUniqueComdat(self.llmod, decl); - }*/ - - //debug!("predefine_fn: instance = {:?}", instance); - - // TODO: use inline attribute from there in linkage.set() above: - //attributes::from_fn_attrs(self, decl, instance); - - //self.instances.borrow_mut().insert(instance, decl); + // TODO(antoyo): call set_link_section() to allow initializing argc/argv. + // TODO(antoyo): set unique comdat. + // TODO(antoyo): use inline attribute from there in linkage.set() above. } } diff --git a/src/type_.rs b/src/type_.rs index 2ef90bca5ac20..3545e1b628105 100644 --- a/src/type_.rs +++ b/src/type_.rs @@ -14,6 +14,9 @@ use crate::type_of::LayoutGccExt; impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { pub fn type_ix(&self, num_bits: u64) -> Type<'gcc> { // gcc only supports 1, 2, 4 or 8-byte integers. + // FIXME(antoyo): this is misleading to use the next power of two as rustc_codegen_ssa + // sometimes use 96-bit numbers and the following code will give an integer of a different + // size. let bytes = (num_bits / 8).next_power_of_two() as i32; match bytes { 1 => self.i8_type, @@ -23,17 +26,8 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { 16 => self.i128_type, _ => panic!("unexpected num_bits: {}", num_bits), } - /* - let bytes = (num_bits / 8).next_power_of_two() as i32; - println!("num_bits: {}, bytes: {}", num_bits, bytes); - self.context.new_int_type(bytes, true) // TODO: check if it is indeed a signed integer. - */ } - /*pub fn type_bool(&self) -> Type<'gcc> { - self.bool_type - }*/ - pub fn type_void(&self) -> Type<'gcc> { self.context.new_type::<()>() } @@ -67,39 +61,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { let ity = Integer::approximate_align(self, align); self.type_from_integer(ity) } - - /*pub fn type_int_from_ty(&self, t: ty::IntTy) -> Type<'gcc> { - match t { - ty::IntTy::Isize => self.type_isize(), - ty::IntTy::I8 => self.type_i8(), - ty::IntTy::I16 => self.type_i16(), - ty::IntTy::I32 => self.type_i32(), - ty::IntTy::I64 => self.type_i64(), - ty::IntTy::I128 => self.type_i128(), - } - } - - pub fn type_uint_from_ty(&self, t: ty::UintTy) -> Type<'gcc> { - match t { - ty::UintTy::Usize => self.type_isize(), - ty::UintTy::U8 => self.type_i8(), - ty::UintTy::U16 => self.type_i16(), - ty::UintTy::U32 => self.type_i32(), - ty::UintTy::U64 => self.type_i64(), - ty::UintTy::U128 => self.type_i128(), - } - } - - pub fn type_float_from_ty(&self, t: ty::FloatTy) -> Type<'gcc> { - match t { - ty::FloatTy::F32 => self.type_f32(), - ty::FloatTy::F64 => self.type_f64(), - } - } - - pub fn type_vector(&self, ty: Type<'gcc>, len: u64) -> Type<'gcc> { - self.context.new_vector_type(ty, len) - }*/ } impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> { @@ -151,9 +112,7 @@ impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> { let fields: Vec<_> = fields.iter().enumerate() .map(|(index, field)| self.context.new_field(None, *field, &format!("field{}_TODO", index))) .collect(); - // TODO: use packed. - //let name = types.iter().map(|typ| format!("{:?}", typ)).collect::>().join("_"); - //let typ = self.context.new_struct_type(None, format!("struct{}", name), &fields).as_type(); + // TODO(antoyo): use packed. let typ = self.context.new_struct_type(None, "struct", &fields).as_type(); self.struct_types.borrow_mut().insert(types, typ); typ @@ -167,21 +126,17 @@ impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> { TypeKind::Vector } else { - // TODO + // TODO(antoyo): support other types. TypeKind::Void } } fn type_ptr_to(&self, ty: Type<'gcc>) -> Type<'gcc> { - // TODO - /*assert_ne!(self.type_kind(ty), TypeKind::Function, - "don't call ptr_to on function types, use ptr_to_gcc_type on FnAbi instead" - );*/ ty.make_pointer() } fn type_ptr_to_ext(&self, ty: Type<'gcc>, _address_space: AddressSpace) -> Type<'gcc> { - // TODO: use address_space + // TODO(antoyo): use address_space ty.make_pointer() } @@ -202,7 +157,6 @@ impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn vector_length(&self, _ty: Type<'gcc>) -> usize { unimplemented!(); - //unsafe { llvm::LLVMGetVectorSize(ty) as usize } } fn float_width(&self, typ: Type<'gcc>) -> usize { @@ -217,14 +171,7 @@ impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> { else { panic!("Cannot get width of float type {:?}", typ); } - // TODO: support other sizes. - /*match self.type_kind(ty) { - TypeKind::Float => 32, - TypeKind::Double => 64, - TypeKind::X86_FP80 => 80, - TypeKind::FP128 | TypeKind::PPC_FP128 => 128, - _ => bug!("llvm_float_width called on a non-float type"), - }*/ + // TODO(antoyo): support other sizes. } fn int_width(&self, typ: Type<'gcc>) -> u64 { @@ -263,21 +210,13 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } pub fn set_struct_body(&self, typ: Struct<'gcc>, fields: &[Type<'gcc>], _packed: bool) { - // TODO: use packed. + // TODO(antoyo): use packed. let fields: Vec<_> = fields.iter().enumerate() .map(|(index, field)| self.context.new_field(None, *field, &format!("field_{}", index))) .collect(); typ.set_fields(None, &fields); } - /*fn type_struct(&self, fields: &[Type<'gcc>], packed: bool) -> Type<'gcc> { - // TODO: use packed. - let fields: Vec<_> = fields.iter().enumerate() - .map(|(index, field)| self.context.new_field(None, *field, &format!("field_{}", index))) - .collect(); - return self.context.new_struct_type(None, "unnamedStruct", &fields).as_type(); - }*/ - pub fn type_named_struct(&self, name: &str) -> Struct<'gcc> { self.context.new_opaque_struct_type(None, name) } @@ -288,7 +227,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { // NOTE: since gccjit only supports i32 for the array size and libcore's tests uses a // size of usize::MAX in test_binary_search, we workaround this by setting the size to // zero for ZSTs. - // FIXME: fix gccjit API. + // FIXME(antoyo): fix gccjit API. len = 0; } } @@ -305,7 +244,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { } pub fn struct_fields<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout<'tcx>) -> (Vec>, bool) { - //debug!("struct_fields: {:#?}", layout); let field_count = layout.fields.count(); let mut packed = false; @@ -319,23 +257,13 @@ pub fn struct_fields<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout layout.align.abi.min(field.align.abi).restrict_for_offset(target_offset); packed |= effective_field_align < field.align.abi; - /*debug!( - "struct_fields: {}: {:?} offset: {:?} target_offset: {:?} \ - effective_field_align: {}", - i, - field, - offset, - target_offset, - effective_field_align.bytes() - );*/ assert!(target_offset >= offset); let padding = target_offset - offset; let padding_align = prev_effective_align.min(effective_field_align); assert_eq!(offset.align_to(padding_align) + padding, target_offset); result.push(cx.type_padding_filler(padding, padding_align)); - //debug!(" padding before: {:?}", padding); - result.push(field.gcc_type(cx, !field.ty.is_any_ptr())); // FIXME: might need to check if the type is inside another, like Box. + result.push(field.gcc_type(cx, !field.ty.is_any_ptr())); // FIXME(antoyo): might need to check if the type is inside another, like Box. offset = target_offset + field.size; prev_effective_align = effective_field_align; } @@ -346,14 +274,8 @@ pub fn struct_fields<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout let padding = layout.size - offset; let padding_align = prev_effective_align; assert_eq!(offset.align_to(padding_align) + padding, layout.size); - /*debug!( - "struct_fields: pad_bytes: {:?} offset: {:?} stride: {:?}", - padding, offset, layout.size - );*/ result.push(cx.type_padding_filler(padding, padding_align)); assert_eq!(result.len(), 1 + field_count * 2); - } else { - //debug!("struct_fields: offset: {:?} stride: {:?}", offset, layout.size); } (result, packed) diff --git a/src/type_of.rs b/src/type_of.rs index 010805808d2a3..9302f57b642af 100644 --- a/src/type_of.rs +++ b/src/type_of.rs @@ -71,7 +71,7 @@ pub fn uncached_gcc_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLa // If `Some` is returned then a named struct is created in LLVM. Name collisions are // avoided by LLVM (with increasing suffixes). If rustc doesn't generate names then that // can improve perf. - // FIXME: I don't think that's true for libgccjit. + // FIXME(antoyo): I don't think that's true for libgccjit. Some(String::new()) } _ => None, @@ -144,6 +144,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { /// with the inner-most trailing unsized field using the "minimal unit" /// of that field's type - this is useful for taking the address of /// that field and ensuring the struct has the right alignment. + //TODO(antoyo): do we still need the set_fields parameter? fn gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, set_fields: bool) -> Type<'gcc> { if let Abi::Scalar(ref scalar) = self.abi { // Use a different cache for scalars because pointers to DSTs @@ -184,8 +185,6 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { return ty; } - //debug!("gcc_type({:#?})", self); - assert!(!self.ty.has_escaping_bound_vars(), "{:?} has escaping bound vars", self.ty); // Make sure lifetimes are erased, to avoid generating distinct LLVM @@ -204,22 +203,12 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { else { uncached_gcc_type(cx, *self, &mut defer) }; - //debug!("--> mapped {:#?} to ty={:?}", self, ty); cx.types.borrow_mut().insert((self.ty, variant_index), ty); if let Some((ty, layout)) = defer { - //TODO: do we still need this conditions and the set_fields parameter? - //if set_fields { - let (fields, packed) = struct_fields(cx, layout); - cx.set_struct_body(ty, &fields, packed); - /*} - else { - // Since we might be trying to generate a type containing another type which is not - // completely generated yet, we don't set the fields right now, but we save the - // type to set the fields later. - cx.types_with_fields_to_set.borrow_mut().insert(ty.as_type(), (ty, layout)); - }*/ + let (fields, packed) = struct_fields(cx, layout); + cx.set_struct_body(ty, &fields, packed); } ty @@ -255,7 +244,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { } fn scalar_pair_element_gcc_type<'gcc>(&self, cx: &CodegenCx<'gcc, 'tcx>, index: usize, immediate: bool) -> Type<'gcc> { - // TODO: remove llvm hack: + // TODO(antoyo): remove llvm hack: // HACK(eddyb) special-case fat pointers until LLVM removes // pointee types, to avoid bitcasting every `OperandRef::deref`. match self.ty.kind() { @@ -281,8 +270,8 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { // immediate, just like `bool` is typically `i8` in memory and only `i1` // when immediate. We need to load/store `bool` as `i8` to avoid // crippling LLVM optimizations or triggering other LLVM bugs with `i1`. - // TODO: this bugs certainly don't happen in this case since the bool type is used instead of i1. - if /*immediate &&*/ scalar.is_bool() { + // TODO(antoyo): this bugs certainly don't happen in this case since the bool type is used instead of i1. + if scalar.is_bool() { return cx.type_i1(); } @@ -361,12 +350,10 @@ impl<'gcc, 'tcx> LayoutTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> { fn reg_backend_type(&self, _ty: &Reg) -> Type<'gcc> { unimplemented!(); - //ty.gcc_type(self) } fn fn_decl_backend_type(&self, _fn_abi: &FnAbi<'tcx, Ty<'tcx>>) -> Type<'gcc> { - // FIXME: return correct type. + // FIXME(antoyo): return correct type. self.type_void() - //fn_abi.gcc_type(self) } } diff --git a/src/va_arg.rs b/src/va_arg.rs deleted file mode 100644 index 404a169d39ac9..0000000000000 --- a/src/va_arg.rs +++ /dev/null @@ -1,179 +0,0 @@ -/*use gccjit::{RValue, ToRValue, Type}; -use rustc_codegen_ssa::mir::operand::OperandRef; -use rustc_codegen_ssa::{ - common::IntPredicate, - traits::{BaseTypeMethods, BuilderMethods, ConstMethods, DerivedTypeMethods}, -}; -use rustc_middle::ty::layout::HasTyCtxt; -use rustc_middle::ty::Ty; -use rustc_target::abi::{Align, Endian, HasDataLayout, LayoutOf, Size}; - -use crate::builder::Builder; -use crate::type_of::LayoutGccExt; - -fn round_pointer_up_to_alignment<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, addr: RValue<'gcc>, align: Align, ptr_ty: Type<'gcc>) -> RValue<'gcc> { - let mut ptr_as_int = bx.ptrtoint(addr, bx.cx().type_isize()); - ptr_as_int = bx.add(ptr_as_int, bx.cx().const_i32(align.bytes() as i32 - 1)); - ptr_as_int = bx.and(ptr_as_int, bx.cx().const_i32(-(align.bytes() as i32))); - bx.inttoptr(ptr_as_int, ptr_ty) -} - -fn emit_direct_ptr_va_arg<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, list: OperandRef<'tcx, RValue<'gcc>>, llty: Type<'gcc>, size: Size, align: Align, slot_size: Align, allow_higher_align: bool) -> (RValue<'gcc>, Align) { - let va_list_ptr_ty = bx.cx().type_ptr_to(bx.cx.type_i8p()); - let va_list_addr = - if list.layout.gcc_type(bx.cx, true) != va_list_ptr_ty { - bx.bitcast(list.immediate(), va_list_ptr_ty) - } - else { - list.immediate() - }; - - let ptr = bx.load(va_list_addr, bx.tcx().data_layout.pointer_align.abi); - - let (addr, addr_align) = if allow_higher_align && align > slot_size { - (round_pointer_up_to_alignment(bx, ptr, align, bx.cx().type_i8p()), align) - } else { - (ptr, slot_size) - }; - - let aligned_size = size.align_to(slot_size).bytes() as i32; - let full_direct_size = bx.cx().const_i32(aligned_size); - let next = bx.inbounds_gep(addr, &[full_direct_size]); - bx.store(next, va_list_addr, bx.tcx().data_layout.pointer_align.abi); - - if size.bytes() < slot_size.bytes() && bx.tcx().sess.target.endian == Endian::Big { - let adjusted_size = bx.cx().const_i32((slot_size.bytes() - size.bytes()) as i32); - let adjusted = bx.inbounds_gep(addr, &[adjusted_size]); - (bx.bitcast(adjusted, bx.cx().type_ptr_to(llty)), addr_align) - } else { - (bx.bitcast(addr, bx.cx().type_ptr_to(llty)), addr_align) - } -} - -fn emit_ptr_va_arg<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, list: OperandRef<'tcx, RValue<'gcc>>, target_ty: Ty<'tcx>, indirect: bool, slot_size: Align, allow_higher_align: bool) -> RValue<'gcc> { - let layout = bx.cx.layout_of(target_ty); - let (llty, size, align) = - if indirect { - ( - bx.cx.layout_of(bx.cx.tcx.mk_imm_ptr(target_ty)).gcc_type(bx.cx, true), - bx.cx.data_layout().pointer_size, - bx.cx.data_layout().pointer_align, - ) - } - else { - (layout.gcc_type(bx.cx, true), layout.size, layout.align) - }; - let (addr, addr_align) = emit_direct_ptr_va_arg(bx, list, llty, size, align.abi, slot_size, allow_higher_align); - if indirect { - let tmp_ret = bx.load(addr, addr_align); - bx.load(tmp_ret, align.abi) - } - else { - bx.load(addr, addr_align) - } -} - -fn emit_aapcs_va_arg<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, list: OperandRef<'tcx, RValue<'gcc>>, target_ty: Ty<'tcx>) -> RValue<'gcc> { - // Implementation of the AAPCS64 calling convention for va_args see - // https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst - let va_list_addr = list.immediate(); - let layout = bx.cx.layout_of(target_ty); - let gcc_type = layout.immediate_gcc_type(bx); - - let function = bx.llbb().get_function(); - let variable = function.new_local(None, gcc_type, "va_arg"); - - let mut maybe_reg = bx.build_sibling_block("va_arg.maybe_reg"); - let mut in_reg = bx.build_sibling_block("va_arg.in_reg"); - let mut on_stack = bx.build_sibling_block("va_arg.on_stack"); - let end = bx.build_sibling_block("va_arg.end"); - let zero = bx.const_i32(0); - let offset_align = Align::from_bytes(4).unwrap(); - assert!(bx.tcx().sess.target.endian == Endian::Little); - - let gr_type = target_ty.is_any_ptr() || target_ty.is_integral(); - let (reg_off, reg_top_index, slot_size) = if gr_type { - let gr_offs = bx.struct_gep(va_list_addr, 7); - let nreg = (layout.size.bytes() + 7) / 8; - (gr_offs, 3, nreg * 8) - } else { - let vr_off = bx.struct_gep(va_list_addr, 9); - let nreg = (layout.size.bytes() + 15) / 16; - (vr_off, 5, nreg * 16) - }; - - // if the offset >= 0 then the value will be on the stack - let mut reg_off_v = bx.load(reg_off, offset_align); - let use_stack = bx.icmp(IntPredicate::IntSGE, reg_off_v, zero); - bx.cond_br(use_stack, on_stack.llbb(), maybe_reg.llbb()); - - // The value at this point might be in a register, but there is a chance that - // it could be on the stack so we have to update the offset and then check - // the offset again. - - if gr_type && layout.align.abi.bytes() > 8 { - reg_off_v = maybe_reg.add(reg_off_v, bx.const_i32(15)); - reg_off_v = maybe_reg.and(reg_off_v, bx.const_i32(-16)); - } - let new_reg_off_v = maybe_reg.add(reg_off_v, bx.const_i32(slot_size as i32)); - - maybe_reg.store(new_reg_off_v, reg_off, offset_align); - - // Check to see if we have overflowed the registers as a result of this. - // If we have then we need to use the stack for this value - let use_stack = maybe_reg.icmp(IntPredicate::IntSGT, new_reg_off_v, zero); - maybe_reg.cond_br(use_stack, on_stack.llbb(), in_reg.llbb()); - - let top = in_reg.struct_gep(va_list_addr, reg_top_index); - let top = in_reg.load(top, bx.tcx().data_layout.pointer_align.abi); - - // reg_value = *(@top + reg_off_v); - let top = in_reg.gep(top, &[reg_off_v]); - let top = in_reg.bitcast(top, bx.cx.type_ptr_to(layout.gcc_type(bx, true))); - let reg_value = in_reg.load(top, layout.align.abi); - in_reg.assign(variable, reg_value); - in_reg.br(end.llbb()); - - // On Stack block - let stack_value = - emit_ptr_va_arg(&mut on_stack, list, target_ty, false, Align::from_bytes(8).unwrap(), true); - on_stack.assign(variable, stack_value); - on_stack.br(end.llbb()); - - *bx = end; - variable.to_rvalue() -} - -pub(super) fn emit_va_arg<'a, 'gcc, 'tcx>(bx: &mut Builder<'a, 'gcc, 'tcx>, addr: OperandRef<'tcx, RValue<'gcc>>, target_ty: Ty<'tcx>) -> RValue<'gcc> { - // Determine the va_arg implementation to use. The LLVM va_arg instruction - // is lacking in some instances, so we should only use it as a fallback. - let target = &bx.cx.tcx.sess.target; - let arch = &bx.cx.tcx.sess.target.arch; - match &**arch { - // Windows x86 - "x86" if target.options.is_like_windows => { - emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(4).unwrap(), false) - } - // Generic x86 - "x86" => emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(4).unwrap(), true), - // Windows AArch64 - "aarch64" if target.options.is_like_windows => { - emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(8).unwrap(), false) - } - // macOS / iOS AArch64 - "aarch64" if target.options.is_like_osx => { - emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(8).unwrap(), true) - } - "aarch64" => emit_aapcs_va_arg(bx, addr, target_ty), - // Windows x86_64 - "x86_64" if target.options.is_like_windows => { - let target_ty_size = bx.cx.size_of(target_ty).bytes(); - let indirect: bool = target_ty_size > 8 || !target_ty_size.is_power_of_two(); - emit_ptr_va_arg(bx, addr, target_ty, indirect, Align::from_bytes(8).unwrap(), false) - } - // For all other architecture/OS combinations fall back to using - // the LLVM va_arg instruction. - // https://llvm.org/docs/LangRef.html#va-arg-instruction - _ => bx.va_arg(addr.immediate(), bx.cx.layout_of(target_ty).gcc_type(bx.cx, true)), - } -}*/ diff --git a/test.sh b/test.sh index 1e13b06cf9c3d..06faa98d9b1f0 100755 --- a/test.sh +++ b/test.sh @@ -1,8 +1,7 @@ #!/bin/bash -# TODO: rewrite to cargo-make (or just) or something like that to only rebuild the sysroot when needed? +# TODO(antoyo): rewrite to cargo-make (or just) or something like that to only rebuild the sysroot when needed? -#set -x set -e export GCC_PATH=$(cat gcc_path) @@ -30,17 +29,9 @@ $RUSTC example/mini_core.rs --crate-name mini_core --crate-type lib,dylib --targ echo "[BUILD] example" $RUSTC example/example.rs --crate-type lib --target $TARGET_TRIPLE -#if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then - #echo "[JIT] mini_core_hello_world" - #CG_CLIF_JIT=1 CG_CLIF_JIT_ARGS="abc bcd" $RUSTC --crate-type bin -Cprefer-dynamic 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" $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 "[BUILD] sysroot" time ./build_sysroot/build_sysroot.sh @@ -52,20 +43,12 @@ $RUN_WRAPPER ./target/out/arbitrary_self_types_pointers_and_wrappers echo "[AOT] alloc_system" $RUSTC example/alloc_system.rs --crate-type lib --target "$TARGET_TRIPLE" -# FIXME: this requires linking an additional lib for __popcountdi2 -#echo "[AOT] alloc_example" -#$RUSTC example/alloc_example.rs --crate-type bin --target $TARGET_TRIPLE -#$RUN_WRAPPER ./target/out/alloc_example - -#if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then - #echo "[JIT] std_example" - #CG_CLIF_JIT=1 $RUSTC --crate-type bin -Cprefer-dynamic example/std_example.rs --target $HOST_TRIPLE -#else - #echo "[JIT] std_example (skipped)" -#fi +echo "[AOT] alloc_example" +$RUSTC example/alloc_example.rs --crate-type bin --target $TARGET_TRIPLE +$RUN_WRAPPER ./target/out/alloc_example echo "[AOT] dst_field_align" -# FIXME Re-add -Zmir-opt-level=2 once rust-lang/rust#67529 is fixed. +# FIXME(antoyo): Re-add -Zmir-opt-level=2 once rust-lang/rust#67529 is fixed. $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) @@ -81,14 +64,14 @@ echo "[AOT] track-caller-attribute" $RUSTC example/track-caller-attribute.rs --crate-type bin -Cpanic=abort --target $TARGET_TRIPLE $RUN_WRAPPER ./target/out/track-caller-attribute -# FIXME: this requires linking an additional lib for __popcountdi2 -#echo "[BUILD] mod_bench" -#$RUSTC example/mod_bench.rs --crate-type bin --target $TARGET_TRIPLE +echo "[BUILD] mod_bench" +$RUSTC example/mod_bench.rs --crate-type bin --target $TARGET_TRIPLE -# FIXME linker gives multiple definitions error on Linux +# FIXME(antoyo): linker gives multiple definitions error on Linux #echo "[BUILD] sysroot in release mode" #./build_sysroot/build_sysroot.sh --release +# TODO(antoyo): uncomment when it works. #pushd simple-raytracer #if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then #echo "[BENCH COMPILE] ebobby/simple-raytracer" @@ -113,6 +96,7 @@ rm -r ./target || true ../../../../../cargo.sh test popd +# TODO(antoyo): uncomment when it works. #pushd regex #echo "[TEST] rust-lang/regex example shootout-regex-dna" #../cargo.sh clean @@ -152,9 +136,6 @@ git fetch git checkout $(rustc -V | cut -d' ' -f3 | tr -d '(') export RUSTFLAGS= -#git apply ../rust_lang.patch - - rm config.toml || true cat > config.toml < Date: Sun, 15 Aug 2021 10:54:12 -0400 Subject: [PATCH 027/621] Empty gcc_path --- gcc_path | 1 - 1 file changed, 1 deletion(-) diff --git a/gcc_path b/gcc_path index b3696e3cf885e..e69de29bb2d1d 100644 --- a/gcc_path +++ b/gcc_path @@ -1 +0,0 @@ -/home/bouanto/Ordinateur/Programmation/Projets/gcc-build/build/gcc From 1bfa59afe042e03371c7c62860e94db1e20bd657 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Thu, 12 Aug 2021 16:46:58 -0500 Subject: [PATCH 028/621] Update rustc_codegen_cratelift for working_dir change --- src/debuginfo/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/debuginfo/mod.rs b/src/debuginfo/mod.rs index ceef65d54785f..c471da83de234 100644 --- a/src/debuginfo/mod.rs +++ b/src/debuginfo/mod.rs @@ -66,7 +66,7 @@ impl<'tcx> DebugContext<'tcx> { rustc_interface::util::version_str().unwrap_or("unknown version"), cranelift_codegen::VERSION, ); - let comp_dir = tcx.sess.working_dir.to_string_lossy(false).into_owned(); + let comp_dir = tcx.sess.opts.working_dir.to_string_lossy(false).into_owned(); let (name, file_info) = match tcx.sess.local_crate_source_file.clone() { Some(path) => { let name = path.to_string_lossy().into_owned(); From 923212e3e8fd39a7ef68db12bc4196c8ea41390e Mon Sep 17 00:00:00 2001 From: Stein Somers Date: Mon, 16 Aug 2021 17:29:35 +0200 Subject: [PATCH 029/621] BTree: toughen panicky test of clone() --- .../alloc/src/collections/btree/map/tests.rs | 55 +++++++++++-------- .../collections/btree/testing/crash_test.rs | 2 +- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index 17e538483431c..234410b1c051d 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -1494,33 +1494,40 @@ fn test_clone() { map.check(); } -#[test] -fn test_clone_panic_leak() { - let a = CrashTestDummy::new(0); - let b = CrashTestDummy::new(1); - let c = CrashTestDummy::new(2); +fn test_clone_panic_leak(size: usize) { + for i in 0..size { + let dummies: Vec = (0..size).map(|id| CrashTestDummy::new(id)).collect(); + let map: BTreeMap<_, ()> = dummies + .iter() + .map(|dummy| { + let panic = if dummy.id == i { Panic::InClone } else { Panic::Never }; + (dummy.spawn(panic), ()) + }) + .collect(); - let mut map = BTreeMap::new(); - map.insert(a.spawn(Panic::Never), ()); - map.insert(b.spawn(Panic::InClone), ()); - map.insert(c.spawn(Panic::Never), ()); + catch_unwind(|| map.clone()).unwrap_err(); + for d in &dummies { + assert_eq!(d.cloned(), if d.id <= i { 1 } else { 0 }, "id={}/{}", d.id, i); + assert_eq!(d.dropped(), if d.id < i { 1 } else { 0 }, "id={}/{}", d.id, i); + } + assert_eq!(map.len(), size); - catch_unwind(|| map.clone()).unwrap_err(); - assert_eq!(a.cloned(), 1); - assert_eq!(b.cloned(), 1); - assert_eq!(c.cloned(), 0); - assert_eq!(a.dropped(), 1); - assert_eq!(b.dropped(), 0); - assert_eq!(c.dropped(), 0); - assert_eq!(map.len(), 3); + drop(map); + for d in &dummies { + assert_eq!(d.cloned(), if d.id <= i { 1 } else { 0 }, "id={}/{}", d.id, i); + assert_eq!(d.dropped(), if d.id < i { 2 } else { 1 }, "id={}/{}", d.id, i); + } + } +} - drop(map); - assert_eq!(a.cloned(), 1); - assert_eq!(b.cloned(), 1); - assert_eq!(c.cloned(), 0); - assert_eq!(a.dropped(), 2); - assert_eq!(b.dropped(), 1); - assert_eq!(c.dropped(), 1); +#[test] +fn test_clone_panic_leak_height_0() { + test_clone_panic_leak(3) +} + +#[test] +fn test_clone_panic_leak_height_1() { + test_clone_panic_leak(MIN_INSERTS_HEIGHT_1) } #[test] diff --git a/library/alloc/src/collections/btree/testing/crash_test.rs b/library/alloc/src/collections/btree/testing/crash_test.rs index b2527b95f5bba..488eaa07a95b4 100644 --- a/library/alloc/src/collections/btree/testing/crash_test.rs +++ b/library/alloc/src/collections/btree/testing/crash_test.rs @@ -11,7 +11,7 @@ use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; /// on anything defined in the crate, apart from the `Debug` trait. #[derive(Debug)] pub struct CrashTestDummy { - id: usize, + pub id: usize, cloned: AtomicUsize, dropped: AtomicUsize, queried: AtomicUsize, From e394bb763be932c6748a16ddb49688e60464274a Mon Sep 17 00:00:00 2001 From: Stein Somers Date: Sun, 1 Aug 2021 11:06:21 +0200 Subject: [PATCH 030/621] BTree: refine some comments --- library/alloc/src/collections/btree/map.rs | 10 +++++----- library/alloc/src/collections/btree/navigate.rs | 6 ++---- library/alloc/src/collections/btree/node.rs | 4 ++-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs index 4b649e43371de..40aa91da75a35 100644 --- a/library/alloc/src/collections/btree/map.rs +++ b/library/alloc/src/collections/btree/map.rs @@ -17,16 +17,16 @@ mod entry; pub use entry::{Entry, OccupiedEntry, OccupiedError, VacantEntry}; use Entry::*; -/// Minimum number of elements in nodes that are not a root. +/// Minimum number of elements in a node that is not a root. /// We might temporarily have fewer elements during methods. pub(super) const MIN_LEN: usize = node::MIN_LEN_AFTER_SPLIT; // A tree in a `BTreeMap` is a tree in the `node` module with additional invariants: // - Keys must appear in ascending order (according to the key's type). -// - If the root node is internal, it must contain at least 1 element. +// - Every non-leaf node contains at least 1 element (has at least 2 children). // - Every non-root node contains at least MIN_LEN elements. // -// An empty map may be represented both by the absence of a root node or by a +// An empty map is represented either by the absence of a root node or by a // root node that is an empty leaf. /// A map based on a [B-Tree]. @@ -1723,8 +1723,8 @@ impl<'a, K: 'a, V: 'a> DrainFilterInner<'a, K, V> { pub(super) fn size_hint(&self) -> (usize, Option) { // In most of the btree iterators, `self.length` is the number of elements // yet to be visited. Here, it includes elements that were visited and that - // the predicate decided not to drain. Making this upper bound more accurate - // requires maintaining an extra field and is not worth while. + // the predicate decided not to drain. Making this upper bound more tight + // during iteration would require an extra field. (0, Some(*self.length)) } } diff --git a/library/alloc/src/collections/btree/navigate.rs b/library/alloc/src/collections/btree/navigate.rs index 7b1d4d68c4f56..9d0db34500945 100644 --- a/library/alloc/src/collections/btree/navigate.rs +++ b/library/alloc/src/collections/btree/navigate.rs @@ -440,8 +440,7 @@ impl Handle, marker::Edge> { /// - The given edge must not have been previously returned by counterpart /// `deallocating_next_back`. /// - The returned KV handle is only valid to access the key and value, - /// and only valid until the next call to this method or counterpart - /// `deallocating_next_back`. + /// and only valid until the next call to a `deallocating_` method. unsafe fn deallocating_next( self, ) -> Option<(Self, Handle, marker::KV>)> @@ -470,8 +469,7 @@ impl Handle, marker::Edge> { /// - The given edge must not have been previously returned by counterpart /// `deallocating_next`. /// - The returned KV handle is only valid to access the key and value, - /// and only valid until the next call to this method or counterpart - /// `deallocating_next`. + /// and only valid until the next call to a `deallocating_` method. unsafe fn deallocating_next_back( self, ) -> Option<(Self, Handle, marker::KV>)> diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 8f6a2ec9ebd1f..dfce98f97bd44 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -574,7 +574,7 @@ impl NodeRef { /// no cleanup is done on any of the keys, values and other children. /// This decreases the height by 1 and is the opposite of `push_internal_level`. /// - /// Requires exclusive access to the `Root` object but not to the root node; + /// Requires exclusive access to the `NodeRef` object but not to the root node; /// it will not invalidate other handles or references to the root node. /// /// Panics if there is no internal level, i.e., if the root node is a leaf. @@ -1663,7 +1663,7 @@ pub mod marker { const PERMITS_TRAVERSAL: bool = true; } impl BorrowType for Owned { - // Traversal isn't needede, it happens using the result of `borrow_mut`. + // Traversal isn't needed, it happens using the result of `borrow_mut`. // By disabling traversal, and only creating new references to roots, // we know that every reference of the `Owned` type is to a root node. const PERMITS_TRAVERSAL: bool = false; From 03ed8c811513a41ad076ae206cbc2a930888bef2 Mon Sep 17 00:00:00 2001 From: Charles Lew Date: Wed, 18 Aug 2021 12:45:18 +0800 Subject: [PATCH 031/621] Fold `vtable_trait_upcasting_coercion_new_vptr_slot` logic into obligation processing. --- src/unsize.rs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/src/unsize.rs b/src/unsize.rs index d9c4647cba3a9..fd96858010ea9 100644 --- a/src/unsize.rs +++ b/src/unsize.rs @@ -31,21 +31,10 @@ pub(crate) fn unsized_info<'tcx>( if data_a.principal_def_id() == data_b.principal_def_id() { return old_info; } - // trait upcasting coercion - // if both of the two `principal`s are `None`, this function would have returned early above. - // and if one of the two `principal`s is `None`, typechecking would have rejected this case. - let principal_a = data_a - .principal() - .expect("unsized_info: missing principal trait for trait upcasting coercion"); - let principal_b = data_b - .principal() - .expect("unsized_info: missing principal trait for trait upcasting coercion"); - - let vptr_entry_idx = fx.tcx.vtable_trait_upcasting_coercion_new_vptr_slot(( - principal_a.with_self_ty(fx.tcx, source), - principal_b.with_self_ty(fx.tcx, source), - )); + // trait upcasting coercion + let vptr_entry_idx = + fx.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((source, target)); if let Some(entry_idx) = vptr_entry_idx { let entry_idx = u32::try_from(entry_idx).unwrap(); From 8b8ab5f599f04d83f31994a47cf2345dbaddd38b Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Fri, 6 Aug 2021 17:14:27 +0200 Subject: [PATCH 032/621] remove box_syntax uses from cranelift and tools --- example/alloc_example.rs | 2 +- example/mini_core_hello_world.rs | 2 +- example/mod_bench.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/example/alloc_example.rs b/example/alloc_example.rs index 71e93e87b6c41..2a9f7e58e01c2 100644 --- a/example/alloc_example.rs +++ b/example/alloc_example.rs @@ -1,4 +1,4 @@ -#![feature(start, box_syntax, core_intrinsics, alloc_prelude, alloc_error_handler)] +#![feature(start, core_intrinsics, alloc_prelude, alloc_error_handler)] #![no_std] extern crate alloc; diff --git a/example/mini_core_hello_world.rs b/example/mini_core_hello_world.rs index d997ce6d1b379..6e13e4dcbfbff 100644 --- a/example/mini_core_hello_world.rs +++ b/example/mini_core_hello_world.rs @@ -1,4 +1,4 @@ -#![feature(no_core, lang_items, box_syntax, never_type, linkage, extern_types, thread_local)] +#![feature(no_core, lang_items, never_type, linkage, extern_types, thread_local)] #![no_core] #![allow(dead_code, non_camel_case_types)] diff --git a/example/mod_bench.rs b/example/mod_bench.rs index 152041aa9ed0b..e3e8a3c2d6ab0 100644 --- a/example/mod_bench.rs +++ b/example/mod_bench.rs @@ -1,4 +1,4 @@ -#![feature(start, box_syntax, core_intrinsics, lang_items)] +#![feature(start, core_intrinsics, lang_items)] #![no_std] #[cfg_attr(unix, link(name = "c"))] From edadc29c773781fb634492e501f21688fbb7cf55 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 20 Aug 2021 16:15:52 +0200 Subject: [PATCH 033/621] Rustup to rustc 1.56.0-nightly (6d64f7f69 2021-08-19) --- build_sysroot/Cargo.lock | 27 +++++++++++++++++++-------- example/alloc_example.rs | 2 +- example/mini_core_hello_world.rs | 2 +- rust-toolchain | 2 +- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/build_sysroot/Cargo.lock b/build_sysroot/Cargo.lock index e068f084234bc..307e0b222ca06 100644 --- a/build_sysroot/Cargo.lock +++ b/build_sysroot/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.14.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" +checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd" dependencies = [ "compiler_builtins", "gimli", @@ -99,9 +99,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.23.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce" +checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7" dependencies = [ "compiler_builtins", "rustc-std-workspace-alloc", @@ -132,13 +132,23 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.98" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" +checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" dependencies = [ "rustc-std-workspace-core", ] +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +dependencies = [ + "compiler_builtins", + "rustc-std-workspace-core", +] + [[package]] name = "miniz_oxide" version = "0.4.4" @@ -154,11 +164,12 @@ dependencies = [ [[package]] name = "object" -version = "0.22.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" +checksum = "ee2766204889d09937d00bfbb7fec56bb2a199e2ade963cab19185d8a6104c7c" dependencies = [ "compiler_builtins", + "memchr", "rustc-std-workspace-alloc", "rustc-std-workspace-core", ] diff --git a/example/alloc_example.rs b/example/alloc_example.rs index 2a9f7e58e01c2..d0d492e96742d 100644 --- a/example/alloc_example.rs +++ b/example/alloc_example.rs @@ -1,4 +1,4 @@ -#![feature(start, core_intrinsics, alloc_prelude, alloc_error_handler)] +#![feature(start, core_intrinsics, alloc_prelude, alloc_error_handler, box_syntax)] #![no_std] extern crate alloc; diff --git a/example/mini_core_hello_world.rs b/example/mini_core_hello_world.rs index 6e13e4dcbfbff..cbfdb3c44f33e 100644 --- a/example/mini_core_hello_world.rs +++ b/example/mini_core_hello_world.rs @@ -1,4 +1,4 @@ -#![feature(no_core, lang_items, never_type, linkage, extern_types, thread_local)] +#![feature(no_core, lang_items, never_type, linkage, extern_types, thread_local, box_syntax)] #![no_core] #![allow(dead_code, non_camel_case_types)] diff --git a/rust-toolchain b/rust-toolchain index fd95377d06dc8..a4db8011dc95e 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-08-07" +channel = "nightly-2021-08-20" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] From 3b22c3a8ec7cc7a66b472c82edc972a3d1fb4410 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 20 Aug 2021 19:30:16 +0200 Subject: [PATCH 034/621] Remove unnecessary bailout in clif_pair_type_from_ty --- src/common.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/common.rs b/src/common.rs index 892ccf27f6df8..a6d6cfac1dd8b 100644 --- a/src/common.rs +++ b/src/common.rs @@ -90,9 +90,6 @@ fn clif_pair_type_from_ty<'tcx>( 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, _) => { From d29e0484cf671a21399264d8b4c94d06a66f3b9a Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 20 Aug 2021 19:58:38 +0200 Subject: [PATCH 035/621] Revert "Remove unnecessary bailout in clif_pair_type_from_ty" This reverts commit 3b22c3a8ec7cc7a66b472c82edc972a3d1fb4410. Seems to be necessary after all. At least when compiling regex. --- src/common.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/common.rs b/src/common.rs index a6d6cfac1dd8b..892ccf27f6df8 100644 --- a/src/common.rs +++ b/src/common.rs @@ -90,6 +90,9 @@ fn clif_pair_type_from_ty<'tcx>( 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, _) => { From a929c952d42d874b15dadb17bc6837484a01e893 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 20 Aug 2021 21:32:04 +0200 Subject: [PATCH 036/621] Add more architectures to with_object --- src/backend.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/backend.rs b/src/backend.rs index 05c06bac27db4..023d197ab3945 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -122,10 +122,23 @@ pub(crate) fn with_object(sess: &Session, name: &str, f: impl FnOnce(&mut Object binary_format => sess.fatal(&format!("binary format {} is unsupported", binary_format)), }; let architecture = match triple.architecture { + target_lexicon::Architecture::Aarch64(_) => object::Architecture::Aarch64, + target_lexicon::Architecture::Arm(_) => object::Architecture::Arm, + target_lexicon::Architecture::Avr => object::Architecture::Avr, + target_lexicon::Architecture::Hexagon => object::Architecture::Hexagon, + target_lexicon::Architecture::Mips32(_) => object::Architecture::Mips, + target_lexicon::Architecture::Mips64(_) => object::Architecture::Mips64, + target_lexicon::Architecture::Msp430 => object::Architecture::Msp430, + target_lexicon::Architecture::Powerpc => object::Architecture::PowerPc, + target_lexicon::Architecture::Powerpc64 => object::Architecture::PowerPc64, + target_lexicon::Architecture::Powerpc64le => todo!(), + target_lexicon::Architecture::Riscv32(_) => object::Architecture::Riscv32, + target_lexicon::Architecture::Riscv64(_) => object::Architecture::Riscv64, + target_lexicon::Architecture::S390x => object::Architecture::S390x, + target_lexicon::Architecture::Sparc64 => object::Architecture::Sparc64, + target_lexicon::Architecture::Sparcv9 => object::Architecture::Sparc64, 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,)) } From d8308ece3656c2174faf4891d96e7be868a28eb7 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 20 Aug 2021 21:38:17 +0200 Subject: [PATCH 037/621] Move Object creation into metadata.rs --- src/backend.rs | 70 ++--------------------------------------------- src/driver/aot.rs | 4 +-- src/metadata.rs | 66 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 62 insertions(+), 78 deletions(-) diff --git a/src/backend.rs b/src/backend.rs index 023d197ab3945..aa8e417e4eb16 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -9,37 +9,13 @@ use cranelift_codegen::isa::TargetIsa; use cranelift_module::FuncId; use cranelift_object::{ObjectBuilder, ObjectModule, ObjectProduct}; -use object::write::*; -use object::{RelocationEncoding, SectionKind, SymbolFlags}; +use object::write::{Relocation, StandardSegment}; +use object::{RelocationEncoding, SectionKind}; use gimli::SectionId; use crate::debuginfo::{DebugReloc, DebugRelocName}; -pub(crate) trait WriteMetadata { - fn add_rustc_section(&mut self, symbol_name: String, data: Vec); -} - -impl WriteMetadata for object::write::Object { - fn add_rustc_section(&mut self, symbol_name: String, data: Vec) { - 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; @@ -112,48 +88,6 @@ impl WriteDebugInfo for ObjectProduct { } } -pub(crate) fn with_object(sess: &Session, name: &str, f: impl FnOnce(&mut Object)) -> Vec { - let triple = crate::target_triple(sess); - - 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::Aarch64(_) => object::Architecture::Aarch64, - target_lexicon::Architecture::Arm(_) => object::Architecture::Arm, - target_lexicon::Architecture::Avr => object::Architecture::Avr, - target_lexicon::Architecture::Hexagon => object::Architecture::Hexagon, - target_lexicon::Architecture::Mips32(_) => object::Architecture::Mips, - target_lexicon::Architecture::Mips64(_) => object::Architecture::Mips64, - target_lexicon::Architecture::Msp430 => object::Architecture::Msp430, - target_lexicon::Architecture::Powerpc => object::Architecture::PowerPc, - target_lexicon::Architecture::Powerpc64 => object::Architecture::PowerPc64, - target_lexicon::Architecture::Powerpc64le => todo!(), - target_lexicon::Architecture::Riscv32(_) => object::Architecture::Riscv32, - target_lexicon::Architecture::Riscv64(_) => object::Architecture::Riscv64, - target_lexicon::Architecture::S390x => object::Architecture::S390x, - target_lexicon::Architecture::Sparc64 => object::Architecture::Sparc64, - target_lexicon::Architecture::Sparcv9 => object::Architecture::Sparc64, - target_lexicon::Architecture::X86_32(_) => object::Architecture::I386, - target_lexicon::Architecture::X86_64 => object::Architecture::X86_64, - 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, isa: Box, name: String) -> ObjectModule { let mut builder = ObjectBuilder::new(isa, name + ".o", cranelift_module::default_libcall_names()).unwrap(); diff --git a/src/driver/aot.rs b/src/driver/aot.rs index a8b802f449437..7b65b48802352 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -267,9 +267,7 @@ pub(crate) fn run_aot( let tmp_file = tcx.output_filenames(()).temp_path(OutputType::Metadata, Some(&metadata_cgu_name)); - let obj = crate::backend::with_object(tcx.sess, &metadata_cgu_name, |object| { - crate::metadata::write_metadata(tcx, object); - }); + let obj = crate::metadata::new_metadata_object(tcx, &metadata_cgu_name); if let Err(err) = std::fs::write(&tmp_file, obj) { tcx.sess.fatal(&format!("error writing metadata object file: {}", err)); diff --git a/src/metadata.rs b/src/metadata.rs index db24bf65eb5a2..47d1032964201 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -1,11 +1,12 @@ //! Writing of the rustc metadata for dylibs -use rustc_middle::ty::TyCtxt; +use object::write::{Object, StandardSegment, Symbol, SymbolSection}; +use object::{SectionKind, SymbolFlags, SymbolKind, SymbolScope}; -use crate::backend::WriteMetadata; +use rustc_middle::ty::TyCtxt; // Adapted from https://github.com/rust-lang/rust/blob/da573206f87b5510de4b0ee1a9c044127e409bd3/src/librustc_codegen_llvm/base.rs#L47-L112 -pub(crate) fn write_metadata(tcx: TyCtxt<'_>, object: &mut O) { +pub(crate) fn new_metadata_object(tcx: TyCtxt<'_>, cgu_name: &str) -> Vec { use snap::write::FrameEncoder; use std::io::Write; @@ -13,8 +14,59 @@ pub(crate) fn write_metadata(tcx: TyCtxt<'_>, object: &mut O) let mut compressed = rustc_metadata::METADATA_HEADER.to_vec(); FrameEncoder::new(&mut compressed).write_all(&metadata.raw_data).unwrap(); - object.add_rustc_section( - rustc_middle::middle::exported_symbols::metadata_symbol_name(tcx), - compressed, - ); + let triple = crate::target_triple(tcx.sess); + + 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 => tcx.sess.fatal(&format!("binary format {} is unsupported", binary_format)), + }; + let architecture = match triple.architecture { + target_lexicon::Architecture::Aarch64(_) => object::Architecture::Aarch64, + target_lexicon::Architecture::Arm(_) => object::Architecture::Arm, + target_lexicon::Architecture::Avr => object::Architecture::Avr, + target_lexicon::Architecture::Hexagon => object::Architecture::Hexagon, + target_lexicon::Architecture::Mips32(_) => object::Architecture::Mips, + target_lexicon::Architecture::Mips64(_) => object::Architecture::Mips64, + target_lexicon::Architecture::Msp430 => object::Architecture::Msp430, + target_lexicon::Architecture::Powerpc => object::Architecture::PowerPc, + target_lexicon::Architecture::Powerpc64 => object::Architecture::PowerPc64, + target_lexicon::Architecture::Powerpc64le => todo!(), + target_lexicon::Architecture::Riscv32(_) => object::Architecture::Riscv32, + target_lexicon::Architecture::Riscv64(_) => object::Architecture::Riscv64, + target_lexicon::Architecture::S390x => object::Architecture::S390x, + target_lexicon::Architecture::Sparc64 => object::Architecture::Sparc64, + target_lexicon::Architecture::Sparcv9 => object::Architecture::Sparc64, + target_lexicon::Architecture::X86_32(_) => object::Architecture::I386, + target_lexicon::Architecture::X86_64 => object::Architecture::X86_64, + architecture => { + tcx.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 object = Object::new(binary_format, architecture, endian); + object.add_file_symbol(cgu_name.as_bytes().to_vec()); + + let segment = object.segment_name(StandardSegment::Data).to_vec(); + let section_id = object.add_section(segment, b".rustc".to_vec(), SectionKind::Data); + let offset = object.append_section_data(section_id, &compressed, 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. + object.add_symbol(Symbol { + name: rustc_middle::middle::exported_symbols::metadata_symbol_name(tcx).into_bytes(), + value: offset, + size: compressed.len() as u64, + kind: SymbolKind::Data, + scope: SymbolScope::Dynamic, + weak: false, + section: SymbolSection::Section(section_id), + flags: SymbolFlags::None, + }); + + object.write().unwrap() } From 14617516f8e3346ef5b6251c5ec9360cf81b7bbb Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 20 Aug 2021 21:38:50 +0200 Subject: [PATCH 038/621] Make {Debug,Unwind}Context::emit non-generic --- src/debuginfo/emit.rs | 3 ++- src/debuginfo/unwind.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/debuginfo/emit.rs b/src/debuginfo/emit.rs index fb6ccd7c53584..bbbc72279004d 100644 --- a/src/debuginfo/emit.rs +++ b/src/debuginfo/emit.rs @@ -1,5 +1,6 @@ //! Write the debuginfo into an object file. +use cranelift_object::ObjectProduct; use rustc_data_structures::fx::FxHashMap; use gimli::write::{Address, AttributeValue, EndianVec, Result, Sections, Writer}; @@ -10,7 +11,7 @@ use crate::backend::WriteDebugInfo; use super::DebugContext; impl DebugContext<'_> { - pub(crate) fn emit(&mut self, product: &mut P) { + pub(crate) fn emit(&mut self, product: &mut ObjectProduct) { 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); diff --git a/src/debuginfo/unwind.rs b/src/debuginfo/unwind.rs index d1251e749f31f..66ed0f03ee49c 100644 --- a/src/debuginfo/unwind.rs +++ b/src/debuginfo/unwind.rs @@ -4,6 +4,7 @@ use crate::prelude::*; use cranelift_codegen::isa::{unwind::UnwindInfo, TargetIsa}; +use cranelift_object::ObjectProduct; use gimli::write::{Address, CieId, EhFrame, FrameTable, Section}; use gimli::RunTimeEndian; @@ -55,7 +56,7 @@ impl UnwindContext { } } - pub(crate) fn emit(self, product: &mut P) { + pub(crate) fn emit(self, product: &mut ObjectProduct) { let mut eh_frame = EhFrame::from(super::emit::WriterRelocate::new(self.endian)); self.frame_table.write_eh_frame(&mut eh_frame).unwrap(); From 47e4a62ac50aecc36671c0c2cbfb90d82947c827 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 20 Aug 2021 21:43:53 +0200 Subject: [PATCH 039/621] Move all code out of backend.rs --- src/debuginfo/emit.rs | 3 +-- src/debuginfo/mod.rs | 1 + src/{backend.rs => debuginfo/object.rs} | 18 ++---------------- src/debuginfo/unwind.rs | 2 +- src/driver/aot.rs | 19 +++++++++++++++---- src/lib.rs | 1 - 6 files changed, 20 insertions(+), 24 deletions(-) rename src/{backend.rs => debuginfo/object.rs} (76%) diff --git a/src/debuginfo/emit.rs b/src/debuginfo/emit.rs index bbbc72279004d..c8c2d50b03409 100644 --- a/src/debuginfo/emit.rs +++ b/src/debuginfo/emit.rs @@ -6,8 +6,7 @@ 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::object::WriteDebugInfo; use super::DebugContext; impl DebugContext<'_> { diff --git a/src/debuginfo/mod.rs b/src/debuginfo/mod.rs index c471da83de234..3eba732e8823a 100644 --- a/src/debuginfo/mod.rs +++ b/src/debuginfo/mod.rs @@ -2,6 +2,7 @@ mod emit; mod line_info; +mod object; mod unwind; use crate::prelude::*; diff --git a/src/backend.rs b/src/debuginfo/object.rs similarity index 76% rename from src/backend.rs rename to src/debuginfo/object.rs index aa8e417e4eb16..9984dc92c44d0 100644 --- a/src/backend.rs +++ b/src/debuginfo/object.rs @@ -1,13 +1,9 @@ -//! Abstraction around the object writing crate - use std::convert::{TryFrom, TryInto}; use rustc_data_structures::fx::FxHashMap; -use rustc_session::Session; -use cranelift_codegen::isa::TargetIsa; use cranelift_module::FuncId; -use cranelift_object::{ObjectBuilder, ObjectModule, ObjectProduct}; +use cranelift_object::ObjectProduct; use object::write::{Relocation, StandardSegment}; use object::{RelocationEncoding, SectionKind}; @@ -16,7 +12,7 @@ use gimli::SectionId; use crate::debuginfo::{DebugReloc, DebugRelocName}; -pub(crate) trait WriteDebugInfo { +pub(super) trait WriteDebugInfo { type SectionId: Copy; fn add_debug_section(&mut self, name: SectionId, data: Vec) -> Self::SectionId; @@ -87,13 +83,3 @@ impl WriteDebugInfo for ObjectProduct { .unwrap(); } } - -pub(crate) fn make_module(sess: &Session, isa: Box, name: String) -> ObjectModule { - let mut builder = - ObjectBuilder::new(isa, 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) -} diff --git a/src/debuginfo/unwind.rs b/src/debuginfo/unwind.rs index 66ed0f03ee49c..f0896ea0e167f 100644 --- a/src/debuginfo/unwind.rs +++ b/src/debuginfo/unwind.rs @@ -8,7 +8,7 @@ use cranelift_object::ObjectProduct; use gimli::write::{Address, CieId, EhFrame, FrameTable, Section}; use gimli::RunTimeEndian; -use crate::backend::WriteDebugInfo; +use super::object::WriteDebugInfo; pub(crate) struct UnwindContext { endian: RunTimeEndian, diff --git a/src/driver/aot.rs b/src/driver/aot.rs index 7b65b48802352..8564e2e7adf30 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -11,8 +11,10 @@ use rustc_middle::middle::cstore::EncodedMetadata; use rustc_middle::mir::mono::{CodegenUnit, MonoItem}; use rustc_session::cgu_reuse_tracker::CguReuse; use rustc_session::config::{DebugInfo, OutputType}; +use rustc_session::Session; -use cranelift_object::ObjectModule; +use cranelift_codegen::isa::TargetIsa; +use cranelift_object::{ObjectBuilder, ObjectModule}; use crate::{prelude::*, BackendConfig}; @@ -24,6 +26,16 @@ impl HashStable for ModuleCodegenResult { } } +fn make_module(sess: &Session, isa: Box, name: String) -> ObjectModule { + let mut builder = + ObjectBuilder::new(isa, 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) +} + fn emit_module( tcx: TyCtxt<'_>, backend_config: &BackendConfig, @@ -105,7 +117,7 @@ fn module_codegen( let mono_items = cgu.items_in_deterministic_order(tcx); let isa = crate::build_isa(tcx.sess, &backend_config); - let mut module = crate::backend::make_module(tcx.sess, isa, cgu_name.as_str().to_string()); + let mut module = make_module(tcx.sess, isa, cgu_name.as_str().to_string()); let mut cx = crate::CodegenCx::new( tcx, @@ -228,8 +240,7 @@ pub(crate) fn run_aot( tcx.sess.abort_if_errors(); let isa = crate::build_isa(tcx.sess, &backend_config); - let mut allocator_module = - crate::backend::make_module(tcx.sess, isa, "allocator_shim".to_string()); + let mut allocator_module = make_module(tcx.sess, isa, "allocator_shim".to_string()); assert_eq!(pointer_ty(tcx), allocator_module.target_config().pointer_type()); let mut allocator_unwind_context = UnwindContext::new(tcx, allocator_module.isa(), true); let created_alloc_shim = diff --git a/src/lib.rs b/src/lib.rs index 4ef53663ca0d9..0f145d3ea70ee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,7 +45,6 @@ mod abi; mod allocator; mod analyze; mod archive; -mod backend; mod base; mod cast; mod codegen_i128; From 22112e439092e26536a9c9aaa35e892ce36afbea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lanteri=20Thauvin?= Date: Tue, 24 Aug 2021 10:07:16 +0200 Subject: [PATCH 040/621] Remove unnecessary unsafe block in `process_unix` --- library/std/src/sys/unix/process/process_unix.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 12edf04a4e2e9..c4215e2c78d77 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -552,8 +552,7 @@ impl Process { use crate::os::unix::io::FromRawFd; use crate::sys_common::FromInner; // Safety: If `pidfd` is nonnegative, we assume it's valid and otherwise unowned. - let pidfd = (pidfd >= 0) - .then(|| PidFd::from_inner(unsafe { sys::fd::FileDesc::from_raw_fd(pidfd) })); + let pidfd = (pidfd >= 0).then(|| PidFd::from_inner(sys::fd::FileDesc::from_raw_fd(pidfd))); Process { pid, status: None, pidfd } } From 848a38ac9d9bc956cded893bdd55e1171a2706dd Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Tue, 24 Aug 2021 18:56:24 +0100 Subject: [PATCH 041/621] Manual Debug for Unix ExitCode ExitStatus ExitStatusError These structs have misleading names. An ExitStatus[Error] is actually a Unix wait status; an ExitCode is actually an exit status. The Display impls are fixed, but the Debug impls are still misleading, as reported in #74832. Fix this by pretending that these internal structs are called `unix_exit_status` and `unix_wait_status` as applicable. (We can't actually rename the structs because of the way that the cross-platform machinery works: the names are cross-platform.) Signed-off-by: Ian Jackson --- .../std/src/sys/unix/process/process_common.rs | 8 +++++++- library/std/src/sys/unix/process/process_unix.rs | 16 ++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs index 7b261a302c33f..7ac2f9d8af75a 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -457,9 +457,15 @@ impl fmt::Debug for Command { } } -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Copy)] pub struct ExitCode(u8); +impl fmt::Debug for ExitCode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("unix_exit_status").field(&self.0).finish() + } +} + impl ExitCode { pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _); pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _); diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 12edf04a4e2e9..7b8230d89daa7 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -607,9 +607,15 @@ impl Process { } /// Unix exit statuses -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Copy)] pub struct ExitStatus(c_int); +impl fmt::Debug for ExitStatus { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("unix_wait_status").field(&self.0).finish() + } +} + impl ExitStatus { pub fn new(status: c_int) -> ExitStatus { ExitStatus(status) @@ -683,7 +689,7 @@ impl fmt::Display for ExitStatus { } } -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Copy)] pub struct ExitStatusError(NonZero_c_int); impl Into for ExitStatusError { @@ -692,6 +698,12 @@ impl Into for ExitStatusError { } } +impl fmt::Debug for ExitStatusError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("unix_wait_status").field(&self.0).finish() + } +} + impl ExitStatusError { pub fn code(self) -> Option { ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap()) From 92f488754b345f2d93b82195107facef07aea7db Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Wed, 25 Aug 2021 13:12:40 +0200 Subject: [PATCH 042/621] Rustup to rustc 1.56.0-nightly (b03ccace5 2021-08-24) --- build_sysroot/Cargo.lock | 8 ++++---- rust-toolchain | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build_sysroot/Cargo.lock b/build_sysroot/Cargo.lock index 307e0b222ca06..21a4711f97a34 100644 --- a/build_sysroot/Cargo.lock +++ b/build_sysroot/Cargo.lock @@ -132,9 +132,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.99" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765" +checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5" dependencies = [ "rustc-std-workspace-core", ] @@ -206,9 +206,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dead70b0b5e03e9c814bcb6b01e03e68f7c57a80aa48c72ec92152ab3e818d49" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", diff --git a/rust-toolchain b/rust-toolchain index a4db8011dc95e..626062c84ddf6 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-08-20" +channel = "nightly-2021-08-25" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] From d1f164142b1f551fbec8759a803882ed3935d8c7 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Wed, 25 Aug 2021 13:46:15 +0200 Subject: [PATCH 043/621] Update patches/0027-sysroot-128bit-atomic-operations.patch --- ...027-sysroot-128bit-atomic-operations.patch | 41 +++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/patches/0027-sysroot-128bit-atomic-operations.patch b/patches/0027-sysroot-128bit-atomic-operations.patch index cda8153083c33..e2d07bd126702 100644 --- a/patches/0027-sysroot-128bit-atomic-operations.patch +++ b/patches/0027-sysroot-128bit-atomic-operations.patch @@ -1,4 +1,4 @@ -From 6a4e6f5dc8c8a529a822eb9b57f9e57519595439 Mon Sep 17 00:00:00 2001 +From ad7ffe71baba46865f2e65266ab025920dfdc20b Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Thu, 18 Feb 2021 18:45:28 +0100 Subject: [PATCH] Disable 128bit atomic operations @@ -8,7 +8,8 @@ Cranelift doesn't support them yet library/core/src/panic/unwind_safe.rs | 6 ----- library/core/src/sync/atomic.rs | 38 --------------------------- library/core/tests/atomic.rs | 4 --- - 3 files changed, 48 deletions(-) + library/std/src/time/monotonic.rs | 6 +++-- + 4 files changed, 4 insertions(+), 50 deletions(-) diff --git a/library/core/src/panic/unwind_safe.rs b/library/core/src/panic/unwind_safe.rs index 092b7cf..158cf71 100644 @@ -35,10 +36,10 @@ index 092b7cf..158cf71 100644 #[cfg(target_has_atomic_load_store = "8")] #[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")] diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs -index 0194c58..25a0038 100644 +index d9de37e..8293fce 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs -@@ -2229,44 +2229,6 @@ atomic_int! { +@@ -2234,44 +2234,6 @@ atomic_int! { "AtomicU64::new(0)", u64 AtomicU64 ATOMIC_U64_INIT } @@ -98,6 +99,38 @@ index b735957..ea728b6 100644 #[cfg(target_has_atomic = "ptr")] assert_eq!(align_of::(), size_of::()); #[cfg(target_has_atomic = "ptr")] +diff --git a/library/std/src/time/monotonic.rs b/library/std/src/time/monotonic.rs +index fa96b7a..2854f9c 100644 +--- a/library/std/src/time/monotonic.rs ++++ b/library/std/src/time/monotonic.rs +@@ -5,7 +5,7 @@ pub(super) fn monotonize(raw: time::Instant) -> time::Instant { + inner::monotonize(raw) + } + +-#[cfg(all(target_has_atomic = "64", not(target_has_atomic = "128")))] ++#[cfg(target_has_atomic = "64")] + pub mod inner { + use crate::sync::atomic::AtomicU64; + use crate::sync::atomic::Ordering::*; +@@ -70,6 +70,7 @@ pub mod inner { + } + } + ++/* + #[cfg(target_has_atomic = "128")] + pub mod inner { + use crate::sync::atomic::AtomicU128; +@@ -94,8 +95,9 @@ pub mod inner { + ZERO.checked_add_duration(&Duration::new(secs, nanos)).unwrap() + } + } ++*/ + +-#[cfg(not(any(target_has_atomic = "64", target_has_atomic = "128")))] ++#[cfg(not(target_has_atomic = "64"))] + pub mod inner { + use crate::cmp; + use crate::sys::time; -- 2.26.2.7.g19db9cfb68 From 9f5b52045c928157b12bec1d670e220fc7597375 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Wed, 25 Aug 2021 15:17:34 +0200 Subject: [PATCH 044/621] Filter out tests marked with needs-asm-support --- scripts/test_rustc_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/test_rustc_tests.sh b/scripts/test_rustc_tests.sh index 0ac49dd35740f..b714d47fec2a6 100755 --- a/scripts/test_rustc_tests.sh +++ b/scripts/test_rustc_tests.sh @@ -11,7 +11,7 @@ pushd rust cargo install ripgrep rm -r src/test/ui/{extern/,panics/,unsized-locals/,thinlto/,simd*,*lto*.rs,linkage*,unwind-*.rs} || true -for test in $(rg --files-with-matches "asm!|catch_unwind|should_panic|lto" src/test/ui); do +for test in $(rg --files-with-matches "asm!|catch_unwind|should_panic|lto|// needs-asm-support" src/test/ui); do rm $test done From e0ea466661961b55b981c08681d75e9b35c58f5e Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 16 Mar 2021 00:05:45 +0100 Subject: [PATCH 045/621] make unevaluated const substs optional --- src/constant.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/constant.rs b/src/constant.rs index c87309e22224f..424a0d742d12b 100644 --- a/src/constant.rs +++ b/src/constant.rs @@ -129,13 +129,13 @@ pub(crate) fn codegen_constant<'tcx>( }; let const_val = match const_.val { ConstKind::Value(const_val) => const_val, - ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) - if fx.tcx.is_static(def.did) => + ConstKind::Unevaluated(uv) + if fx.tcx.is_static(uv.def.did) => { - assert!(substs.is_empty()); - assert!(promoted.is_none()); + assert!(uv.substs(fx.tcx).is_empty()); + assert!(uv.promoted.is_none()); - return codegen_static_ref(fx, def.did, fx.layout_of(const_.ty)).to_cvalue(fx); + return codegen_static_ref(fx, uv.def.did, fx.layout_of(const_.ty)).to_cvalue(fx); } ConstKind::Unevaluated(unevaluated) => { match fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), unevaluated, None) { From 905c2ba5f8592c9722c7317cbc2781a7561c250b Mon Sep 17 00:00:00 2001 From: Jacob Pratt Date: Thu, 26 Aug 2021 05:27:39 -0400 Subject: [PATCH 046/621] Partially stabilize `array_methods` This also makes `<[T; N]>::as_slice` const due to its trivial nature. --- library/core/src/array/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index 3c638e655dc91..3b21a980e8feb 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -368,14 +368,14 @@ impl [T; N] { } /// Returns a slice containing the entire array. Equivalent to `&s[..]`. - #[unstable(feature = "array_methods", issue = "76118")] - pub fn as_slice(&self) -> &[T] { + #[stable(feature = "array_as_slice", since = "1.57.0")] + pub const fn as_slice(&self) -> &[T] { self } /// Returns a mutable slice containing the entire array. Equivalent to /// `&mut s[..]`. - #[unstable(feature = "array_methods", issue = "76118")] + #[stable(feature = "array_as_slice", since = "1.57.0")] pub fn as_mut_slice(&mut self) -> &mut [T] { self } From ca88f10e39d421cbbd876a5da8d1ce52f84df73d Mon Sep 17 00:00:00 2001 From: Sebastian Widua Date: Thu, 26 Aug 2021 23:23:17 +0200 Subject: [PATCH 047/621] Add missing # Panics section to `Vec` method namely `Vec::extend_from_within` --- library/alloc/src/vec/mod.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 87a0d37181562..39ed03c437943 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2164,7 +2164,12 @@ impl Vec { /// Copies elements from `src` range to the end of the vector. /// - /// ## Examples + /// # Panics + /// + /// Panics if the starting point is greater than the end point or if + /// the end point is greater than the length of the vector. + /// + /// # Examples /// /// ``` /// let mut vec = vec![0, 1, 2, 3, 4]; From 7753eda91a846fd279d74567559515bfa3e16f85 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Thu, 26 Aug 2021 12:46:01 +0200 Subject: [PATCH 048/621] Path remapping: Make behavior of diagnostics output dependent on presence of --remap-path-prefix. --- src/debuginfo/mod.rs | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/debuginfo/mod.rs b/src/debuginfo/mod.rs index c471da83de234..14a593a71ce78 100644 --- a/src/debuginfo/mod.rs +++ b/src/debuginfo/mod.rs @@ -66,7 +66,7 @@ impl<'tcx> DebugContext<'tcx> { rustc_interface::util::version_str().unwrap_or("unknown version"), cranelift_codegen::VERSION, ); - let comp_dir = tcx.sess.opts.working_dir.to_string_lossy(false).into_owned(); + let comp_dir = tcx.sess.opts.working_dir.to_string_lossy(FileNameDisplayPreference::Remapped).into_owned(); let (name, file_info) = match tcx.sess.local_crate_source_file.clone() { Some(path) => { let name = path.to_string_lossy().into_owned(); diff --git a/src/lib.rs b/src/lib.rs index 4ef53663ca0d9..6c7c8cbc311f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,7 +74,7 @@ mod vtable; mod prelude { pub(crate) use std::convert::{TryFrom, TryInto}; - pub(crate) use rustc_span::Span; + pub(crate) use rustc_span::{Span, FileNameDisplayPreference}; pub(crate) use rustc_hir::def_id::{DefId, LOCAL_CRATE}; pub(crate) use rustc_middle::bug; From d9f0448815fa189cbe418026cf0ca1f1241715a4 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Wed, 25 Aug 2021 14:51:22 +0300 Subject: [PATCH 049/621] rustc_target: add lifetime parameter to `LayoutOf`. --- src/common.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common.rs b/src/common.rs index 892ccf27f6df8..03f462a63b31b 100644 --- a/src/common.rs +++ b/src/common.rs @@ -256,7 +256,7 @@ pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> { pub(crate) inline_asm_index: u32, } -impl<'tcx> LayoutOf for FunctionCx<'_, '_, 'tcx> { +impl<'tcx> LayoutOf<'tcx> for FunctionCx<'_, '_, 'tcx> { type Ty = Ty<'tcx>; type TyAndLayout = TyAndLayout<'tcx>; @@ -364,7 +364,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { pub(crate) struct RevealAllLayoutCx<'tcx>(pub(crate) TyCtxt<'tcx>); -impl<'tcx> LayoutOf for RevealAllLayoutCx<'tcx> { +impl<'tcx> LayoutOf<'tcx> for RevealAllLayoutCx<'tcx> { type Ty = Ty<'tcx>; type TyAndLayout = TyAndLayout<'tcx>; From 6f5c9b97dc536d71725f6905f98940daa09bfbdc Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 27 Aug 2021 14:04:00 +0200 Subject: [PATCH 050/621] Enable some warnings --- src/bin/cg_clif.rs | 3 +++ src/bin/cg_clif_build_sysroot.rs | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/bin/cg_clif.rs b/src/bin/cg_clif.rs index a044b43b86470..b924f2085a0fc 100644 --- a/src/bin/cg_clif.rs +++ b/src/bin/cg_clif.rs @@ -1,4 +1,7 @@ #![feature(rustc_private, once_cell)] +#![warn(rust_2018_idioms)] +#![warn(unused_lifetimes)] +#![warn(unreachable_pub)] extern crate rustc_data_structures; extern crate rustc_driver; diff --git a/src/bin/cg_clif_build_sysroot.rs b/src/bin/cg_clif_build_sysroot.rs index e7cd5edbbf654..bde4d71b9a33c 100644 --- a/src/bin/cg_clif_build_sysroot.rs +++ b/src/bin/cg_clif_build_sysroot.rs @@ -7,8 +7,10 @@ //! target crates. #![feature(rustc_private)] +#![warn(rust_2018_idioms)] +#![warn(unused_lifetimes)] +#![warn(unreachable_pub)] -extern crate rustc_data_structures; extern crate rustc_driver; extern crate rustc_interface; extern crate rustc_session; From e9eac6bd93624775209ca7a9c5e7f2ccf13248f5 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 27 Aug 2021 15:17:17 +0200 Subject: [PATCH 051/621] Remove unnecessary feature gates One wasn't necessary anymore at all. Others are only necessary when jit mode is enabled. --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 0f145d3ea70ee..fc7b323e0d3b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ -#![feature(rustc_private, decl_macro, never_type, hash_drain_filter, vec_into_raw_parts, once_cell)] +#![feature(rustc_private, decl_macro)] +#![cfg_attr(feature = "jit", feature(never_type, vec_into_raw_parts, once_cell))] #![warn(rust_2018_idioms)] #![warn(unused_lifetimes)] #![warn(unreachable_pub)] From 18fb97e4cc66e35fd552789b884a8784f6ae19d5 Mon Sep 17 00:00:00 2001 From: Mariano Casco Date: Wed, 25 Aug 2021 18:06:31 -0300 Subject: [PATCH 052/621] Remove ignore-tidy-undocumented-unsafe from core::slice::sort Write down the missing safety arguments. --- library/core/src/slice/sort.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/library/core/src/slice/sort.rs b/library/core/src/slice/sort.rs index 8a31388fbdbbc..60b39295cafe1 100644 --- a/library/core/src/slice/sort.rs +++ b/library/core/src/slice/sort.rs @@ -6,8 +6,6 @@ //! Unstable sorting is compatible with libcore because it doesn't allocate memory, unlike our //! stable sorting implementation. -// ignore-tidy-undocumented-unsafe - use crate::cmp; use crate::mem::{self, MaybeUninit}; use crate::ptr; @@ -291,6 +289,9 @@ where } else if start_r < end_r { block_l = rem; } else { + // There were the same number of elements to switch on both blocks during the last + // iteration, so there are no remaining elements on either block. Cover the remaining + // items with roughly equally-sized blocks. block_l = rem / 2; block_r = rem - block_l; } @@ -437,6 +438,17 @@ where // Move its remaining out-of-order elements to the far right. debug_assert_eq!(width(l, r), block_l); while start_l < end_l { + // remaining-elements-safety + // SAFETY: while the loop condition holds there are still elements in `offsets_l`, so it + // is safe to point `end_l` to the previous element. + // + // The `ptr::swap` is safe if both its arguments are valid for reads and writes: + // - Per the debug assert above, the distance between `l` and `r` is `block_l` + // elements, so there can be at most `block_l` remaining offsets between `start_l` + // and `end_l`. This means `r` will be moved at most `block_l` steps back, which + // makes the `r.offset` calls valid (at that point `l == r`). + // - `offsets_l` contains valid offsets into `v` collected during the partitioning of + // the last block, so the `l.offset` calls are valid. unsafe { end_l = end_l.offset(-1); ptr::swap(l.offset(*end_l as isize), r.offset(-1)); @@ -449,6 +461,7 @@ where // Move its remaining out-of-order elements to the far left. debug_assert_eq!(width(l, r), block_r); while start_r < end_r { + // SAFETY: See the reasoning in [remaining-elements-safety]. unsafe { end_r = end_r.offset(-1); ptr::swap(l, r.offset(-(*end_r as isize) - 1)); @@ -481,6 +494,8 @@ where // Read the pivot into a stack-allocated variable for efficiency. If a following comparison // operation panics, the pivot will be automatically written back into the slice. + + // SAFETY: `pivot` is a reference to the first element of `v`, so `ptr::read` is safe. let mut tmp = mem::ManuallyDrop::new(unsafe { ptr::read(pivot) }); let _pivot_guard = CopyOnDrop { src: &mut *tmp, dest: pivot }; let pivot = &*tmp; @@ -646,6 +661,12 @@ where if len >= 8 { // Swaps indices so that `v[a] <= v[b]`. + // SAFETY: `len >= 8` so there are at least two elements in the neighborhoods of + // `a`, `b` and `c`. This means the three calls to `sort_adjacent` result in + // corresponding calls to `sort3` with valid 3-item neighborhoods around each + // pointer, which in turn means the calls to `sort2` are done with valid + // references. Thus the `v.get_unchecked` calls are safe, as is the `ptr::swap` + // call. let mut sort2 = |a: &mut usize, b: &mut usize| unsafe { if is_less(v.get_unchecked(*b), v.get_unchecked(*a)) { ptr::swap(a, b); From 5dad13cc3bfbf69f470198860ea905d72fc9fbd3 Mon Sep 17 00:00:00 2001 From: antoyo Date: Sat, 28 Aug 2021 11:34:47 -0400 Subject: [PATCH 053/621] Update custom rustc instructions (#73) --- .github/workflows/main.yml | 5 ++++- .gitignore | 1 + Readme.md | 3 +-- gcc_path | 0 4 files changed, 6 insertions(+), 3 deletions(-) delete mode 100644 gcc_path diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 774a68aa5e1a4..98bed8ef387ff 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -31,7 +31,9 @@ jobs: ln gcc-build/libgccjit.so gcc-build/libgccjit.so.0 - name: Set LIBRARY_PATH - run: echo "LIBRARY_PATH=$(cat gcc_path)" >> $GITHUB_ENV + run: | + echo "LIBRARY_PATH=$(cat gcc_path)" >> $GITHUB_ENV + echo "LD_LIBRARY_PATH=$(cat gcc_path)" >> $GITHUB_ENV # https://github.com/actions/cache/issues/133 - name: Fixup owner of ~/.cargo/ @@ -66,6 +68,7 @@ jobs: run: | ./prepare_build.sh ./build.sh + cargo test ./clean_all.sh - name: Prepare dependencies diff --git a/.gitignore b/.gitignore index d9f0ae05ed40d..573ed83e53566 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ gimple* *asm res test-backend +gcc_path diff --git a/Readme.md b/Readme.md index 44cb16334742f..96a6c3a04145e 100644 --- a/Readme.md +++ b/Readme.md @@ -113,6 +113,5 @@ p loc->m_line ### How to use a custom-build rustc - * Build the stage1 compiler (`rustup toolchain link debug-current stage2 build/x86_64-unknown-linux-gnu/stage1`). + * Build the stage2 compiler (`rustup toolchain link debug-current build/x86_64-unknown-linux-gnu/stage2`). * Clean and rebuild the codegen with `debug-current` in the file `rust-toolchain`. - * Add `~/.rustup/toolchains/debug-current/lib/rustlib/x86_64-unknown-linux-gnu/lib` to `LD_LIBRARY_PATH`. diff --git a/gcc_path b/gcc_path deleted file mode 100644 index e69de29bb2d1d..0000000000000 From dec7fc3ced5bc3c18d0e5d29921d087f93189cb8 Mon Sep 17 00:00:00 2001 From: Theodore Luo Wang Date: Sat, 28 Aug 2021 21:18:20 -0400 Subject: [PATCH 054/621] Fix an ICE caused by type mismatch errors being ignored --- compiler/rustc_typeck/src/check/demand.rs | 6 ------ .../ui/typeck/issue-87771-ice-assign-assign-to-bool.rs | 4 ++++ .../typeck/issue-87771-ice-assign-assign-to-bool.stderr | 9 +++++++++ 3 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 src/test/ui/typeck/issue-87771-ice-assign-assign-to-bool.rs create mode 100644 src/test/ui/typeck/issue-87771-ice-assign-assign-to-bool.stderr diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index 808685d7fa9c8..f186d35bcbc84 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -145,12 +145,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expr_ty = self.resolve_vars_with_obligations(checked_ty); let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e); - if self.is_assign_to_bool(expr, expected) { - // Error reported in `check_assign` so avoid emitting error again. - err.delay_as_bug(); - return (expected, None); - } - self.emit_coerce_suggestions(&mut err, expr, expr_ty, expected, expected_ty_expr); (expected, Some(err)) diff --git a/src/test/ui/typeck/issue-87771-ice-assign-assign-to-bool.rs b/src/test/ui/typeck/issue-87771-ice-assign-assign-to-bool.rs new file mode 100644 index 0000000000000..751dc8719cf33 --- /dev/null +++ b/src/test/ui/typeck/issue-87771-ice-assign-assign-to-bool.rs @@ -0,0 +1,4 @@ +fn main() { + let mut a; + a = a = true; //~ ERROR mismatched types +} diff --git a/src/test/ui/typeck/issue-87771-ice-assign-assign-to-bool.stderr b/src/test/ui/typeck/issue-87771-ice-assign-assign-to-bool.stderr new file mode 100644 index 0000000000000..fe10fa733d238 --- /dev/null +++ b/src/test/ui/typeck/issue-87771-ice-assign-assign-to-bool.stderr @@ -0,0 +1,9 @@ +error[E0308]: mismatched types + --> $DIR/issue-87771-ice-assign-assign-to-bool.rs:3:9 + | +LL | a = a = true; + | ^^^^^^^^ expected `bool`, found `()` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From 2ab73cf63d94d9ca0dc8a777813ad9339b3991f1 Mon Sep 17 00:00:00 2001 From: Cheng XU Date: Sat, 28 Aug 2021 19:46:58 -0700 Subject: [PATCH 055/621] add benchmark for From<[T; N]> in VecDeque --- library/alloc/benches/vec_deque.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/library/alloc/benches/vec_deque.rs b/library/alloc/benches/vec_deque.rs index bf2dffd1e931e..404cfa6addb73 100644 --- a/library/alloc/benches/vec_deque.rs +++ b/library/alloc/benches/vec_deque.rs @@ -52,3 +52,18 @@ fn bench_try_fold(b: &mut Bencher) { b.iter(|| black_box(ring.iter().try_fold(0, |a, b| Some(a + b)))) } + +#[bench] +fn bench_from_array_1000(b: &mut Bencher) { + const N: usize = 1000; + let mut array: [usize; N] = [0; N]; + + for i in 0..N { + array[i] = i; + } + + b.iter(|| { + let deq: VecDeque<_> = array.into(); + black_box(deq); + }) +} From c3cff0a75412f18971a325640a5dd7bd67b28972 Mon Sep 17 00:00:00 2001 From: Cheng XU Date: Sat, 28 Aug 2021 21:09:36 -0700 Subject: [PATCH 056/621] VecDeque: improve performance for From<[T; N]> Create VecDeque directly from the array instead of inserting items one-by-one. --- .../alloc/src/collections/vec_deque/mod.rs | 12 +++++++- .../alloc/src/collections/vec_deque/tests.rs | 30 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index e4b28204158d9..31d92b0ef7366 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -3003,6 +3003,16 @@ impl From<[T; N]> for VecDeque { /// assert_eq!(deq1, deq2); /// ``` fn from(arr: [T; N]) -> Self { - core::array::IntoIter::new(arr).collect() + let mut deq = VecDeque::with_capacity(N); + let arr = ManuallyDrop::new(arr); + if mem::size_of::() != 0 { + // SAFETY: VecDeque::with_capacity ensures that there is enough capacity. + unsafe { + ptr::copy_nonoverlapping(arr.as_ptr(), deq.ptr(), N); + } + } + deq.tail = 0; + deq.head = N; + deq } } diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs index 56257e4346254..2be83f68f017f 100644 --- a/library/alloc/src/collections/vec_deque/tests.rs +++ b/library/alloc/src/collections/vec_deque/tests.rs @@ -507,6 +507,36 @@ fn test_from_vec_zst_overflow() { assert_eq!(vd.len(), vec.len()); } +#[test] +fn test_from_array() { + fn test() { + let mut array: [usize; N] = [0; N]; + + for i in 0..N { + array[i] = i; + } + + let deq: VecDeque<_> = array.into(); + + for i in 0..N { + assert_eq!(deq[i], i); + } + + assert!(deq.cap().is_power_of_two()); + assert_eq!(deq.len(), N); + } + test::<0>(); + test::<1>(); + test::<2>(); + test::<32>(); + test::<35>(); + + let array = [(); MAXIMUM_ZST_CAPACITY - 1]; + let deq = VecDeque::from(array); + assert!(deq.cap().is_power_of_two()); + assert_eq!(deq.len(), MAXIMUM_ZST_CAPACITY - 1); +} + #[test] fn test_vec_from_vecdeque() { use crate::vec::Vec; From 10ddabc194cc3349d4cd0951b00484d736edc6aa Mon Sep 17 00:00:00 2001 From: Lamb Date: Fri, 2 Jul 2021 23:34:52 +0200 Subject: [PATCH 057/621] const fn for option copied, take & replace + tests fix: move test that require mut to another Adding TODOs for Option::take and Option::copied TODO to FIXME + moving const stability under normal Moving const stability attr under normal stab attr move more rustc stability attributes --- library/core/src/lib.rs | 1 + library/core/src/option.rs | 23 ++++++++++++++++------- library/core/tests/option.rs | 13 +++++++++++++ 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 1a6d1aed2fd1e..28433f734c656 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -91,6 +91,7 @@ #![feature(const_maybe_uninit_assume_init)] #![feature(const_option)] #![feature(const_pin)] +#![feature(const_replace)] #![feature(const_ptr_offset)] #![feature(const_ptr_offset_from)] #![feature(const_ptr_read)] diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 47865240f6a6f..59ebc90222402 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -540,8 +540,8 @@ impl Option { /// ``` #[must_use = "if you intended to assert that this has a value, consider `.unwrap()` instead"] #[inline] - #[rustc_const_stable(feature = "const_option", since = "1.48.0")] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_option", since = "1.48.0")] pub const fn is_some(&self) -> bool { matches!(*self, Some(_)) } @@ -560,8 +560,8 @@ impl Option { #[must_use = "if you intended to assert that this doesn't have a value, consider \ `.and_then(|_| panic!(\"`Option` had a value when expected `None`\"))` instead"] #[inline] - #[rustc_const_stable(feature = "const_option", since = "1.48.0")] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_stable(feature = "const_option", since = "1.48.0")] pub const fn is_none(&self) -> bool { !self.is_some() } @@ -1312,8 +1312,10 @@ impl Option { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn take(&mut self) -> Option { - mem::take(self) + #[rustc_const_unstable(feature = "const_option", issue = "67441")] + pub const fn take(&mut self) -> Option { + // FIXME replace `mem::replace` by `mem::take` when the latter is const ready + mem::replace(self, None) } /// Replaces the actual value in the option by the value given in parameter, @@ -1334,8 +1336,9 @@ impl Option { /// assert_eq!(old, None); /// ``` #[inline] + #[rustc_const_unstable(feature = "const_option", issue = "67441")] #[stable(feature = "option_replace", since = "1.31.0")] - pub fn replace(&mut self, value: T) -> Option { + pub const fn replace(&mut self, value: T) -> Option { mem::replace(self, Some(value)) } @@ -1440,8 +1443,14 @@ impl Option<&T> { /// assert_eq!(copied, Some(12)); /// ``` #[stable(feature = "copied", since = "1.35.0")] - pub fn copied(self) -> Option { - self.map(|&t| t) + #[rustc_const_unstable(feature = "const_option", issue = "67441")] + pub const fn copied(self) -> Option { + // FIXME: this implementation, which sidesteps using `Option::map` since it's not const + // ready yet, should be reverted when possible to avoid code repetition + match self { + Some(&v) => Some(v), + None => None, + } } } diff --git a/library/core/tests/option.rs b/library/core/tests/option.rs index cd8fdebe36a05..8995f96b1238a 100644 --- a/library/core/tests/option.rs +++ b/library/core/tests/option.rs @@ -367,6 +367,19 @@ fn option_const() { const IS_NONE: bool = OPTION.is_none(); assert!(!IS_NONE); + + const COPIED: Option = OPTION.as_ref().copied(); + assert_eq!(COPIED, OPTION); +} + +#[test] +const fn option_const_mut() { + // test that the methods of `Option` that take mutable references are usable in a const context + + let mut option: Option = Some(32); + + let _take = option.take(); + let _replace = option.replace(42); } #[test] From 39e16da52916f3c22a7c08f165154ad3c7ca9be1 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Wed, 25 Aug 2021 18:05:10 +0300 Subject: [PATCH 058/621] rustc_target: `TyAndLayout::field` should never error. --- src/debuginfo/mod.rs | 10 ++++------ src/intrinsics/mod.rs | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/debuginfo/mod.rs b/src/debuginfo/mod.rs index c471da83de234..95216f1c3d787 100644 --- a/src/debuginfo/mod.rs +++ b/src/debuginfo/mod.rs @@ -160,12 +160,10 @@ impl<'tcx> DebugContext<'tcx> { for (field_idx, field_def) in variant.fields.iter().enumerate() { let field_offset = layout.fields.offset(field_idx); - let field_layout = layout - .field( - &layout::LayoutCx { tcx: self.tcx, param_env: ParamEnv::reveal_all() }, - field_idx, - ) - .unwrap(); + let field_layout = layout.field( + &layout::LayoutCx { tcx: self.tcx, param_env: ParamEnv::reveal_all() }, + field_idx, + ); let field_type = self.dwarf_ty(field_layout.ty); diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 1c4d307fc50e1..12f61e0c564aa 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -789,7 +789,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( return; } - if intrinsic == sym::assert_zero_valid && !layout.might_permit_raw_init(fx, /*zero:*/ true).unwrap() { + if intrinsic == sym::assert_zero_valid && !layout.might_permit_raw_init(fx, /*zero:*/ true) { with_no_trimmed_paths(|| crate::base::codegen_panic( fx, &format!("attempted to zero-initialize type `{}`, which is invalid", T), @@ -798,7 +798,7 @@ pub(crate) fn codegen_intrinsic_call<'tcx>( return; } - if intrinsic == sym::assert_uninit_valid && !layout.might_permit_raw_init(fx, /*zero:*/ false).unwrap() { + if intrinsic == sym::assert_uninit_valid && !layout.might_permit_raw_init(fx, /*zero:*/ false) { with_no_trimmed_paths(|| crate::base::codegen_panic( fx, &format!("attempted to leave type `{}` uninitialized, which is invalid", T), From 18df8d6e559174700e302369c2fe0d9c7c143e66 Mon Sep 17 00:00:00 2001 From: Kevin Reid Date: Mon, 30 Aug 2021 20:20:14 -0700 Subject: [PATCH 059/621] Expand documentation for `FpCategory`. I intend these changes to be helpful to readers who are not yet familiar with the quirks of floating-point numbers. Additionally, I felt it was misleading to describe `Nan` as being the result of division by zero, since most divisions by zero (except for 0/0) produce `Infinite` floats, so I moved that remark to the `Infinite` variant with adjustment. The first sentence of the `Nan` documentation is copied from `f32`; I followed the example of the `f64` documentation by referring to `f32` for general concepts, rather than duplicating the text. --- library/core/src/num/mod.rs | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 09b7418bec0d9..360ca9b700b7a 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -778,23 +778,41 @@ impl usize { #[derive(Copy, Clone, PartialEq, Eq, Debug)] #[stable(feature = "rust1", since = "1.0.0")] pub enum FpCategory { - /// "Not a Number", often obtained by dividing by zero. + /// NaN (not a number): this value results from calculations like `(-1.0).sqrt()`. + /// + /// See [the documentation for `f32`](f32) for more information on the unusual properties + /// of NaN. #[stable(feature = "rust1", since = "1.0.0")] Nan, - /// Positive or negative infinity. + /// Positive or negative infinity, which often results from dividing a nonzero number + /// by zero. #[stable(feature = "rust1", since = "1.0.0")] Infinite, /// Positive or negative zero. + /// + /// See [the documentation for `f32`](f32) for more information on the signedness of zeroes. #[stable(feature = "rust1", since = "1.0.0")] Zero, - /// De-normalized floating point representation (less precise than `Normal`). + /// “Subnormal” or “denormal” floating point representation (less precise, relative to + /// their magnitude, than [`Normal`]). + /// + /// Subnormal numbers are larger in magnitude than [`Zero`] but smaller in magnitude than all + /// [`Normal`] numbers. + /// + /// [`Normal`]: Self::Normal + /// [`Zero`]: Self::Zero #[stable(feature = "rust1", since = "1.0.0")] Subnormal, - /// A regular floating point number. + /// A regular floating point number, not any of the exceptional categories. + /// + /// The smallest positive normal numbers are [`f32::MIN_POSITIVE`] and [`f64::MIN_POSITIVE`], + /// and the largest positive normal numbers are [`f32::MAX`] and [`f64::MAX`]. (Unlike signed + /// integers, floating point numbers are symmetric in their range, so negating any of these + /// constants will produce their negative counterpart.) #[stable(feature = "rust1", since = "1.0.0")] Normal, } From 0b007722ad4fa825128c2183b137bd09f8a494f9 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Wed, 1 Sep 2021 14:21:36 +0200 Subject: [PATCH 060/621] Rustup to rustc 1.56.0-nightly (29ef6cf16 2021-08-31) --- build_sysroot/Cargo.lock | 14 +++++++------- build_system/prepare.rs | 2 +- rust-toolchain | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/build_sysroot/Cargo.lock b/build_sysroot/Cargo.lock index 21a4711f97a34..c1f43113d5c33 100644 --- a/build_sysroot/Cargo.lock +++ b/build_sysroot/Cargo.lock @@ -40,9 +40,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "cc" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2" +checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0" [[package]] name = "cfg-if" @@ -56,7 +56,7 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.46" +version = "0.1.50" dependencies = [ "rustc-std-workspace-core", ] @@ -132,9 +132,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5" +checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" dependencies = [ "rustc-std-workspace-core", ] @@ -164,9 +164,9 @@ dependencies = [ [[package]] name = "object" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2766204889d09937d00bfbb7fec56bb2a199e2ade963cab19185d8a6104c7c" +checksum = "39f37e50073ccad23b6d09bcb5b263f4e76d3bb6038e4a3c08e52162ffa8abc2" dependencies = [ "compiler_builtins", "memchr", diff --git a/build_system/prepare.rs b/build_system/prepare.rs index 4b2051b605abd..7994ccc9ed71b 100644 --- a/build_system/prepare.rs +++ b/build_system/prepare.rs @@ -92,7 +92,7 @@ fn prepare_sysroot() { clone_repo( "build_sysroot/compiler-builtins", "https://github.com/rust-lang/compiler-builtins.git", - "0.1.46", + "0.1.50", ); apply_patches("compiler-builtins", Path::new("build_sysroot/compiler-builtins")); } diff --git a/rust-toolchain b/rust-toolchain index 626062c84ddf6..9514427156732 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-08-25" +channel = "nightly-2021-09-01" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] From 677c786109a5b47b3f143847e278689687d63abb Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Wed, 1 Sep 2021 14:38:37 +0200 Subject: [PATCH 061/621] Move add_rlib and add_native_library to cg_ssa This deduplicates logic between codegen backends --- src/archive.rs | 83 ++++++++++++-------------------------------------- 1 file changed, 19 insertions(+), 64 deletions(-) diff --git a/src/archive.rs b/src/archive.rs index 22897c43e7ef9..0fa228fc944a1 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -4,8 +4,7 @@ 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_codegen_ssa::back::archive::ArchiveBuilder; use rustc_session::Session; use object::{Object, ObjectSymbol, SymbolKind}; @@ -19,7 +18,6 @@ enum ArchiveEntry { pub(crate) struct ArArchiveBuilder<'a> { sess: &'a Session, dst: PathBuf, - lib_search_paths: Vec, use_gnu_style_archive: bool, no_builtin_ranlib: bool, @@ -31,8 +29,6 @@ pub(crate) struct ArArchiveBuilder<'a> { 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(); @@ -55,7 +51,6 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { 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, @@ -85,42 +80,27 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { )); } - fn add_native_library(&mut self, name: rustc_span::symbol::Symbol, verbatim: bool) { - let location = find_library(name, verbatim, &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; - } + fn add_archive(&mut self, archive_path: &Path, 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(); - // 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; + 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; + } - // ok, don't skip this - false - }) + self.src_archives.push((archive_path.to_owned(), archive)); + Ok(()) } fn update_symbols(&mut self) {} @@ -264,28 +244,3 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> { bug!("injecting dll imports is not supported"); } } - -impl<'a> ArArchiveBuilder<'a> { - fn add_archive(&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(()) - } -} From 66ccff19b81c68aacf2f5be6de2c41d530b21c22 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Wed, 1 Sep 2021 15:05:03 +0200 Subject: [PATCH 062/621] Update protable-simd --- build_system/prepare.rs | 8 ++-- ...able-simd-Disable-unsupported-tests.patch} | 48 +++++++------------ scripts/tests.sh | 4 +- 3 files changed, 23 insertions(+), 37 deletions(-) rename patches/{0001-stdsimd-Disable-unsupported-tests.patch => 0001-portable-simd-Disable-unsupported-tests.patch} (78%) diff --git a/build_system/prepare.rs b/build_system/prepare.rs index 7994ccc9ed71b..722843903bbfa 100644 --- a/build_system/prepare.rs +++ b/build_system/prepare.rs @@ -28,11 +28,11 @@ pub(crate) fn prepare() { ); clone_repo( - "stdsimd", - "https://github.com/rust-lang/stdsimd", - "be96995d8ddec03fac9a0caf4d4c51c7fbc33507", + "portable-simd", + "https://github.com/rust-lang/portable-simd", + "d42875302dd9e924f8d667b32e88989388989b79", ); - apply_patches("stdsimd", Path::new("stdsimd")); + apply_patches("portable-simd", Path::new("portable-simd")); clone_repo( "simple-raytracer", diff --git a/patches/0001-stdsimd-Disable-unsupported-tests.patch b/patches/0001-portable-simd-Disable-unsupported-tests.patch similarity index 78% rename from patches/0001-stdsimd-Disable-unsupported-tests.patch rename to patches/0001-portable-simd-Disable-unsupported-tests.patch index 731c60fda58d6..6c52783369398 100644 --- a/patches/0001-stdsimd-Disable-unsupported-tests.patch +++ b/patches/0001-portable-simd-Disable-unsupported-tests.patch @@ -4,21 +4,20 @@ Date: Sun, 25 Jul 2021 18:39:31 +0200 Subject: [PATCH] Disable unsupported tests --- - crates/core_simd/src/array.rs | 2 ++ - crates/core_simd/src/lib.rs | 2 +- + crates/core_simd/src/vector.rs | 2 ++ crates/core_simd/src/math.rs | 4 ++++ crates/core_simd/tests/masks.rs | 12 ------------ crates/core_simd/tests/ops_macros.rs | 6 ++++++ crates/core_simd/tests/round.rs | 2 ++ 6 files changed, 15 insertions(+), 13 deletions(-) -diff --git a/crates/core_simd/src/array.rs b/crates/core_simd/src/array.rs +diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 25c5309..2b3d819 100644 ---- a/crates/core_simd/src/array.rs -+++ b/crates/core_simd/src/array.rs +--- a/crates/core_simd/src/vector.rs ++++ b/crates/core_simd/src/vector.rs @@ -22,6 +22,7 @@ where - #[must_use] - fn splat(val: Self::Scalar) -> Self; + self.0 + } + /* /// SIMD gather: construct a SIMD vector by reading from a slice, using potentially discontiguous indices. @@ -31,27 +30,14 @@ index 25c5309..2b3d819 100644 + */ } - macro_rules! impl_simdarray_for { -diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs -index a64904d..299eb11 100644 ---- a/crates/core_simd/src/lib.rs -+++ b/crates/core_simd/src/lib.rs -@@ -1,7 +1,7 @@ - #![no_std] - #![allow(incomplete_features)] - #![feature( -- const_generics, -+ const_generics, - platform_intrinsics, - repr_simd, - simd_ffi, + impl Copy for Simd diff --git a/crates/core_simd/src/math.rs b/crates/core_simd/src/math.rs index 7290a28..e394730 100644 --- a/crates/core_simd/src/math.rs +++ b/crates/core_simd/src/math.rs @@ -2,6 +2,7 @@ macro_rules! impl_uint_arith { - ($(($name:ident, $n:ident)),+) => { - $( impl $name where Self: crate::LanesAtMost32 { + ($($ty:ty),+) => { + $( impl Simd<$ty, LANES> where LaneCount: SupportedLaneCount { + /* /// Lanewise saturating add. @@ -66,8 +52,8 @@ index 7290a28..e394730 100644 } } @@ -46,6 +48,7 @@ macro_rules! impl_int_arith { - ($(($name:ident, $n:ident)),+) => { - $( impl $name where Self: crate::LanesAtMost32 { + ($($ty:ty),+) => { + $( impl Simd<$ty, LANES> where LaneCount: SupportedLaneCount { + /* /// Lanewise saturating add. @@ -87,7 +73,7 @@ index 61d8e44..2bccae2 100644 +++ b/crates/core_simd/tests/masks.rs @@ -67,18 +67,6 @@ macro_rules! test_mask_api { assert_eq!(int.to_array(), [-1, 0, 0, -1, 0, 0, -1, 0]); - assert_eq!(core_simd::$name::<8>::from_int(int), mask); + assert_eq!(core_simd::Mask::<$type, 8>::from_int(int), mask); } - - #[test] @@ -96,10 +82,10 @@ index 61d8e44..2bccae2 100644 - true, false, false, true, false, false, true, false, - true, true, false, false, false, false, false, true, - ]; -- let mask = core_simd::$name::<16>::from_array(values); +- let mask = core_simd::Mask::<$type, 16>::from_array(values); - let bitmask = mask.to_bitmask(); - assert_eq!(bitmask, [0b01001001, 0b10000011]); -- assert_eq!(core_simd::$name::<16>::from_bitmask(bitmask), mask); +- assert_eq!(core_simd::Mask::<$type, 16>::from_bitmask(bitmask), mask); - } } } @@ -122,7 +108,7 @@ index cb39e73..fc0ebe1 100644 } + */ - fn sqrt() { + fn recip() { test_helpers::test_unary_elementwise( @@ -581,6 +585,7 @@ macro_rules! impl_float_tests { }); @@ -138,8 +124,8 @@ index cb39e73..fc0ebe1 100644 } + */ } - } - } + + #[cfg(feature = "std")] diff --git a/crates/core_simd/tests/round.rs b/crates/core_simd/tests/round.rs index 37044a7..4cdc6b7 100644 --- a/crates/core_simd/tests/round.rs diff --git a/scripts/tests.sh b/scripts/tests.sh index 0eef710239bdd..28a7980d6613c 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -137,8 +137,8 @@ function extended_sysroot_tests() { fi popd - pushd stdsimd - echo "[TEST] rust-lang/stdsimd" + pushd portable-simd + echo "[TEST] rust-lang/portable-simd" ../build/cargo clean ../build/cargo build --all-targets --target $TARGET_TRIPLE if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then From 33b62ae3f33b4b7757e12c931a463a08135d5c4c Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Wed, 1 Sep 2021 15:06:25 +0200 Subject: [PATCH 063/621] Disable protable-simd tests Portable-simd doesn't compile on latest nightly --- scripts/tests.sh | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/scripts/tests.sh b/scripts/tests.sh index 28a7980d6613c..a71a99641bbc3 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -137,14 +137,15 @@ function extended_sysroot_tests() { fi popd - pushd portable-simd - echo "[TEST] rust-lang/portable-simd" - ../build/cargo clean - ../build/cargo build --all-targets --target $TARGET_TRIPLE - if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then - ../build/cargo test -q - fi - popd + # FIXME(rust-lang/portable-simd#156) portable-simd doesn't compile with latest nightly + #pushd portable-simd + #echo "[TEST] rust-lang/portable-simd" + #../build/cargo clean + #../build/cargo build --all-targets --target $TARGET_TRIPLE + #if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then + # ../build/cargo test -q + #fi + #popd } case "$1" in From ddaa7459b2f6e4a8840f038003417d0b0037b859 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Wed, 1 Sep 2021 17:21:50 +0200 Subject: [PATCH 064/621] Update clean_all.sh for the rename of stdsimd to portable-simd --- clean_all.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clean_all.sh b/clean_all.sh index 23e5bf2e0a8fd..865de7d234f14 100755 --- a/clean_all.sh +++ b/clean_all.sh @@ -3,4 +3,4 @@ set -e rm -rf build_sysroot/{sysroot_src/,target/,compiler-builtins/,rustc_version} rm -rf target/ build/ perf.data{,.old} -rm -rf rand/ regex/ simple-raytracer/ stdsimd/ +rm -rf rand/ regex/ simple-raytracer/ portable-simd/ From b710c0d48ce8d5873441a8078dcf992c1d951ac8 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Mon, 30 Aug 2021 17:38:27 +0300 Subject: [PATCH 065/621] rustc_target: move `LayoutOf` to `ty::layout`. --- src/common.rs | 6 ++---- src/lib.rs | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/common.rs b/src/common.rs index 03f462a63b31b..6ba03f0340cc6 100644 --- a/src/common.rs +++ b/src/common.rs @@ -257,8 +257,7 @@ pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> { } impl<'tcx> LayoutOf<'tcx> for FunctionCx<'_, '_, 'tcx> { - type Ty = Ty<'tcx>; - type TyAndLayout = TyAndLayout<'tcx>; + type LayoutOfResult = TyAndLayout<'tcx>; fn layout_of(&self, ty: Ty<'tcx>) -> TyAndLayout<'tcx> { RevealAllLayoutCx(self.tcx).layout_of(ty) @@ -365,8 +364,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { pub(crate) struct RevealAllLayoutCx<'tcx>(pub(crate) TyCtxt<'tcx>); impl<'tcx> LayoutOf<'tcx> for RevealAllLayoutCx<'tcx> { - type Ty = Ty<'tcx>; - type TyAndLayout = TyAndLayout<'tcx>; + type LayoutOfResult = TyAndLayout<'tcx>; fn layout_of(&self, ty: Ty<'tcx>) -> TyAndLayout<'tcx> { assert!(!ty.still_further_specializable()); diff --git a/src/lib.rs b/src/lib.rs index 4ef53663ca0d9..34cd309d26f34 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,12 +79,12 @@ mod prelude { pub(crate) use rustc_hir::def_id::{DefId, LOCAL_CRATE}; pub(crate) use rustc_middle::bug; pub(crate) use rustc_middle::mir::{self, *}; - pub(crate) use rustc_middle::ty::layout::{self, TyAndLayout}; + pub(crate) use rustc_middle::ty::layout::{self, LayoutOf, TyAndLayout}; pub(crate) use rustc_middle::ty::{ self, FloatTy, Instance, InstanceDef, IntTy, ParamEnv, Ty, TyCtxt, TypeAndMut, TypeFoldable, UintTy, }; - pub(crate) use rustc_target::abi::{Abi, LayoutOf, Scalar, Size, VariantIdx}; + pub(crate) use rustc_target::abi::{Abi, Scalar, Size, VariantIdx}; pub(crate) use rustc_data_structures::fx::FxHashMap; From 6a9c9b0f18d6e1587fb2770ec999ce07c18fb46e Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Mon, 30 Aug 2021 18:01:58 +0300 Subject: [PATCH 066/621] ty::layout: implement `layout_of` automatically as a default method. --- src/common.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/common.rs b/src/common.rs index 6ba03f0340cc6..1d7a1f11b6c80 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,4 +1,5 @@ use rustc_index::vec::IndexVec; +use rustc_middle::ty::layout::LayoutError; use rustc_middle::ty::SymbolName; use rustc_target::abi::call::FnAbi; use rustc_target::abi::{Integer, Primitive}; @@ -259,8 +260,9 @@ pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> { impl<'tcx> LayoutOf<'tcx> for FunctionCx<'_, '_, 'tcx> { type LayoutOfResult = TyAndLayout<'tcx>; - fn layout_of(&self, ty: Ty<'tcx>) -> TyAndLayout<'tcx> { - RevealAllLayoutCx(self.tcx).layout_of(ty) + #[inline] + fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { + RevealAllLayoutCx(self.tcx).handle_layout_err(err, span, ty) } } @@ -366,15 +368,13 @@ pub(crate) struct RevealAllLayoutCx<'tcx>(pub(crate) TyCtxt<'tcx>); impl<'tcx> LayoutOf<'tcx> for RevealAllLayoutCx<'tcx> { type LayoutOfResult = TyAndLayout<'tcx>; - fn layout_of(&self, ty: Ty<'tcx>) -> TyAndLayout<'tcx> { - assert!(!ty.still_further_specializable()); - self.0.layout_of(ParamEnv::reveal_all().and(&ty)).unwrap_or_else(|e| { - if let layout::LayoutError::SizeOverflow(_) = e { - self.0.sess.fatal(&e.to_string()) - } else { - bug!("failed to get layout for `{}`: {}", ty, e) - } - }) + #[inline] + fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { + if let layout::LayoutError::SizeOverflow(_) = err { + self.0.sess.span_fatal(span, &err.to_string()) + } else { + span_bug!(span, "failed to get layout for `{}`: {}", ty, err) + } } } From dc6c4defdcfe4a518a1dd1b54602d2a9693f161c Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Mon, 30 Aug 2021 20:37:36 +0300 Subject: [PATCH 067/621] ty::layout: split `LayoutOf` into required and (blanket) provided halves. --- src/common.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common.rs b/src/common.rs index 1d7a1f11b6c80..6f7ca51d038c0 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,5 +1,5 @@ use rustc_index::vec::IndexVec; -use rustc_middle::ty::layout::LayoutError; +use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers}; use rustc_middle::ty::SymbolName; use rustc_target::abi::call::FnAbi; use rustc_target::abi::{Integer, Primitive}; @@ -257,7 +257,7 @@ pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> { pub(crate) inline_asm_index: u32, } -impl<'tcx> LayoutOf<'tcx> for FunctionCx<'_, '_, 'tcx> { +impl<'tcx> LayoutOfHelpers<'tcx> for FunctionCx<'_, '_, 'tcx> { type LayoutOfResult = TyAndLayout<'tcx>; #[inline] @@ -365,7 +365,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { pub(crate) struct RevealAllLayoutCx<'tcx>(pub(crate) TyCtxt<'tcx>); -impl<'tcx> LayoutOf<'tcx> for RevealAllLayoutCx<'tcx> { +impl<'tcx> LayoutOfHelpers<'tcx> for RevealAllLayoutCx<'tcx> { type LayoutOfResult = TyAndLayout<'tcx>; #[inline] From a8ac6d471e074e15e0ecd44744735503c2eaf1dd Mon Sep 17 00:00:00 2001 From: bdbai Date: Thu, 2 Sep 2021 18:18:00 +0800 Subject: [PATCH 068/621] I/O safety for WinUWP --- library/std/src/sys/windows/fs.rs | 4 ++-- library/std/src/sys/windows/stdio_uwp.rs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index 0c1a50e231cd4..cc137771bb8d4 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -357,7 +357,7 @@ impl File { let mut info: c::FILE_BASIC_INFO = mem::zeroed(); let size = mem::size_of_val(&info); cvt(c::GetFileInformationByHandleEx( - self.handle.raw(), + self.handle.as_raw_handle(), c::FileBasicInfo, &mut info as *mut _ as *mut libc::c_void, size as c::DWORD, @@ -385,7 +385,7 @@ impl File { let mut info: c::FILE_STANDARD_INFO = mem::zeroed(); let size = mem::size_of_val(&info); cvt(c::GetFileInformationByHandleEx( - self.handle.raw(), + self.handle.as_raw_handle(), c::FileStandardInfo, &mut info as *mut _ as *mut libc::c_void, size as c::DWORD, diff --git a/library/std/src/sys/windows/stdio_uwp.rs b/library/std/src/sys/windows/stdio_uwp.rs index 872511af862a7..31ffa8c95718d 100644 --- a/library/std/src/sys/windows/stdio_uwp.rs +++ b/library/std/src/sys/windows/stdio_uwp.rs @@ -2,6 +2,7 @@ use crate::io; use crate::mem::ManuallyDrop; +use crate::os::windows::io::FromRawHandle; use crate::sys::c; use crate::sys::handle::Handle; @@ -25,7 +26,7 @@ pub fn get_handle(handle_id: c::DWORD) -> io::Result { fn write(handle_id: c::DWORD, data: &[u8]) -> io::Result { let handle = get_handle(handle_id)?; - let handle = Handle::new(handle); + let handle = unsafe { Handle::from_raw_handle(handle) }; ManuallyDrop::new(handle).write(data) } @@ -38,7 +39,7 @@ impl Stdin { impl io::Read for Stdin { fn read(&mut self, buf: &mut [u8]) -> io::Result { let handle = get_handle(c::STD_INPUT_HANDLE)?; - let handle = Handle::new(handle); + let handle = unsafe { Handle::from_raw_handle(handle) }; ManuallyDrop::new(handle).read(buf) } } From e6049759d0c0d0bdef1b37edec03d76f52621746 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Mon, 30 Aug 2021 15:06:56 +0200 Subject: [PATCH 069/621] Use in_incr_comp_dir_sess in cg_clif --- src/driver/aot.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/driver/aot.rs b/src/driver/aot.rs index a8b802f449437..3de706ed6d7af 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -68,14 +68,13 @@ fn reuse_workproduct_for_cgu( cgu: &CodegenUnit<'_>, work_products: &mut FxHashMap, ) -> CompiledModule { - let incr_comp_session_dir = tcx.sess.incr_comp_session_dir(); let mut object = None; let work_product = cgu.work_product(tcx); if let Some(saved_file) = &work_product.saved_file { let obj_out = tcx.output_filenames(()).temp_path(OutputType::Object, Some(&cgu.name().as_str())); object = Some(obj_out.clone()); - let source_file = rustc_incremental::in_incr_comp_dir(&incr_comp_session_dir, &saved_file); + let source_file = rustc_incremental::in_incr_comp_dir_sess(&tcx.sess, &saved_file); if let Err(err) = rustc_fs_util::link_or_copy(&source_file, &obj_out) { tcx.sess.err(&format!( "unable to copy {} to {}: {}", From aaa6de7905b4e5a9d194b8eb6810761861d4280a Mon Sep 17 00:00:00 2001 From: Ophir LOJKINE Date: Fri, 3 Sep 2021 12:14:55 +0200 Subject: [PATCH 070/621] Add a better error message for #39364 There is a known bug in the implementation of mpsc channels in rust. This adds a clearer error message when the bug occurs, so that developers don't lose too much time looking for the origin of the bug. See https://github.com/rust-lang/rust/issues/39364 --- library/std/src/sync/mpsc/shared.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sync/mpsc/shared.rs b/library/std/src/sync/mpsc/shared.rs index 0c32e636a5633..d4ee1b414d9b0 100644 --- a/library/std/src/sync/mpsc/shared.rs +++ b/library/std/src/sync/mpsc/shared.rs @@ -248,7 +248,7 @@ impl Packet { // Returns true if blocking should proceed. fn decrement(&self, token: SignalToken) -> StartResult { unsafe { - assert_eq!(self.to_wake.load(Ordering::SeqCst), 0); + assert_eq!(self.to_wake.load(Ordering::SeqCst), 0, "This is a known bug in rust. See https://github.com/rust-lang/rust/issues/39364"); let ptr = token.cast_to_usize(); self.to_wake.store(ptr, Ordering::SeqCst); From ce450f893d551e25123e0bdb27acc9a85d15cb7f Mon Sep 17 00:00:00 2001 From: Ali Saidi Date: Sat, 4 Sep 2021 15:11:26 -0500 Subject: [PATCH 071/621] Use the 64b inner:monotonize() implementation not the 128b one for aarch64 aarch64 prior to v8.4 (FEAT_LSE2) doesn't have an instruction that guarantees untorn 128b reads except for completing a 128b load/store exclusive pair (ldxp/stxp) or compare-and-swap (casp) successfully. The requirement to complete a 128b read+write atomic is actually more expensive and more unfair than the previous implementation of monotonize() which used a Mutex on aarch64, especially at large core counts. For aarch64 switch to the 64b atomic implementation which is about 13x faster for a benchmark that involves many calls to Instant::now(). --- library/std/src/time/monotonic.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/time/monotonic.rs b/library/std/src/time/monotonic.rs index fa96b7abff6d6..5783fa760dff8 100644 --- a/library/std/src/time/monotonic.rs +++ b/library/std/src/time/monotonic.rs @@ -5,7 +5,7 @@ pub(super) fn monotonize(raw: time::Instant) -> time::Instant { inner::monotonize(raw) } -#[cfg(all(target_has_atomic = "64", not(target_has_atomic = "128")))] +#[cfg(any(all(target_has_atomic = "64", not(target_has_atomic = "128")), target_arch = "aarch64"))] pub mod inner { use crate::sync::atomic::AtomicU64; use crate::sync::atomic::Ordering::*; @@ -70,7 +70,7 @@ pub mod inner { } } -#[cfg(target_has_atomic = "128")] +#[cfg(all(target_has_atomic = "128", not(target_arch = "aarch64")))] pub mod inner { use crate::sync::atomic::AtomicU128; use crate::sync::atomic::Ordering::*; From 7c707e4b9535b70de8c6beac24b7bc05595b1932 Mon Sep 17 00:00:00 2001 From: Commeownist Date: Sun, 5 Sep 2021 18:26:01 +0300 Subject: [PATCH 072/621] Implement basic inline asm support (#72) * Implement basic support for inline assembly * Disable LTO We don't support it yet at all * Handle `inout(reg) var` correctly Turns out that `+` readwrite output registers cannot be tied with input variables. * Add limited support for llvm_asm! * Handle CHANNEL correctly * Add support for arbitrary explicit registers * Handle symbols properly * Add rudimentary asm tests * Exclude llvm_asm! tests from tests runs * Insert `__builtin_unreachable()` after diverging asm blocks --- .gitignore | 2 + Cargo.lock | 4 +- build.sh | 7 +- cargo.sh | 2 +- config.sh | 9 +- src/asm.rs | 724 +++++++++++++++++++++++++++++++++-------------- test.sh | 9 +- tests/run/asm.rs | 87 ++++++ 8 files changed, 631 insertions(+), 213 deletions(-) diff --git a/.gitignore b/.gitignore index 573ed83e53566..1e2f9e3aebb2c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,9 +7,11 @@ perf.data.old *.events *.string* /build_sysroot/sysroot +/build_sysroot/sysroot_src /build_sysroot/Cargo.lock /build_sysroot/test_target/Cargo.lock /rust +/simple-raytracer /regex gimple* *asm diff --git a/Cargo.lock b/Cargo.lock index 952c03b05c7f4..f3e07fd08ee8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -56,7 +56,7 @@ dependencies = [ [[package]] name = "gccjit" version = "1.0.0" -source = "git+https://github.com/antoyo/gccjit.rs#0572117c7ffdfcb0e6c6526d45266c3f34796bea" +source = "git+https://github.com/antoyo/gccjit.rs#54be27e41fff7b6ab532e2e21a82df50a12b9ad3" dependencies = [ "gccjit_sys", ] @@ -64,7 +64,7 @@ dependencies = [ [[package]] name = "gccjit_sys" version = "0.0.1" -source = "git+https://github.com/antoyo/gccjit.rs#0572117c7ffdfcb0e6c6526d45266c3f34796bea" +source = "git+https://github.com/antoyo/gccjit.rs#54be27e41fff7b6ab532e2e21a82df50a12b9ad3" dependencies = [ "libc 0.1.12", ] diff --git a/build.sh b/build.sh index 9f1228687e27d..17a0d2ab3f060 100755 --- a/build.sh +++ b/build.sh @@ -3,7 +3,12 @@ #set -x set -e -export GCC_PATH=$(cat gcc_path) +if [ -f ./gcc_path ]; then + export GCC_PATH=$(cat gcc_path) +else + echo 'Please put the path to your custom build of libgccjit in the file `gcc_path`, see Readme.md for details' + exit 1 +fi export LD_LIBRARY_PATH="$GCC_PATH" export LIBRARY_PATH="$GCC_PATH" diff --git a/cargo.sh b/cargo.sh index a85865019c135..1001c522052c8 100755 --- a/cargo.sh +++ b/cargo.sh @@ -20,4 +20,4 @@ fi cmd=$1 shift -RUSTDOCFLAGS=$RUSTFLAGS cargo +${TOOLCHAIN} $cmd --target $TARGET_TRIPLE $@ +RUSTDOCFLAGS="$RUSTFLAGS" cargo +${TOOLCHAIN} $cmd --target $TARGET_TRIPLE $@ diff --git a/config.sh b/config.sh index bd2d915f589a0..98caeb7407e08 100644 --- a/config.sh +++ b/config.sh @@ -2,7 +2,12 @@ set -e export CARGO_INCREMENTAL=0 -export GCC_PATH=$(cat gcc_path) +if [ -f ./gcc_path ]; then + export GCC_PATH=$(cat gcc_path) +else + echo 'Please put the path to your custom build of libgccjit in the file `gcc_path`, see Readme.md for details' + exit 1 +fi unamestr=`uname` if [[ "$unamestr" == 'Linux' ]]; then @@ -30,7 +35,7 @@ if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then fi fi -export RUSTFLAGS=$linker' -Cpanic=abort -Cdebuginfo=2 -Zpanic-abort-tests -Zcodegen-backend='$(pwd)'/target/'$CHANNEL'/librustc_codegen_gcc.'$dylib_ext' --sysroot '$(pwd)'/build_sysroot/sysroot' +export RUSTFLAGS="$linker -Cpanic=abort -Cdebuginfo=2 -Clto=off -Zpanic-abort-tests -Zcodegen-backend=$(pwd)/target/${CHANNEL:-debug}/librustc_codegen_gcc.$dylib_ext --sysroot $(pwd)/build_sysroot/sysroot" # FIXME(antoyo): remove once the atomic shim is gone if [[ `uname` == 'Darwin' ]]; then diff --git a/src/asm.rs b/src/asm.rs index e4d57c39de48a..512b9f553f5e3 100644 --- a/src/asm.rs +++ b/src/asm.rs @@ -1,254 +1,558 @@ -use gccjit::{RValue, ToRValue, Type}; +use gccjit::{LValue, RValue, ToRValue, Type}; use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece}; use rustc_codegen_ssa::mir::operand::OperandValue; use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::{AsmBuilderMethods, AsmMethods, BaseTypeMethods, BuilderMethods, GlobalAsmOperandRef, InlineAsmOperandRef}; -use rustc_data_structures::fx::FxHashMap; + use rustc_hir::LlvmInlineAsmInner; -use rustc_middle::bug; +use rustc_middle::{bug, ty::Instance}; use rustc_span::Span; use rustc_target::asm::*; +use std::borrow::Cow; + use crate::builder::Builder; use crate::context::CodegenCx; use crate::type_of::LayoutGccExt; + +// Rust asm! and GCC Extended Asm semantics differ substantially. +// +// 1. Rust asm operands go along as one list of operands. Operands themselves indicate +// if they're "in" or "out". "In" and "out" operands can interleave. One operand can be +// both "in" and "out" (`inout(reg)`). +// +// GCC asm has two different lists for "in" and "out" operands. In terms of gccjit, +// this means that all "out" operands must go before "in" operands. "In" and "out" operands +// cannot interleave. +// +// 2. Operand lists in both Rust and GCC are indexed. Index starts from 0. Indexes are important +// because the asm template refers to operands by index. +// +// Mapping from Rust to GCC index would be 1-1 if it wasn't for... +// +// 3. Clobbers. GCC has a separate list of clobbers, and clobbers don't have indexes. +// Contrary, Rust expresses clobbers through "out" operands that aren't tied to +// a variable (`_`), and such "clobbers" do have index. +// +// 4. Furthermore, GCC Extended Asm does not support explicit register constraints +// (like `out("eax")`) directly, offering so-called "local register variables" +// as a workaround. These variables need to be declared and initialized *before* +// the Extended Asm block but *after* normal local variables +// (see comment in `codegen_inline_asm` for explanation). +// +// With that in mind, let's see how we translate Rust syntax to GCC +// (from now on, `CC` stands for "constraint code"): +// +// * `out(reg_class) var` -> translated to output operand: `"=CC"(var)` +// * `inout(reg_class) var` -> translated to output operand: `"+CC"(var)` +// * `in(reg_class) var` -> translated to input operand: `"CC"(var)` +// +// * `out(reg_class) _` -> translated to one `=r(tmp)`, where "tmp" is a temporary unused variable +// +// * `out("explicit register") _` -> not translated to any operands, register is simply added to clobbers list +// +// * `inout(reg_class) in_var => out_var` -> translated to two operands: +// output: `"=CC"(in_var)` +// input: `"num"(out_var)` where num is the GCC index +// of the corresponding output operand +// +// * `inout(reg_class) in_var => _` -> same as `inout(reg_class) in_var => tmp`, +// where "tmp" is a temporary unused variable +// +// * `out/in/inout("explicit register") var` -> translated to one or two operands as described above +// with `"r"(var)` constraint, +// and one register variable assigned to the desired register. +// + +const ATT_SYNTAX_INS: &str = ".att_syntax noprefix\n\t"; +const INTEL_SYNTAX_INS: &str = "\n\t.intel_syntax noprefix"; + + +struct AsmOutOperand<'a, 'tcx, 'gcc> { + rust_idx: usize, + constraint: &'a str, + late: bool, + readwrite: bool, + + tmp_var: LValue<'gcc>, + out_place: Option>> +} + +struct AsmInOperand<'a, 'tcx> { + rust_idx: usize, + constraint: Cow<'a, str>, + val: RValue<'tcx> +} + +impl AsmOutOperand<'_, '_, '_> { + fn to_constraint(&self) -> String { + let mut res = String::with_capacity(self.constraint.len() + self.late as usize + 1); + + let sign = if self.readwrite { '+' } else { '=' }; + res.push(sign); + if !self.late { + res.push('&'); + } + + res.push_str(&self.constraint); + res + } +} + +enum ConstraintOrRegister { + Constraint(&'static str), + Register(&'static str) +} + + impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { - fn codegen_llvm_inline_asm(&mut self, _ia: &LlvmInlineAsmInner, _outputs: Vec>>, mut _inputs: Vec>, _span: Span) -> bool { - // TODO(antoyo) - return true; + fn codegen_llvm_inline_asm(&mut self, ia: &LlvmInlineAsmInner, outputs: Vec>>, inputs: Vec>, span: Span) -> bool { + if ia.asm.as_str().is_empty() && outputs.is_empty() { + // TODO(@Commeownist): there's one use of `llvm_asm` in rustc sysroot we can't get rid of just yet. + // Fortunately, it's used as a simple black box to make sure that inputs are not optimized away. + // Let's just emulate it. + let block = self.llbb(); + let extended_asm = block.add_extended_asm(None, ""); + for input in inputs { + extended_asm.add_input_operand(None, "r", input); + } + extended_asm.add_clobber("memory"); + extended_asm.set_volatile_flag(true); + } + else { + // TODO(@Commeownist): switch to `struct_span_err_with_code` + // once we get this merged into rustc + self.sess().struct_span_err(span, "GCC backend does not support `llvm_asm!`") + .help("consider using the `asm!` macro instead") + .emit(); + } + + // We return `true` even if we've failed to generate the asm + // because we want to suppress the "malformed inline assembly" error + // generated by the frontend. + true } - fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, _span: &[Span]) { + fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, _span: &[Span]) { let asm_arch = self.tcx.sess.asm_arch.unwrap(); + let is_x86 = matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64); + let att_dialect = is_x86 && options.contains(InlineAsmOptions::ATT_SYNTAX); + let intel_dialect = is_x86 && !options.contains(InlineAsmOptions::ATT_SYNTAX); - let intel_dialect = - match asm_arch { - InlineAsmArch::X86 | InlineAsmArch::X86_64 if !options.contains(InlineAsmOptions::ATT_SYNTAX) => true, - _ => false, - }; + // GCC index of an output operand equals its position in the array + let mut outputs = vec![]; + + // GCC index of an input operand equals its position in the array + // added to `outputs.len()` + let mut inputs = vec![]; - // Collect the types of output operands - // FIXME(antoyo): we do this here instead of later because of a bug in libgccjit where creating the - // variable after the extended asm expression causes a segfault: - // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100380 - let mut output_vars = FxHashMap::default(); - let mut operand_numbers = FxHashMap::default(); - let mut current_number = 0; - for (idx, op) in operands.iter().enumerate() { + // Clobbers collected from `out("explicit register") _` and `inout("expl_reg") var => _` + let mut clobbers = vec![]; + + // We're trying to preallocate space for the template + let mut constants_len = 0; + + // There are rules we must adhere to if we want GCC to do the right thing: + // + // * Every local variable that the asm block uses as an output must be declared *before* + // the asm block. + // * There must be no instructions whatsoever between the register variables and the asm. + // + // Therefore, the backend must generate the instructions strictly in this order: + // + // 1. Output variables. + // 2. Register variables. + // 3. The asm block. + // + // We also must make sure that no input operands are emitted before output operands. + // + // This is why we work in passes, first emitting local vars, then local register vars. + // Also, we don't emit any asm operands immediately; we save them to + // the one of the buffers to be emitted later. + + // 1. Normal variables (and saving operands to buffers). + for (rust_idx, op) in rust_operands.iter().enumerate() { match *op { - InlineAsmOperandRef::Out { place, .. } => { - let ty = - match place { - Some(place) => place.layout.gcc_type(self.cx, false), - None => { - // If the output is discarded, we don't really care what - // type is used. We're just using this to tell GCC to - // reserve the register. - //dummy_output_type(self.cx, reg.reg_class()) - - // NOTE: if no output value, we should not create one (it will be a - // clobber). - continue; - }, - }; - let var = self.current_func().new_local(None, ty, "output_register"); - operand_numbers.insert(idx, current_number); - current_number += 1; - output_vars.insert(idx, var); + InlineAsmOperandRef::Out { reg, late, place } => { + use ConstraintOrRegister::*; + + let (constraint, ty) = match (reg_to_gcc(reg), place) { + (Constraint(constraint), Some(place)) => (constraint, place.layout.gcc_type(self.cx, false)), + // When `reg` is a class and not an explicit register but the out place is not specified, + // we need to create an unused output variable to assign the output to. This var + // needs to be of a type that's "compatible" with the register class, but specific type + // doesn't matter. + (Constraint(constraint), None) => (constraint, dummy_output_type(self.cx, reg.reg_class())), + (Register(_), Some(_)) => { + // left for the next pass + continue + }, + (Register(reg_name), None) => { + clobbers.push(reg_name); + continue + } + }; + + let tmp_var = self.current_func().new_local(None, ty, "output_register"); + outputs.push(AsmOutOperand { + constraint, + rust_idx, + late, + readwrite: false, + tmp_var, + out_place: place + }); } - InlineAsmOperandRef::InOut { out_place, .. } => { - let ty = - match out_place { - Some(place) => place.layout.gcc_type(self.cx, false), - None => { - // NOTE: if no output value, we should not create one. - continue; - }, - }; - operand_numbers.insert(idx, current_number); - current_number += 1; - let var = self.current_func().new_local(None, ty, "output_register"); - output_vars.insert(idx, var); + + InlineAsmOperandRef::In { reg, value } => { + if let ConstraintOrRegister::Constraint(constraint) = reg_to_gcc(reg) { + inputs.push(AsmInOperand { + constraint: Cow::Borrowed(constraint), + rust_idx, + val: value.immediate() + }); + } + else { + // left for the next pass + continue + } + } + + InlineAsmOperandRef::InOut { reg, late, in_value, out_place } => { + let constraint = if let ConstraintOrRegister::Constraint(constraint) = reg_to_gcc(reg) { + constraint + } + else { + // left for the next pass + continue + }; + + // Rustc frontend guarantees that input and output types are "compatible", + // so we can just use input var's type for the output variable. + // + // This decision is also backed by the fact that LLVM needs in and out + // values to be of *exactly the same type*, not just "compatible". + // I'm not sure if GCC is so picky too, but better safe than sorry. + let ty = in_value.layout.gcc_type(self.cx, false); + let tmp_var = self.current_func().new_local(None, ty, "output_register"); + + // If the out_place is None (i.e `inout(reg) _` syntax was used), we translate + // it to one "readwrite (+) output variable", otherwise we translate it to two + // "out and tied in" vars as described above. + let readwrite = out_place.is_none(); + outputs.push(AsmOutOperand { + constraint, + rust_idx, + late, + readwrite, + tmp_var, + out_place, + }); + + if !readwrite { + let out_gcc_idx = outputs.len() - 1; + let constraint = Cow::Owned(out_gcc_idx.to_string()); + + inputs.push(AsmInOperand { + constraint, + rust_idx, + val: in_value.immediate() + }); + } + } + + InlineAsmOperandRef::Const { ref string } => { + constants_len += string.len() + att_dialect as usize; + } + + InlineAsmOperandRef::SymFn { instance } => { + constants_len += self.tcx.symbol_name(instance).name.len(); + } + InlineAsmOperandRef::SymStatic { def_id } => { + constants_len += self.tcx.symbol_name(Instance::mono(self.tcx, def_id)).name.len(); } - _ => {} } } - // All output operands must come before the input operands, hence the 2 loops. - for (idx, op) in operands.iter().enumerate() { + // 2. Register variables. + for (rust_idx, op) in rust_operands.iter().enumerate() { match *op { - InlineAsmOperandRef::In { .. } | InlineAsmOperandRef::InOut { .. } => { - operand_numbers.insert(idx, current_number); - current_number += 1; - }, - _ => (), + // `out("explicit register") var` + InlineAsmOperandRef::Out { reg, late, place } => { + if let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) { + let out_place = if let Some(place) = place { + place + } + else { + // processed in the previous pass + continue + }; + + let ty = out_place.layout.gcc_type(self.cx, false); + let tmp_var = self.current_func().new_local(None, ty, "output_register"); + tmp_var.set_register_name(reg_name); + + outputs.push(AsmOutOperand { + constraint: "r".into(), + rust_idx, + late, + readwrite: false, + tmp_var, + out_place: Some(out_place) + }); + } + + // processed in the previous pass + } + + // `in("explicit register") var` + InlineAsmOperandRef::In { reg, value } => { + if let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) { + let ty = value.layout.gcc_type(self.cx, false); + let reg_var = self.current_func().new_local(None, ty, "input_register"); + reg_var.set_register_name(reg_name); + self.llbb().add_assignment(None, reg_var, value.immediate()); + + inputs.push(AsmInOperand { + constraint: "r".into(), + rust_idx, + val: reg_var.to_rvalue() + }); + } + + // processed in the previous pass + } + + // `inout("explicit register") in_var => out_var` + InlineAsmOperandRef::InOut { reg, late, in_value, out_place } => { + if let ConstraintOrRegister::Register(reg_name) = reg_to_gcc(reg) { + let out_place = if let Some(place) = out_place { + place + } + else { + // processed in the previous pass + continue + }; + + // See explanation in the first pass. + let ty = in_value.layout.gcc_type(self.cx, false); + let tmp_var = self.current_func().new_local(None, ty, "output_register"); + tmp_var.set_register_name(reg_name); + + outputs.push(AsmOutOperand { + constraint: "r".into(), + rust_idx, + late, + readwrite: false, + tmp_var, + out_place: Some(out_place) + }); + + let constraint = Cow::Owned((outputs.len() - 1).to_string()); + inputs.push(AsmInOperand { + constraint, + rust_idx, + val: in_value.immediate() + }); + } + + // processed in the previous pass + } + + InlineAsmOperandRef::Const { .. } + | InlineAsmOperandRef::SymFn { .. } + | InlineAsmOperandRef::SymStatic { .. } => { + // processed in the previous pass + } } } - // Build the template string - let mut template_str = String::new(); + // 3. Build the template string + + let mut template_str = String::with_capacity(estimate_template_length(template, constants_len, att_dialect)); + if !intel_dialect { + template_str.push_str(ATT_SYNTAX_INS); + } + for piece in template { match *piece { InlineAsmTemplatePiece::String(ref string) => { - if string.contains('%') { - for c in string.chars() { - if c == '%' { - template_str.push_str("%%"); - } - else { - template_str.push(c); - } - } + // TODO(@Commeownist): switch to `Iterator::intersperse` once it's stable + let mut iter = string.split('%'); + if let Some(s) = iter.next() { + template_str.push_str(s); } - else { - template_str.push_str(string) + + for s in iter { + template_str.push_str("%%"); + template_str.push_str(s); } } InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span: _ } => { - match operands[operand_idx] { - InlineAsmOperandRef::Out { reg, place: Some(_), .. } => { + let mut push_to_template = |modifier, gcc_idx| { + use std::fmt::Write; + + template_str.push('%'); + if let Some(modifier) = modifier { + template_str.push(modifier); + } + write!(template_str, "{}", gcc_idx).expect("pushing to string failed"); + }; + + match rust_operands[operand_idx] { + InlineAsmOperandRef::Out { reg, .. } => { let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier); - if let Some(modifier) = modifier { - template_str.push_str(&format!("%{}{}", modifier, operand_numbers[&operand_idx])); - } else { - template_str.push_str(&format!("%{}", operand_numbers[&operand_idx])); - } - }, - InlineAsmOperandRef::Out { place: None, .. } => { - unimplemented!("Out None"); - }, - InlineAsmOperandRef::In { reg, .. } - | InlineAsmOperandRef::InOut { reg, .. } => { + let gcc_index = outputs.iter() + .position(|op| operand_idx == op.rust_idx) + .expect("wrong rust index"); + push_to_template(modifier, gcc_index); + } + + InlineAsmOperandRef::In { reg, .. } => { let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier); - if let Some(modifier) = modifier { - template_str.push_str(&format!("%{}{}", modifier, operand_numbers[&operand_idx])); - } else { - template_str.push_str(&format!("%{}", operand_numbers[&operand_idx])); - } + let in_gcc_index = inputs.iter() + .position(|op| operand_idx == op.rust_idx) + .expect("wrong rust index"); + let gcc_index = in_gcc_index + outputs.len(); + push_to_template(modifier, gcc_index); + } + + InlineAsmOperandRef::InOut { reg, .. } => { + let modifier = modifier_to_gcc(asm_arch, reg.reg_class(), modifier); + + // The input register is tied to the output, so we can just use the index of the output register + let gcc_index = outputs.iter() + .position(|op| operand_idx == op.rust_idx) + .expect("wrong rust index"); + push_to_template(modifier, gcc_index); } + + InlineAsmOperandRef::SymFn { instance } => { + let name = self.tcx.symbol_name(instance).name; + template_str.push_str(name); + } + + InlineAsmOperandRef::SymStatic { def_id } => { + // TODO(@Commeownist): This may not be sufficient for all kinds of statics. + // Some statics may need the `@plt` suffix, like thread-local vars. + let instance = Instance::mono(self.tcx, def_id); + let name = self.tcx.symbol_name(instance).name; + template_str.push_str(name); + } + InlineAsmOperandRef::Const { ref string } => { // Const operands get injected directly into the template + if att_dialect { + template_str.push('$'); + } template_str.push_str(string); } - InlineAsmOperandRef::SymFn { .. } - | InlineAsmOperandRef::SymStatic { .. } => { - unimplemented!(); - // Only emit the raw symbol name - //template_str.push_str(&format!("${{{}:c}}", op_idx[&operand_idx])); - } } } } } + if !intel_dialect { + template_str.push_str(INTEL_SYNTAX_INS); + } + + // 4. Generate Extended Asm block + let block = self.llbb(); - let template_str = - if intel_dialect { - template_str - } - else { - // FIXME(antoyo): this might break the "m" memory constraint: - // https://stackoverflow.com/a/9347957/389119 - // TODO(antoyo): only set on x86 platforms. - format!(".att_syntax noprefix\n\t{}\n\t.intel_syntax noprefix", template_str) - }; let extended_asm = block.add_extended_asm(None, &template_str); - // Collect the types of output operands - let mut output_types = vec![]; - for (idx, op) in operands.iter().enumerate() { - match *op { - InlineAsmOperandRef::Out { reg, late, place } => { - let ty = - match place { - Some(place) => place.layout.gcc_type(self.cx, false), - None => { - // If the output is discarded, we don't really care what - // type is used. We're just using this to tell GCC to - // reserve the register. - dummy_output_type(self.cx, reg.reg_class()) - }, - }; - output_types.push(ty); - let prefix = if late { "=" } else { "=&" }; - let constraint = format!("{}{}", prefix, reg_to_gcc(reg)); + for op in &outputs { + extended_asm.add_output_operand(None, &op.to_constraint(), op.tmp_var); + } - if place.is_some() { - let var = output_vars[&idx]; - extended_asm.add_output_operand(None, &constraint, var); - } - else { - // NOTE: reg.to_string() returns the register name with quotes around it so - // remove them. - extended_asm.add_clobber(reg.to_string().trim_matches('"')); - } - } - InlineAsmOperandRef::InOut { reg, late, in_value, out_place } => { - let ty = - match out_place { - Some(out_place) => out_place.layout.gcc_type(self.cx, false), - None => dummy_output_type(self.cx, reg.reg_class()) - }; - output_types.push(ty); - // TODO(antoyo): prefix of "+" for reading and writing? - let prefix = if late { "=" } else { "=&" }; - let constraint = format!("{}{}", prefix, reg_to_gcc(reg)); - - if out_place.is_some() { - let var = output_vars[&idx]; - // TODO(antoyo): also specify an output operand when out_place is none: that would - // be the clobber but clobbers do not support general constraint like reg; - // they only support named registers. - // Not sure how we can do this. And the LLVM backend does not seem to add a - // clobber. - extended_asm.add_output_operand(None, &constraint, var); - } + for op in &inputs { + extended_asm.add_input_operand(None, &op.constraint, op.val); + } - let constraint = reg_to_gcc(reg); - extended_asm.add_input_operand(None, &constraint, in_value.immediate()); - } - InlineAsmOperandRef::In { reg, value } => { - let constraint = reg_to_gcc(reg); - extended_asm.add_input_operand(None, &constraint, value.immediate()); - } - _ => {} + for clobber in clobbers.iter() { + extended_asm.add_clobber(clobber); + } + + if !options.contains(InlineAsmOptions::PRESERVES_FLAGS) { + // TODO(@Commeownist): I'm not 100% sure this one clobber is sufficient + // on all architectures. For instance, what about FP stack? + extended_asm.add_clobber("cc"); + } + if !options.contains(InlineAsmOptions::NOMEM) { + extended_asm.add_clobber("memory"); + } + if !options.contains(InlineAsmOptions::PURE) { + extended_asm.set_volatile_flag(true); + } + if !options.contains(InlineAsmOptions::NOSTACK) { + // TODO(@Commeownist): figure out how to align stack + } + if options.contains(InlineAsmOptions::NORETURN) { + let builtin_unreachable = self.context.get_builtin_function("__builtin_unreachable"); + let builtin_unreachable: RValue<'gcc> = unsafe { std::mem::transmute(builtin_unreachable) }; + self.call(self.type_void(), builtin_unreachable, &[], None); + } + + // Write results to outputs. + // + // We need to do this because: + // 1. Turning `PlaceRef` into `RValue` is error-prone and has nasty edge cases + // (especially with current `rustc_backend_ssa` API). + // 2. Not every output operand has an `out_place`, and it's required by `add_output_operand`. + // + // Instead, we generate a temporary output variable for each output operand, and then this loop, + // generates `out_place = tmp_var;` assignments if out_place exists. + for op in &outputs { + if let Some(place) = op.out_place { + OperandValue::Immediate(op.tmp_var.to_rvalue()).store(self, place); } } - // Write results to outputs - for (idx, op) in operands.iter().enumerate() { - if let InlineAsmOperandRef::Out { place: Some(place), .. } - | InlineAsmOperandRef::InOut { out_place: Some(place), .. } = *op - { - OperandValue::Immediate(output_vars[&idx].to_rvalue()).store(self, place); + } +} + +fn estimate_template_length(template: &[InlineAsmTemplatePiece], constants_len: usize, att_dialect: bool) -> usize { + let len: usize = template.iter().map(|piece| { + match *piece { + InlineAsmTemplatePiece::String(ref string) => { + string.len() + } + InlineAsmTemplatePiece::Placeholder { .. } => { + // '%' + 1 char modifier + 1 char index + 3 } } + }) + .sum(); + + // increase it by 5% to account for possible '%' signs that'll be duplicated + // I pulled the number out of blue, but should be fair enough + // as the upper bound + let mut res = (len as f32 * 1.05) as usize + constants_len; + + if att_dialect { + res += INTEL_SYNTAX_INS.len() + ATT_SYNTAX_INS.len(); } + res } /// Converts a register class to a GCC constraint code. -// TODO(antoyo): return &'static str instead? -fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> String { - match reg { +fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister { + let constraint = match reg { // For vector registers LLVM wants the register name to match the type size. InlineAsmRegOrRegClass::Reg(reg) => { // TODO(antoyo): add support for vector register. - let constraint = - match reg.name() { - "ax" => "a", - "bx" => "b", - "cx" => "c", - "dx" => "d", - "si" => "S", - "di" => "D", - // TODO(antoyo): for registers like r11, we have to create a register variable: https://stackoverflow.com/a/31774784/389119 - // TODO(antoyo): in this case though, it's a clobber, so it should work as r11. - // Recent nightly supports clobber() syntax, so update to it. It does not seem - // like it's implemented yet. - name => name, // FIXME(antoyo): probably wrong. - }; - constraint.to_string() + match reg.name() { + "ax" => "a", + "bx" => "b", + "cx" => "c", + "dx" => "d", + "si" => "S", + "di" => "D", + // For registers like r11, we have to create a register variable: https://stackoverflow.com/a/31774784/389119 + name => return ConstraintOrRegister::Register(name), + } }, InlineAsmRegOrRegClass::RegClass(reg) => match reg { InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => unimplemented!(), @@ -278,23 +582,25 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> String { InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) => unimplemented!(), InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => unimplemented!(), InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => unimplemented!(), - InlineAsmRegClass::X86(X86InlineAsmRegClass::mmx_reg) => unimplemented!(), InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) => "r", - InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => unimplemented!(), - InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => unimplemented!(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => "Q", + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => "q", InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg) - | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => unimplemented!(), - InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg) => unimplemented!(), - InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => unimplemented!(), + | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) => "x", + InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v", InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => unimplemented!(), InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(), + InlineAsmRegClass::X86( + X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg, + ) => unreachable!("clobber-only"), InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("GCC backend does not support SPIR-V") } InlineAsmRegClass::Err => unreachable!(), } - .to_string(), - } + }; + + ConstraintOrRegister::Constraint(constraint) } /// Type to use for outputs that are discarded. It doesn't really matter what @@ -379,7 +685,7 @@ impl<'gcc, 'tcx> AsmMethods for CodegenCx<'gcc, 'tcx> { match operands[operand_idx] { GlobalAsmOperandRef::Const { ref string } => { // Const operands get injected directly into the - // template. Note that we don't need to escape $ + // template. Note that we don't need to escape % // here unlike normal inline assembly. template_str.push_str(string); } @@ -431,8 +737,7 @@ fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => unimplemented!(), InlineAsmRegClass::X86(X86InlineAsmRegClass::reg) | InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_abcd) => match modifier { - None if arch == InlineAsmArch::X86_64 => Some('q'), - None => Some('k'), + None => if arch == InlineAsmArch::X86_64 { Some('q') } else { Some('k') }, Some('l') => Some('b'), Some('h') => Some('h'), Some('x') => Some('w'), @@ -440,13 +745,22 @@ fn modifier_to_gcc(arch: InlineAsmArch, reg: InlineAsmRegClass, modifier: Option Some('r') => Some('q'), _ => unreachable!(), }, - InlineAsmRegClass::X86(X86InlineAsmRegClass::mmx_reg) => unimplemented!(), - InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => unimplemented!(), - InlineAsmRegClass::X86(X86InlineAsmRegClass::xmm_reg) - | InlineAsmRegClass::X86(X86InlineAsmRegClass::ymm_reg) - | InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => unimplemented!(), - InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg) => unimplemented!(), - InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => unimplemented!(), + InlineAsmRegClass::X86(X86InlineAsmRegClass::reg_byte) => None, + InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::xmm_reg) + | InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::ymm_reg) + | InlineAsmRegClass::X86(reg @ X86InlineAsmRegClass::zmm_reg) => match (reg, modifier) { + (X86InlineAsmRegClass::xmm_reg, None) => Some('x'), + (X86InlineAsmRegClass::ymm_reg, None) => Some('t'), + (X86InlineAsmRegClass::zmm_reg, None) => Some('g'), + (_, Some('x')) => Some('x'), + (_, Some('y')) => Some('t'), + (_, Some('z')) => Some('g'), + _ => unreachable!(), + }, + InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => None, + InlineAsmRegClass::X86(X86InlineAsmRegClass::x87_reg | X86InlineAsmRegClass::mmx_reg) => { + unreachable!("clobber-only") + } InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => unimplemented!(), InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("LLVM backend does not support SPIR-V") diff --git a/test.sh b/test.sh index 06faa98d9b1f0..114db7d53f5bb 100755 --- a/test.sh +++ b/test.sh @@ -4,7 +4,12 @@ set -e -export GCC_PATH=$(cat gcc_path) +if [ -f ./gcc_path ]; then + export GCC_PATH=$(cat gcc_path) +else + echo 'Please put the path to your custom build of libgccjit in the file `gcc_path`, see Readme.md for details' + exit 1 +fi export LD_LIBRARY_PATH="$GCC_PATH" export LIBRARY_PATH="$GCC_PATH" @@ -157,7 +162,7 @@ done git checkout -- src/test/ui/issues/auxiliary/issue-3136-a.rs # contains //~ERROR, but shouldn't be removed -rm -r src/test/ui/{abi*,extern/,panic-runtime/,panics/,unsized-locals/,proc-macro/,threads-sendsync/,thinlto/,simd*,borrowck/,test*,*lto*.rs} || true +rm -r src/test/ui/{abi*,extern/,llvm-asm/,panic-runtime/,panics/,unsized-locals/,proc-macro/,threads-sendsync/,thinlto/,simd*,borrowck/,test*,*lto*.rs} || true for test in $(rg --files-with-matches "catch_unwind|should_panic|thread|lto" src/test/ui); do rm $test done diff --git a/tests/run/asm.rs b/tests/run/asm.rs index bd76153e047e4..9c0055b0b6b5e 100644 --- a/tests/run/asm.rs +++ b/tests/run/asm.rs @@ -62,5 +62,92 @@ fn main() { } assert_eq!(x, 43); + // check inout(reg_class) x + let mut x: u64 = 42; + unsafe { + asm!("add {0}, {0}", + inout(reg) x + ); + } + assert_eq!(x, 84); + + // check inout("reg") x + let mut x: u64 = 42; + unsafe { + asm!("add r11, r11", + inout("r11") x + ); + } + assert_eq!(x, 84); + + // check a mix of + // in("reg") + // inout(class) x => y + // inout (class) x + let x: u64 = 702; + let y: u64 = 100; + let res: u64; + let mut rem: u64 = 0; + unsafe { + asm!("div r11", + in("r11") y, + inout("eax") x => res, + inout("edx") rem, + ); + } + assert_eq!(res, 7); + assert_eq!(rem, 2); + + // check const + let mut x: u64 = 42; + unsafe { + asm!("add {}, {}", + inout(reg) x, + const 1 + ); + } + assert_eq!(x, 43); + + // check const (ATT syntax) + let mut x: u64 = 42; + unsafe { + asm!("add {}, {}", + const 1, + inout(reg) x, + options(att_syntax) + ); + } + assert_eq!(x, 43); + + // check sym fn + extern "C" fn foo() -> u64 { 42 } + let x: u64; + unsafe { + asm!("call {}", sym foo, lateout("rax") x); + } + assert_eq!(x, 42); + + // check sym fn (ATT syntax) + let x: u64; + unsafe { + asm!("call {}", sym foo, lateout("rax") x, options(att_syntax)); + } + assert_eq!(x, 42); + + // check sym static + static FOO: u64 = 42; + let x: u64; + unsafe { + asm!("mov {1}, qword ptr [rip + {0}]", sym FOO, lateout(reg) x); + } + assert_eq!(x, 42); + + // check sym static (ATT syntax) + let x: u64; + unsafe { + asm!("movq {0}(%rip), {1}", sym FOO, lateout(reg) x, options(att_syntax)); + } + assert_eq!(x, 42); + assert_eq!(unsafe { add_asm(40, 2) }, 42); } From f63096e4f2a08e80eaa4d564946d4fa4d363fc44 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Sun, 5 Sep 2021 22:55:56 +0100 Subject: [PATCH 073/621] rust fmt --- library/std/src/sync/mpsc/shared.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/std/src/sync/mpsc/shared.rs b/library/std/src/sync/mpsc/shared.rs index d4ee1b414d9b0..619a79f437b8a 100644 --- a/library/std/src/sync/mpsc/shared.rs +++ b/library/std/src/sync/mpsc/shared.rs @@ -248,7 +248,11 @@ impl Packet { // Returns true if blocking should proceed. fn decrement(&self, token: SignalToken) -> StartResult { unsafe { - assert_eq!(self.to_wake.load(Ordering::SeqCst), 0, "This is a known bug in rust. See https://github.com/rust-lang/rust/issues/39364"); + assert_eq!( + self.to_wake.load(Ordering::SeqCst), + 0, + "This is a known bug in rust. See https://github.com/rust-lang/rust/issues/39364" + ); let ptr = token.cast_to_usize(); self.to_wake.store(ptr, Ordering::SeqCst); From 734209ed73507e214e49ef5cb7988af8901e74c7 Mon Sep 17 00:00:00 2001 From: ThePuzzlemaker Date: Mon, 6 Sep 2021 16:11:28 -0500 Subject: [PATCH 074/621] Normalize assoc types when checking ret ty of main This fixes #88609. Previously, the return type of `fn main()` would not have any associated type projections within normalized before checking if it implements the standard library trait `std::process::Termination`. This commit appears to fix it. This feels vaguely symptomatic of a problem in the underlying trait solving engine, but I am not sure how I would solve that. I am unsure why the example in #88609 with `assert_impl_termination` and `fn foo()` work, but simply `fn main()` doesn't. The way that I solved this is also probably not the best way to do this, so please let me know if there is a better way to do this. I have added a build-pass regression test for this issue. --- compiler/rustc_typeck/src/lib.rs | 18 +++++++++++++++++- src/test/ui/typeck/issue-88609.rs | 19 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/typeck/issue-88609.rs diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index 749f681e92ed1..e5d5ca3362bf4 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -109,6 +109,7 @@ use rustc_middle::util; use rustc_session::config::EntryFnType; use rustc_span::{symbol::sym, Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; use rustc_trait_selection::traits::{ self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt as _, @@ -328,7 +329,22 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { ObligationCauseCode::MainFunctionType, ); let mut fulfillment_cx = traits::FulfillmentContext::new(); - fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), return_ty, term_id, cause); + // normalize any potential projections in the return type, then add + // any possible obligations to the fulfillment context. + // HACK(ThePuzzlemaker) this feels symptomatic of a problem within + // checking trait fulfillment, not this here. I'm not sure why it + // works in the example in `fn test()` given in #88609? This also + // probably isn't the best way to do this. + let normalized = infcx.partially_normalize_associated_types_in( + cause.clone(), + ty::ParamEnv::empty(), + return_ty, + ); + let new_ty = normalized.value; + for obligation in normalized.obligations { + fulfillment_cx.register_predicate_obligation(&infcx, obligation); + } + fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), new_ty, term_id, cause); if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) { infcx.report_fulfillment_errors(&err, None, false); error = true; diff --git a/src/test/ui/typeck/issue-88609.rs b/src/test/ui/typeck/issue-88609.rs new file mode 100644 index 0000000000000..dc459c885fa70 --- /dev/null +++ b/src/test/ui/typeck/issue-88609.rs @@ -0,0 +1,19 @@ +// Regression test for #88609: +// The return type for `main` is not normalized while checking if it implements +// the trait `std::process::Termination`. + +// build-pass + +trait Same { + type Output; +} + +impl Same for T { + type Output = T; +} + +type Unit = <() as Same>::Output; + +fn main() -> Result { + unimplemented!() +} From 33a28254f21c792cb0bda5427e66efa91b87dd4d Mon Sep 17 00:00:00 2001 From: ThePuzzlemaker Date: Mon, 6 Sep 2021 23:58:26 -0500 Subject: [PATCH 075/621] Clean up the fix a bit Use register_predicate_obligations rather than a for loop, since I didn't see that before. Also destructure in the `let` rather than projecting the fields individually --- compiler/rustc_typeck/src/lib.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs index e5d5ca3362bf4..e2cbaa4727289 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_typeck/src/lib.rs @@ -335,16 +335,20 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { // checking trait fulfillment, not this here. I'm not sure why it // works in the example in `fn test()` given in #88609? This also // probably isn't the best way to do this. - let normalized = infcx.partially_normalize_associated_types_in( - cause.clone(), + let InferOk { value: norm_return_ty, obligations } = infcx + .partially_normalize_associated_types_in( + cause.clone(), + ty::ParamEnv::empty(), + return_ty, + ); + fulfillment_cx.register_predicate_obligations(&infcx, obligations); + fulfillment_cx.register_bound( + &infcx, ty::ParamEnv::empty(), - return_ty, + norm_return_ty, + term_id, + cause, ); - let new_ty = normalized.value; - for obligation in normalized.obligations { - fulfillment_cx.register_predicate_obligation(&infcx, obligation); - } - fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), new_ty, term_id, cause); if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) { infcx.report_fulfillment_errors(&err, None, false); error = true; From 48268f523776f32a321c37a1464a2a6512e357b0 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Mon, 6 Sep 2021 23:30:04 -0700 Subject: [PATCH 076/621] fix(clippy): update loop lints to use arg.span Adapts clippy for fe1a7f71fbf3cf845a09a9b333a6adcf7e839607 --- clippy_lints/src/loops/for_kv_map.rs | 3 +-- clippy_lints/src/loops/iter_next_loop.rs | 4 ++-- clippy_lints/src/loops/mod.rs | 8 ++++---- clippy_lints/src/loops/needless_range_loop.rs | 4 ++-- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/loops/for_kv_map.rs b/clippy_lints/src/loops/for_kv_map.rs index 82bf49f5b49a4..e2f2e770fc258 100644 --- a/clippy_lints/src/loops/for_kv_map.rs +++ b/clippy_lints/src/loops/for_kv_map.rs @@ -15,7 +15,6 @@ pub(super) fn check<'tcx>( pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>, - expr: &'tcx Expr<'_>, ) { let pat_span = pat.span; @@ -43,7 +42,7 @@ pub(super) fn check<'tcx>( span_lint_and_then( cx, FOR_KV_MAP, - expr.span, + arg_span, &format!("you seem to want to iterate on a map's {}s", kind), |diag| { let map = sugg::Sugg::hir(cx, arg, "map"); diff --git a/clippy_lints/src/loops/iter_next_loop.rs b/clippy_lints/src/loops/iter_next_loop.rs index 9148fbfd497af..e640c62ebdace 100644 --- a/clippy_lints/src/loops/iter_next_loop.rs +++ b/clippy_lints/src/loops/iter_next_loop.rs @@ -5,12 +5,12 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_span::sym; -pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, expr: &Expr<'_>) -> bool { +pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool { if is_trait_method(cx, arg, sym::Iterator) { span_lint( cx, ITER_NEXT_LOOP, - expr.span, + arg.span, "you are iterating over `Iterator::next()` which is an Option; this will compile but is \ probably not what you want", ); diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index bd9de5e08d736..86bce75bdb2dd 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -601,15 +601,15 @@ fn check_for_loop<'tcx>( needless_range_loop::check(cx, pat, arg, body, expr); explicit_counter_loop::check(cx, pat, arg, body, expr); } - check_for_loop_arg(cx, pat, arg, expr); - for_kv_map::check(cx, pat, arg, body, expr); + check_for_loop_arg(cx, pat, arg); + for_kv_map::check(cx, pat, arg, body); mut_range_bound::check(cx, arg, body); single_element_loop::check(cx, pat, arg, body, expr); same_item_push::check(cx, pat, arg, body, expr); manual_flatten::check(cx, pat, arg, body, span); } -fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: &Expr<'_>) { +fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) { let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used if let ExprKind::MethodCall(method, _, [self_arg], _) = arg.kind { @@ -622,7 +622,7 @@ fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, expr: explicit_into_iter_loop::check(cx, self_arg, arg); }, "next" => { - next_loop_linted = iter_next_loop::check(cx, arg, expr); + next_loop_linted = iter_next_loop::check(cx, arg); }, _ => {}, } diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 3f77e7af927ad..bdb042421e9c5 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -146,7 +146,7 @@ pub(super) fn check<'tcx>( span_lint_and_then( cx, NEEDLESS_RANGE_LOOP, - expr.span, + arg.span, &format!("the loop variable `{}` is used to index `{}`", ident.name, indexed), |diag| { multispan_sugg( @@ -172,7 +172,7 @@ pub(super) fn check<'tcx>( span_lint_and_then( cx, NEEDLESS_RANGE_LOOP, - expr.span, + arg.span, &format!("the loop variable `{}` is only used to index `{}`", ident.name, indexed), |diag| { multispan_sugg( From 8cc1012f3fdb68e4c52021f74d5c5d7018f281d4 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 2 Jan 2021 14:42:15 +0100 Subject: [PATCH 077/621] Move monomorphize code to its own crate. --- scripts/filter_profile.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/filter_profile.rs b/scripts/filter_profile.rs index c4801a0a87b88..7a51293f5cda5 100755 --- a/scripts/filter_profile.rs +++ b/scripts/filter_profile.rs @@ -42,7 +42,7 @@ fn main() -> Result<(), Box> { continue; } - if stack.contains("rustc_mir::monomorphize::partitioning::collect_and_partition_mono_items") + if stack.contains("rustc_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") { @@ -81,7 +81,7 @@ fn main() -> Result<(), Box> { } const COLLECT_AND_PARTITION_MONO_ITEMS: &str = - "rustc_mir::monomorphize::partitioning::collect_and_partition_mono_items"; + "rustc_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()]; } From 06ae9e43d04672f88ae6743814cdfd8c346bcbf9 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 5 Jan 2021 19:53:07 +0100 Subject: [PATCH 078/621] Move the dataflow framework to its own crate. --- clippy_lints/src/lib.rs | 2 +- clippy_lints/src/redundant_clone.rs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 19719502870bd..3c6faf117fde5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -31,7 +31,7 @@ extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; -extern crate rustc_mir; +extern crate rustc_mir_dataflow; extern crate rustc_parse; extern crate rustc_parse_format; extern crate rustc_session; diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index f5e43264a5c69..7041e4f980ef4 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -15,7 +15,7 @@ use rustc_middle::mir::{ Mutability, }; use rustc_middle::ty::{self, fold::TypeVisitor, Ty, TyCtxt}; -use rustc_mir::dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor}; +use rustc_mir_dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; use rustc_span::sym; @@ -625,7 +625,10 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { .flat_map(HybridBitSet::iter) .collect(); - if ContainsRegion(self.cx.tcx).visit_ty(self.body.local_decls[*dest].ty).is_break() { + if ContainsRegion(self.cx.tcx) + .visit_ty(self.body.local_decls[*dest].ty) + .is_break() + { mutable_variables.push(*dest); } From 9ad50a41fe99ebb415155b2683ce358d61251003 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 5 Jan 2021 19:53:07 +0100 Subject: [PATCH 079/621] Move the dataflow framework to its own crate. --- src/base.rs | 2 +- src/lib.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/base.rs b/src/base.rs index e99a227a3a6ea..46a7485e4ef9c 100644 --- a/src/base.rs +++ b/src/base.rs @@ -23,7 +23,7 @@ pub(crate) fn codegen_fn<'tcx>( let mir = tcx.instance_mir(instance.def); let _mir_guard = crate::PrintOnPanic(|| { let mut buf = Vec::new(); - rustc_mir::util::write_mir_pretty(tcx, Some(instance.def_id()), &mut buf).unwrap(); + rustc_middle::mir::write_mir_pretty(tcx, Some(instance.def_id()), &mut buf).unwrap(); String::from_utf8_lossy(&buf).into_owned() }); diff --git a/src/lib.rs b/src/lib.rs index 2e5e8f683cdaa..87193e3ef5341 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,6 @@ extern crate rustc_incremental; extern crate rustc_index; extern crate rustc_interface; extern crate rustc_metadata; -extern crate rustc_mir; extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; From 0a8787fddae72030347ba08641eaaa3fda4af5fb Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Tue, 7 Sep 2021 20:45:28 +0200 Subject: [PATCH 080/621] Rustup to rustc 1.57.0-nightly (1698e3cac 2021-09-06) --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 9514427156732..c7ef436bfdbc1 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-09-01" +channel = "nightly-2021-09-07" components = ["rust-src", "rustc-dev", "llvm-tools-preview"] From 8a28d7a07ffff8c8f41aedffc573553f901c01c7 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Tue, 7 Sep 2021 20:45:58 +0200 Subject: [PATCH 081/621] Add portable-simd to .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 25080488a88b5..b6567aca78679 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,4 @@ perf.data.old /rand /regex /simple-raytracer -/stdsimd +/portable-simd From 98e8682cb47f7361b2ca4fe2a9773235340eed3f Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Tue, 5 Jan 2021 20:08:11 +0100 Subject: [PATCH 082/621] Rename rustc_mir to rustc_const_eval. --- clippy_utils/src/lib.rs | 2 +- clippy_utils/src/qualify_min_const_fn.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index ddff1686ba2ce..0906f958cfb1f 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -17,6 +17,7 @@ extern crate rustc_ast; extern crate rustc_ast_pretty; extern crate rustc_attr; +extern crate rustc_const_eval; extern crate rustc_data_structures; extern crate rustc_errors; extern crate rustc_hir; @@ -24,7 +25,6 @@ extern crate rustc_infer; extern crate rustc_lexer; extern crate rustc_lint; extern crate rustc_middle; -extern crate rustc_mir; extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index e5bbf75c3b0a1..4fb9e6b07e71a 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -366,7 +366,7 @@ fn check_terminator( } fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool { - rustc_mir::const_eval::is_const_fn(tcx, def_id) + rustc_const_eval::const_eval::is_const_fn(tcx, def_id) && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| { if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level { // Checking MSRV is manually necessary because `rustc` has no such concept. This entire From 091ed44b50a79a158cea77f44d12245a8ce6276a Mon Sep 17 00:00:00 2001 From: flip1995 Date: Wed, 8 Sep 2021 16:31:47 +0200 Subject: [PATCH 083/621] Merge commit '27afd6ade4bb1123a8bf82001629b69d23d62aff' into clippyup --- .cargo/config | 3 +- .github/ISSUE_TEMPLATE/blank_issue.md | 14 + .github/ISSUE_TEMPLATE/bug_report.md | 40 +- .github/ISSUE_TEMPLATE/false_negative.md | 22 +- .github/ISSUE_TEMPLATE/false_positive.md | 31 +- .github/ISSUE_TEMPLATE/ice.md | 21 +- .github/workflows/clippy_dev.yml | 3 - CHANGELOG.md | 12 +- Cargo.toml | 15 +- README.md | 8 +- clippy_dev/src/lib.rs | 1 - clippy_dev/src/main.rs | 9 +- clippy_dev/src/stderr_length_check.rs | 51 --- clippy_lints/Cargo.toml | 4 +- clippy_lints/src/approx_const.rs | 124 +++--- clippy_lints/src/assertions_on_constants.rs | 2 +- clippy_lints/src/blocks_in_if_conditions.rs | 4 +- clippy_lints/src/bool_assert_comparison.rs | 90 +++-- clippy_lints/src/bytecount.rs | 4 +- clippy_lints/src/casts/cast_ptr_alignment.rs | 4 +- clippy_lints/src/collapsible_match.rs | 39 +- clippy_lints/src/copies.rs | 2 +- clippy_lints/src/derivable_impls.rs | 108 ++++++ clippy_lints/src/derive.rs | 3 - clippy_lints/src/entry.rs | 31 +- clippy_lints/src/eval_order_dependence.rs | 4 +- clippy_lints/src/feature_name.rs | 164 ++++++++ clippy_lints/src/floating_point_arithmetic.rs | 32 +- clippy_lints/src/functions/must_use.rs | 1 - clippy_lints/src/if_let_mutex.rs | 6 +- clippy_lints/src/if_let_some_result.rs | 6 +- clippy_lints/src/let_if_seq.rs | 21 +- clippy_lints/src/lib.rs | 41 +- clippy_lints/src/loops/for_kv_map.rs | 6 +- clippy_lints/src/loops/manual_flatten.rs | 6 +- clippy_lints/src/loops/mod.rs | 19 +- clippy_lints/src/loops/mut_range_bound.rs | 98 +++-- clippy_lints/src/loops/needless_collect.rs | 4 +- clippy_lints/src/loops/needless_range_loop.rs | 29 +- clippy_lints/src/loops/never_loop.rs | 4 +- clippy_lints/src/loops/while_let_loop.rs | 6 +- .../src/loops/while_let_on_iterator.rs | 7 +- clippy_lints/src/macro_use.rs | 3 +- clippy_lints/src/manual_map.rs | 322 ++++++++-------- clippy_lints/src/matches.rs | 20 +- clippy_lints/src/mem_forget.rs | 4 +- clippy_lints/src/mem_replace.rs | 85 ++--- clippy_lints/src/methods/filter_next.rs | 7 +- clippy_lints/src/methods/manual_split_once.rs | 213 +++++++++++ clippy_lints/src/methods/mod.rs | 42 ++- clippy_lints/src/methods/or_fun_call.rs | 4 +- clippy_lints/src/methods/suspicious_splitn.rs | 14 +- clippy_lints/src/methods/utils.rs | 6 +- clippy_lints/src/misc.rs | 4 +- clippy_lints/src/module_style.rs | 178 +++++++++ clippy_lints/src/mut_mutex_lock.rs | 4 +- clippy_lints/src/needless_option_as_deref.rs | 66 ++++ clippy_lints/src/no_effect.rs | 69 +++- clippy_lints/src/open_options.rs | 6 +- clippy_lints/src/option_if_let_else.rs | 46 ++- clippy_lints/src/pattern_type_mismatch.rs | 2 +- clippy_lints/src/ptr_offset_with_cast.rs | 8 +- clippy_lints/src/question_mark.rs | 5 +- clippy_lints/src/redundant_clone.rs | 5 +- clippy_lints/src/redundant_closure_call.rs | 4 +- clippy_lints/src/returns.rs | 11 +- clippy_lints/src/strings.rs | 8 +- clippy_lints/src/to_string_in_display.rs | 4 +- clippy_lints/src/types/mod.rs | 81 +++- clippy_lints/src/types/rc_mutex.rs | 11 +- .../src/types/redundant_allocation.rs | 8 +- clippy_lints/src/undropped_manually_drops.rs | 4 +- clippy_lints/src/unnecessary_sort_by.rs | 5 +- clippy_lints/src/unused_async.rs | 7 +- clippy_lints/src/unused_io_amount.rs | 8 +- clippy_lints/src/unused_self.rs | 5 +- clippy_lints/src/unwrap.rs | 133 +++++-- clippy_lints/src/utils/conf.rs | 8 +- clippy_lints/src/utils/inspector.rs | 2 +- clippy_lints/src/utils/internal_lints.rs | 28 +- .../internal_lints/metadata_collector.rs | 4 +- clippy_lints/src/vec.rs | 2 +- clippy_utils/Cargo.toml | 6 +- clippy_utils/src/diagnostics.rs | 2 +- clippy_utils/src/higher.rs | 165 +++++--- clippy_utils/src/hir_utils.rs | 6 +- clippy_utils/src/lib.rs | 357 ++++++++++++++++-- clippy_utils/src/msrvs.rs | 3 + clippy_utils/src/paths.rs | 1 + clippy_utils/src/qualify_min_const_fn.rs | 4 +- clippy_utils/src/sugg.rs | 2 +- clippy_utils/src/ty.rs | 9 +- clippy_utils/src/usage.rs | 11 +- clippy_utils/src/visitors.rs | 117 +++--- doc/adding_lints.md | 9 +- doc/common_tools_writing_lints.md | 27 +- lintcheck/Cargo.toml | 1 + lintcheck/src/main.rs | 46 ++- rust-toolchain | 2 +- tests/compile-test.rs | 159 ++++---- tests/dogfood.rs | 2 + tests/fmt.rs | 3 + tests/integration.rs | 2 + tests/lint_message_convention.rs | 3 + tests/missing-test-files.rs | 2 + tests/ui-cargo/feature_name/fail/Cargo.toml | 21 ++ tests/ui-cargo/feature_name/fail/src/main.rs | 7 + .../feature_name/fail/src/main.stderr | 44 +++ tests/ui-cargo/feature_name/pass/Cargo.toml | 9 + tests/ui-cargo/feature_name/pass/src/main.rs | 7 + .../ui-cargo/module_style/fail_mod/Cargo.toml | 8 + .../module_style/fail_mod/src/bad/inner.rs | 1 + .../fail_mod/src/bad/inner/stuff.rs | 3 + .../fail_mod/src/bad/inner/stuff/most.rs | 1 + .../module_style/fail_mod/src/bad/mod.rs | 3 + .../module_style/fail_mod/src/main.rs | 9 + .../module_style/fail_mod/src/main.stderr | 19 + .../module_style/fail_no_mod/Cargo.toml | 8 + .../module_style/fail_no_mod/src/bad/mod.rs | 1 + .../module_style/fail_no_mod/src/main.rs | 7 + .../module_style/fail_no_mod/src/main.stderr | 11 + .../ui-cargo/module_style/pass_mod/Cargo.toml | 8 + .../module_style/pass_mod/src/bad/mod.rs | 1 + .../module_style/pass_mod/src/main.rs | 10 + .../module_style/pass_mod/src/more/foo.rs | 1 + .../pass_mod/src/more/inner/mod.rs | 1 + .../module_style/pass_mod/src/more/mod.rs | 2 + .../module_style/pass_no_mod/Cargo.toml | 8 + .../module_style/pass_no_mod/src/good.rs | 1 + .../module_style/pass_no_mod/src/main.rs | 7 + tests/ui/approx_const.rs | 4 + tests/ui/approx_const.stderr | 101 +++-- tests/ui/auxiliary/option_helpers.rs | 9 + tests/ui/bool_assert_comparison.rs | 63 ++++ tests/ui/bool_assert_comparison.stderr | 62 ++- tests/ui/box_vec.rs | 28 +- tests/ui/box_vec.stderr | 6 +- .../complex_conditionals.stderr | 39 +- .../complex_conditionals_nested.stderr | 4 +- .../ui/checked_unwrap/simple_conditionals.rs | 4 + .../checked_unwrap/simple_conditionals.stderr | 90 +++-- tests/ui/derivable_impls.rs | 170 +++++++++ tests/ui/derivable_impls.stderr | 77 ++++ tests/ui/entry.fixed | 15 +- tests/ui/entry.rs | 9 +- tests/ui/entry.stderr | 16 +- tests/ui/entry_btree.fixed | 18 + tests/ui/entry_btree.rs | 18 + tests/ui/entry_btree.stderr | 20 + tests/ui/linkedlist.rs | 31 +- tests/ui/linkedlist.stderr | 24 +- tests/ui/manual_flatten.rs | 13 + tests/ui/manual_map_option_2.fixed | 50 +++ tests/ui/manual_map_option_2.rs | 56 +++ tests/ui/manual_map_option_2.stderr | 43 +++ tests/ui/manual_split_once.fixed | 50 +++ tests/ui/manual_split_once.rs | 50 +++ tests/ui/manual_split_once.stderr | 82 ++++ tests/ui/mem_replace.fixed | 20 + tests/ui/mem_replace.rs | 20 + tests/ui/mem_replace.stderr | 20 +- tests/ui/methods.rs | 5 +- tests/ui/min_rust_version_attr.rs | 5 + tests/ui/min_rust_version_attr.stderr | 8 +- tests/ui/missing-doc-crate.stderr | 0 .../missing_const_for_fn/cant_be_const.stderr | 0 tests/ui/mut_range_bound.rs | 39 +- tests/ui/mut_range_bound.stderr | 47 ++- tests/ui/needless_option_as_deref.fixed | 13 + tests/ui/needless_option_as_deref.rs | 13 + tests/ui/needless_option_as_deref.stderr | 16 + tests/ui/option_if_let_else.fixed | 62 +++ tests/ui/option_if_let_else.rs | 68 ++++ tests/ui/option_if_let_else.stderr | 72 +++- tests/ui/proc_macro.stderr | 3 +- tests/ui/rc_buffer_redefined_string.stderr | 0 tests/ui/rc_mutex.rs | 32 +- tests/ui/rc_mutex.stderr | 33 +- tests/ui/redundant_allocation.rs | 20 + tests/ui/redundant_allocation.stderr | 11 +- tests/ui/unnecessary_operation.fixed | 4 +- tests/ui/unnecessary_operation.stderr | 80 ++-- tests/versioncheck.rs | 3 + 183 files changed, 4143 insertions(+), 1285 deletions(-) delete mode 100644 clippy_dev/src/stderr_length_check.rs create mode 100644 clippy_lints/src/derivable_impls.rs create mode 100644 clippy_lints/src/feature_name.rs create mode 100644 clippy_lints/src/methods/manual_split_once.rs create mode 100644 clippy_lints/src/module_style.rs create mode 100644 clippy_lints/src/needless_option_as_deref.rs create mode 100644 tests/ui-cargo/feature_name/fail/Cargo.toml create mode 100644 tests/ui-cargo/feature_name/fail/src/main.rs create mode 100644 tests/ui-cargo/feature_name/fail/src/main.stderr create mode 100644 tests/ui-cargo/feature_name/pass/Cargo.toml create mode 100644 tests/ui-cargo/feature_name/pass/src/main.rs create mode 100644 tests/ui-cargo/module_style/fail_mod/Cargo.toml create mode 100644 tests/ui-cargo/module_style/fail_mod/src/bad/inner.rs create mode 100644 tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff.rs create mode 100644 tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff/most.rs create mode 100644 tests/ui-cargo/module_style/fail_mod/src/bad/mod.rs create mode 100644 tests/ui-cargo/module_style/fail_mod/src/main.rs create mode 100644 tests/ui-cargo/module_style/fail_mod/src/main.stderr create mode 100644 tests/ui-cargo/module_style/fail_no_mod/Cargo.toml create mode 100644 tests/ui-cargo/module_style/fail_no_mod/src/bad/mod.rs create mode 100644 tests/ui-cargo/module_style/fail_no_mod/src/main.rs create mode 100644 tests/ui-cargo/module_style/fail_no_mod/src/main.stderr create mode 100644 tests/ui-cargo/module_style/pass_mod/Cargo.toml create mode 100644 tests/ui-cargo/module_style/pass_mod/src/bad/mod.rs create mode 100644 tests/ui-cargo/module_style/pass_mod/src/main.rs create mode 100644 tests/ui-cargo/module_style/pass_mod/src/more/foo.rs create mode 100644 tests/ui-cargo/module_style/pass_mod/src/more/inner/mod.rs create mode 100644 tests/ui-cargo/module_style/pass_mod/src/more/mod.rs create mode 100644 tests/ui-cargo/module_style/pass_no_mod/Cargo.toml create mode 100644 tests/ui-cargo/module_style/pass_no_mod/src/good.rs create mode 100644 tests/ui-cargo/module_style/pass_no_mod/src/main.rs create mode 100644 tests/ui/derivable_impls.rs create mode 100644 tests/ui/derivable_impls.stderr create mode 100644 tests/ui/entry_btree.fixed create mode 100644 tests/ui/entry_btree.rs create mode 100644 tests/ui/entry_btree.stderr create mode 100644 tests/ui/manual_map_option_2.fixed create mode 100644 tests/ui/manual_map_option_2.rs create mode 100644 tests/ui/manual_map_option_2.stderr create mode 100644 tests/ui/manual_split_once.fixed create mode 100644 tests/ui/manual_split_once.rs create mode 100644 tests/ui/manual_split_once.stderr delete mode 100644 tests/ui/missing-doc-crate.stderr delete mode 100644 tests/ui/missing_const_for_fn/cant_be_const.stderr create mode 100644 tests/ui/needless_option_as_deref.fixed create mode 100644 tests/ui/needless_option_as_deref.rs create mode 100644 tests/ui/needless_option_as_deref.stderr delete mode 100644 tests/ui/rc_buffer_redefined_string.stderr diff --git a/.cargo/config b/.cargo/config index e95ea224cb681..84ae36a46d71d 100644 --- a/.cargo/config +++ b/.cargo/config @@ -5,4 +5,5 @@ lintcheck = "run --target-dir lintcheck/target --package lintcheck --bin lintche collect-metadata = "test --test dogfood --features metadata-collector-lint -- run_metadata_collection_lint --ignored" [build] -rustflags = ["-Zunstable-options"] +# -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests +rustflags = ["-Zunstable-options", "-Zbinary-dep-depinfo"] diff --git a/.github/ISSUE_TEMPLATE/blank_issue.md b/.github/ISSUE_TEMPLATE/blank_issue.md index 9aef3ebe637a1..2891d5e5da1e1 100644 --- a/.github/ISSUE_TEMPLATE/blank_issue.md +++ b/.github/ISSUE_TEMPLATE/blank_issue.md @@ -2,3 +2,17 @@ name: Blank Issue about: Create a blank issue. --- + + + diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2bc87db123d4d..87c18cdee66c5 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -20,28 +20,24 @@ Instead, this happened: *explanation* ### Meta -- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20) -- `rustc -Vv`: - ``` - rustc 1.46.0-nightly (f455e46ea 2020-06-20) - binary: rustc - commit-hash: f455e46eae1a227d735091091144601b467e1565 - commit-date: 2020-06-20 - host: x86_64-unknown-linux-gnu - release: 1.46.0-nightly - LLVM version: 10.0 - ``` +**Rust version (`rustc -Vv`):** + +``` +rustc 1.46.0-nightly (f455e46ea 2020-06-20) +binary: rustc +commit-hash: f455e46eae1a227d735091091144601b467e1565 +commit-date: 2020-06-20 +host: x86_64-unknown-linux-gnu +release: 1.46.0-nightly +LLVM version: 10.0 +``` -
Backtrace -

- - ``` - - ``` - -

-
diff --git a/.github/ISSUE_TEMPLATE/false_negative.md b/.github/ISSUE_TEMPLATE/false_negative.md index 53341c7a928af..d9ea2db34edd7 100644 --- a/.github/ISSUE_TEMPLATE/false_negative.md +++ b/.github/ISSUE_TEMPLATE/false_negative.md @@ -22,14 +22,14 @@ Instead, this happened: *explanation* ### Meta -- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20) -- `rustc -Vv`: - ``` - rustc 1.46.0-nightly (f455e46ea 2020-06-20) - binary: rustc - commit-hash: f455e46eae1a227d735091091144601b467e1565 - commit-date: 2020-06-20 - host: x86_64-unknown-linux-gnu - release: 1.46.0-nightly - LLVM version: 10.0 - ``` +**Rust version (`rustc -Vv`):** + +``` +rustc 1.46.0-nightly (f455e46ea 2020-06-20) +binary: rustc +commit-hash: f455e46eae1a227d735091091144601b467e1565 +commit-date: 2020-06-20 +host: x86_64-unknown-linux-gnu +release: 1.46.0-nightly +LLVM version: 10.0 +``` diff --git a/.github/ISSUE_TEMPLATE/false_positive.md b/.github/ISSUE_TEMPLATE/false_positive.md index 34fd6f4bdb710..4170b9ff2dbe5 100644 --- a/.github/ISSUE_TEMPLATE/false_positive.md +++ b/.github/ISSUE_TEMPLATE/false_positive.md @@ -22,14 +22,23 @@ Instead, this happened: *explanation* ### Meta -- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20) -- `rustc -Vv`: - ``` - rustc 1.46.0-nightly (f455e46ea 2020-06-20) - binary: rustc - commit-hash: f455e46eae1a227d735091091144601b467e1565 - commit-date: 2020-06-20 - host: x86_64-unknown-linux-gnu - release: 1.46.0-nightly - LLVM version: 10.0 - ``` +**Rust version (`rustc -Vv`):** +``` +rustc 1.46.0-nightly (f455e46ea 2020-06-20) +binary: rustc +commit-hash: f455e46eae1a227d735091091144601b467e1565 +commit-date: 2020-06-20 +host: x86_64-unknown-linux-gnu +release: 1.46.0-nightly +LLVM version: 10.0 +``` + + diff --git a/.github/ISSUE_TEMPLATE/ice.md b/.github/ISSUE_TEMPLATE/ice.md index 0b7cd1ed0fb60..6c1bed663c6cb 100644 --- a/.github/ISSUE_TEMPLATE/ice.md +++ b/.github/ISSUE_TEMPLATE/ice.md @@ -20,17 +20,16 @@ http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/ ### Meta -- `cargo clippy -V`: e.g. clippy 0.0.212 (f455e46 2020-06-20) -- `rustc -Vv`: - ``` - rustc 1.46.0-nightly (f455e46ea 2020-06-20) - binary: rustc - commit-hash: f455e46eae1a227d735091091144601b467e1565 - commit-date: 2020-06-20 - host: x86_64-unknown-linux-gnu - release: 1.46.0-nightly - LLVM version: 10.0 - ``` +**Rust version (`rustc -Vv`):** +``` +rustc 1.46.0-nightly (f455e46ea 2020-06-20) +binary: rustc +commit-hash: f455e46eae1a227d735091091144601b467e1565 +commit-date: 2020-06-20 +host: x86_64-unknown-linux-gnu +release: 1.46.0-nightly +LLVM version: 10.0 +``` ### Error output diff --git a/.github/workflows/clippy_dev.yml b/.github/workflows/clippy_dev.yml index 95da775b7bc36..9a5416153abdb 100644 --- a/.github/workflows/clippy_dev.yml +++ b/.github/workflows/clippy_dev.yml @@ -42,9 +42,6 @@ jobs: run: cargo build --features deny-warnings working-directory: clippy_dev - - name: Test limit_stderr_length - run: cargo dev limit_stderr_length - - name: Test update_lints run: cargo dev update_lints --check diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b89170073be5..f5ac2f7c9f8c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -964,7 +964,7 @@ Released 2020-11-19 [#5907](https://github.com/rust-lang/rust-clippy/pull/5907) * [`suspicious_arithmetic_impl`]: extend to implementations of `BitAnd`, `BitOr`, `BitXor`, `Rem`, `Shl`, and `Shr` [#5884](https://github.com/rust-lang/rust-clippy/pull/5884) -* [`invalid_atomic_ordering`]: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update` +* `invalid_atomic_ordering`: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update` [#6025](https://github.com/rust-lang/rust-clippy/pull/6025) * Avoid [`redundant_pattern_matching`] triggering in macros [#6069](https://github.com/rust-lang/rust-clippy/pull/6069) @@ -1451,7 +1451,7 @@ Released 2020-03-12 * [`option_as_ref_deref`] [#4945](https://github.com/rust-lang/rust-clippy/pull/4945) * [`wildcard_in_or_patterns`] [#4960](https://github.com/rust-lang/rust-clippy/pull/4960) * [`iter_nth_zero`] [#4966](https://github.com/rust-lang/rust-clippy/pull/4966) -* [`invalid_atomic_ordering`] [#4999](https://github.com/rust-lang/rust-clippy/pull/4999) +* `invalid_atomic_ordering` [#4999](https://github.com/rust-lang/rust-clippy/pull/4999) * [`skip_while_next`] [#5067](https://github.com/rust-lang/rust-clippy/pull/5067) ### Moves and Deprecations @@ -2613,6 +2613,7 @@ Released 2018-09-13 [`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr [`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver [`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof +[`derivable_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord [`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method @@ -2712,7 +2713,6 @@ Released 2018-09-13 [`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic [`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division [`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref -[`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering [`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons @@ -2754,6 +2754,7 @@ Released 2018-09-13 [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic +[`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once [`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat [`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip [`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap @@ -2795,6 +2796,7 @@ Released 2018-09-13 [`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc [`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes [`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals +[`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files [`module_inception`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_inception [`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions [`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic @@ -2821,6 +2823,7 @@ Released 2018-09-13 [`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main [`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes +[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value [`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop @@ -2828,6 +2831,7 @@ Released 2018-09-13 [`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update [`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord [`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply +[`negative_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#negative_feature_names [`never_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#never_loop [`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self [`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default @@ -2881,6 +2885,7 @@ Released 2018-09-13 [`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call [`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls [`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else +[`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names [`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching @@ -2903,6 +2908,7 @@ Released 2018-09-13 [`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some [`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment [`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors +[`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files [`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse diff --git a/Cargo.toml b/Cargo.toml index 16b6c207a5f19..2310370fb9fbe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.56" +version = "0.1.57" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -32,11 +32,7 @@ tempfile = { version = "3.1.0", optional = true } cargo_metadata = "0.12" compiletest_rs = { version = "0.6.0", features = ["tmp"] } tester = "0.9" -serde = { version = "1.0", features = ["derive"] } -derive-new = "0.5" regex = "1.4" -quote = "1" -syn = { version = "1", features = ["full"] } # This is used by the `collect-metadata` alias. filetime = "0.2" @@ -45,6 +41,15 @@ filetime = "0.2" # for more information. rustc-workspace-hack = "1.0.0" +# UI test dependencies +clippy_utils = { path = "clippy_utils" } +derive-new = "0.5" +if_chain = "1.0" +itertools = "0.10.1" +quote = "1" +serde = { version = "1.0", features = ["derive"] } +syn = { version = "1", features = ["full"] } + [build-dependencies] rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" } diff --git a/README.md b/README.md index e1c968273cdf6..aaf404eadea11 100644 --- a/README.md +++ b/README.md @@ -45,13 +45,13 @@ or in Travis CI. One way to use Clippy is by installing Clippy through rustup as a cargo subcommand. -#### Step 1: Install rustup +#### Step 1: Install Rustup -You can install [rustup](https://rustup.rs/) on supported platforms. This will help +You can install [Rustup](https://rustup.rs/) on supported platforms. This will help us install Clippy and its dependencies. -If you already have rustup installed, update to ensure you have the latest -rustup and compiler: +If you already have Rustup installed, update to ensure you have the latest +Rustup and compiler: ```terminal rustup update diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 72bdaf8d59282..e05db7af58677 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -17,7 +17,6 @@ pub mod fmt; pub mod new_lint; pub mod serve; pub mod setup; -pub mod stderr_length_check; pub mod update_lints; static DEC_CLIPPY_LINT_RE: SyncLazy = SyncLazy::new(|| { diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index ff324ff6ee6ff..8fdeba9842af3 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -3,7 +3,7 @@ #![warn(rust_2018_idioms, unused_lifetimes)] use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; -use clippy_dev::{bless, fmt, new_lint, serve, setup, stderr_length_check, update_lints}; +use clippy_dev::{bless, fmt, new_lint, serve, setup, update_lints}; fn main() { let matches = get_clap_config(); @@ -33,9 +33,6 @@ fn main() { Err(e) => eprintln!("Unable to create lint: {}", e), } }, - ("limit_stderr_length", _) => { - stderr_length_check::check(); - }, ("setup", Some(sub_command)) => match sub_command.subcommand() { ("intellij", Some(matches)) => setup::intellij::setup_rustc_src( matches @@ -152,10 +149,6 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .takes_value(true), ), ) - .subcommand( - SubCommand::with_name("limit_stderr_length") - .about("Ensures that stderr files do not grow longer than a certain amount of lines."), - ) .subcommand( SubCommand::with_name("setup") .about("Support for setting up your personal development environment") diff --git a/clippy_dev/src/stderr_length_check.rs b/clippy_dev/src/stderr_length_check.rs deleted file mode 100644 index e02b6f7da5f7b..0000000000000 --- a/clippy_dev/src/stderr_length_check.rs +++ /dev/null @@ -1,51 +0,0 @@ -use crate::clippy_project_root; -use std::ffi::OsStr; -use std::fs; -use std::path::{Path, PathBuf}; -use walkdir::WalkDir; - -// The maximum length allowed for stderr files. -// -// We limit this because small files are easier to deal with than bigger files. -const LENGTH_LIMIT: usize = 200; - -pub fn check() { - let exceeding_files: Vec<_> = exceeding_stderr_files(); - - if !exceeding_files.is_empty() { - eprintln!("Error: stderr files exceeding limit of {} lines:", LENGTH_LIMIT); - for (path, count) in exceeding_files { - println!("{}: {}", path.display(), count); - } - std::process::exit(1); - } -} - -fn exceeding_stderr_files() -> Vec<(PathBuf, usize)> { - // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories. - WalkDir::new(clippy_project_root().join("tests/ui")) - .into_iter() - .filter_map(Result::ok) - .filter(|f| !f.file_type().is_dir()) - .filter_map(|e| { - let p = e.into_path(); - let count = count_linenumbers(&p); - if p.extension() == Some(OsStr::new("stderr")) && count > LENGTH_LIMIT { - Some((p, count)) - } else { - None - } - }) - .collect() -} - -#[must_use] -fn count_linenumbers(filepath: &Path) -> usize { - match fs::read(filepath) { - Ok(content) => bytecount::count(&content, b'\n'), - Err(e) => { - eprintln!("Failed to read file: {}", e); - 0 - }, - } -} diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index a3b92e1faa1a9..3c28024bf926a 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -1,8 +1,6 @@ [package] name = "clippy_lints" -# begin automatic update -version = "0.1.56" -# end automatic update +version = "0.1.57" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index 6100f4e435a8a..fb54ac1ec5119 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -1,8 +1,10 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{meets_msrv, msrvs}; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; use rustc_hir::{Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol; use std::f64::consts as f64; @@ -36,68 +38,82 @@ declare_clippy_lint! { "the approximate of a known float constant (in `std::fXX::consts`)" } -// Tuples are of the form (constant, name, min_digits) -const KNOWN_CONSTS: [(f64, &str, usize); 18] = [ - (f64::E, "E", 4), - (f64::FRAC_1_PI, "FRAC_1_PI", 4), - (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5), - (f64::FRAC_2_PI, "FRAC_2_PI", 5), - (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5), - (f64::FRAC_PI_2, "FRAC_PI_2", 5), - (f64::FRAC_PI_3, "FRAC_PI_3", 5), - (f64::FRAC_PI_4, "FRAC_PI_4", 5), - (f64::FRAC_PI_6, "FRAC_PI_6", 5), - (f64::FRAC_PI_8, "FRAC_PI_8", 5), - (f64::LN_10, "LN_10", 5), - (f64::LN_2, "LN_2", 5), - (f64::LOG10_E, "LOG10_E", 5), - (f64::LOG2_E, "LOG2_E", 5), - (f64::LOG2_10, "LOG2_10", 5), - (f64::LOG10_2, "LOG10_2", 5), - (f64::PI, "PI", 3), - (f64::SQRT_2, "SQRT_2", 5), +// Tuples are of the form (constant, name, min_digits, msrv) +const KNOWN_CONSTS: [(f64, &str, usize, Option); 19] = [ + (f64::E, "E", 4, None), + (f64::FRAC_1_PI, "FRAC_1_PI", 4, None), + (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5, None), + (f64::FRAC_2_PI, "FRAC_2_PI", 5, None), + (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5, None), + (f64::FRAC_PI_2, "FRAC_PI_2", 5, None), + (f64::FRAC_PI_3, "FRAC_PI_3", 5, None), + (f64::FRAC_PI_4, "FRAC_PI_4", 5, None), + (f64::FRAC_PI_6, "FRAC_PI_6", 5, None), + (f64::FRAC_PI_8, "FRAC_PI_8", 5, None), + (f64::LN_2, "LN_2", 5, None), + (f64::LN_10, "LN_10", 5, None), + (f64::LOG2_10, "LOG2_10", 5, Some(msrvs::LOG2_10)), + (f64::LOG2_E, "LOG2_E", 5, None), + (f64::LOG10_2, "LOG10_2", 5, Some(msrvs::LOG10_2)), + (f64::LOG10_E, "LOG10_E", 5, None), + (f64::PI, "PI", 3, None), + (f64::SQRT_2, "SQRT_2", 5, None), + (f64::TAU, "TAU", 3, Some(msrvs::TAU)), ]; -declare_lint_pass!(ApproxConstant => [APPROX_CONSTANT]); +pub struct ApproxConstant { + msrv: Option, +} -impl<'tcx> LateLintPass<'tcx> for ApproxConstant { - fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Lit(lit) = &e.kind { - check_lit(cx, &lit.node, e); +impl ApproxConstant { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { msrv } + } + + fn check_lit(&self, cx: &LateContext<'_>, lit: &LitKind, e: &Expr<'_>) { + match *lit { + LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty { + FloatTy::F32 => self.check_known_consts(cx, e, s, "f32"), + FloatTy::F64 => self.check_known_consts(cx, e, s, "f64"), + }, + LitKind::Float(s, LitFloatType::Unsuffixed) => self.check_known_consts(cx, e, s, "f{32, 64}"), + _ => (), } } -} -fn check_lit(cx: &LateContext<'_>, lit: &LitKind, e: &Expr<'_>) { - match *lit { - LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty { - FloatTy::F32 => check_known_consts(cx, e, s, "f32"), - FloatTy::F64 => check_known_consts(cx, e, s, "f64"), - }, - LitKind::Float(s, LitFloatType::Unsuffixed) => check_known_consts(cx, e, s, "f{32, 64}"), - _ => (), + fn check_known_consts(&self, cx: &LateContext<'_>, e: &Expr<'_>, s: symbol::Symbol, module: &str) { + let s = s.as_str(); + if s.parse::().is_ok() { + for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS { + if is_approx_const(constant, &s, min_digits) + && msrv.as_ref().map_or(true, |msrv| meets_msrv(self.msrv.as_ref(), msrv)) + { + span_lint_and_help( + cx, + APPROX_CONSTANT, + e.span, + &format!("approximate value of `{}::consts::{}` found", module, &name), + None, + "consider using the constant directly", + ); + return; + } + } + } } } -fn check_known_consts(cx: &LateContext<'_>, e: &Expr<'_>, s: symbol::Symbol, module: &str) { - let s = s.as_str(); - if s.parse::().is_ok() { - for &(constant, name, min_digits) in &KNOWN_CONSTS { - if is_approx_const(constant, &s, min_digits) { - span_lint( - cx, - APPROX_CONSTANT, - e.span, - &format!( - "approximate value of `{}::consts::{}` found. \ - Consider using it directly", - module, &name - ), - ); - return; - } +impl_lint_pass!(ApproxConstant => [APPROX_CONSTANT]); + +impl<'tcx> LateLintPass<'tcx> for ApproxConstant { + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Lit(lit) = &e.kind { + self.check_lit(cx, &lit.node, e); } } + + extract_msrv_attr!(LateContext); } /// Returns `false` if the number of significant figures in `value` are diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index 891e865b245dd..d834a1d317a0f 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -118,7 +118,7 @@ enum AssertKind { fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option { if_chain! { if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr); - if let ExprKind::Unary(UnOp::Not, ref expr) = cond.kind; + if let ExprKind::Unary(UnOp::Not, expr) = cond.kind; // bind the first argument of the `assert!` macro if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr); // block diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs index 51d95cc6f0b10..47e5b0d583dab 100644 --- a/clippy_lints/src/blocks_in_if_conditions.rs +++ b/clippy_lints/src/blocks_in_if_conditions.rs @@ -61,8 +61,8 @@ impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> { // do not lint if the closure is called using an iterator (see #1141) if_chain! { if let Some(parent) = get_parent_expr(self.cx, expr); - if let ExprKind::MethodCall(_, _, args, _) = parent.kind; - let caller = self.cx.typeck_results().expr_ty(&args[0]); + if let ExprKind::MethodCall(_, _, [self_arg, ..], _) = &parent.kind; + let caller = self.cx.typeck_results().expr_ty(self_arg); if let Some(iter_id) = self.cx.tcx.get_diagnostic_item(sym::Iterator); if implements_trait(self.cx, caller, iter_id, &[]); then { diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index 8d3f68565b223..cdc192a47e48a 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -1,9 +1,11 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{ast_utils, is_direct_expn_of}; -use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; +use clippy_utils::{diagnostics::span_lint_and_sugg, higher, is_direct_expn_of, ty::implements_trait}; +use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_hir::{Expr, ExprKind, Lit}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::Ident; declare_clippy_lint! { /// ### What it does @@ -28,45 +30,77 @@ declare_clippy_lint! { declare_lint_pass!(BoolAssertComparison => [BOOL_ASSERT_COMPARISON]); -fn is_bool_lit(e: &Expr) -> bool { +fn is_bool_lit(e: &Expr<'_>) -> bool { matches!( e.kind, ExprKind::Lit(Lit { - kind: LitKind::Bool(_), + node: LitKind::Bool(_), .. }) ) && !e.span.from_expansion() } -impl EarlyLintPass for BoolAssertComparison { - fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) { +fn is_impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(e); + + cx.tcx + .lang_items() + .not_trait() + .filter(|trait_id| implements_trait(cx, ty, *trait_id, &[])) + .and_then(|trait_id| { + cx.tcx.associated_items(trait_id).find_by_name_and_kind( + cx.tcx, + Ident::from_str("Output"), + ty::AssocKind::Type, + trait_id, + ) + }) + .map_or(false, |assoc_item| { + let proj = cx.tcx.mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(ty, &[])); + let nty = cx.tcx.normalize_erasing_regions(cx.param_env, proj); + + nty.is_bool() + }) +} + +impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let macros = ["assert_eq", "debug_assert_eq"]; let inverted_macros = ["assert_ne", "debug_assert_ne"]; for mac in macros.iter().chain(inverted_macros.iter()) { - if let Some(span) = is_direct_expn_of(e.span, mac) { - if let Some([a, b]) = ast_utils::extract_assert_macro_args(e) { - let nb_bool_args = is_bool_lit(a) as usize + is_bool_lit(b) as usize; + if let Some(span) = is_direct_expn_of(expr.span, mac) { + if let Some(args) = higher::extract_assert_macro_args(expr) { + if let [a, b, ..] = args[..] { + let nb_bool_args = is_bool_lit(a) as usize + is_bool_lit(b) as usize; + + if nb_bool_args != 1 { + // If there are two boolean arguments, we definitely don't understand + // what's going on, so better leave things as is... + // + // Or there is simply no boolean and then we can leave things as is! + return; + } - if nb_bool_args != 1 { - // If there are two boolean arguments, we definitely don't understand - // what's going on, so better leave things as is... - // - // Or there is simply no boolean and then we can leave things as is! + if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) { + // At this point the expression which is not a boolean + // literal does not implement Not trait with a bool output, + // so we cannot suggest to rewrite our code + return; + } + + let non_eq_mac = &mac[..mac.len() - 3]; + span_lint_and_sugg( + cx, + BOOL_ASSERT_COMPARISON, + span, + &format!("used `{}!` with a literal bool", mac), + "replace it with", + format!("{}!(..)", non_eq_mac), + Applicability::MaybeIncorrect, + ); return; } - - let non_eq_mac = &mac[..mac.len() - 3]; - span_lint_and_sugg( - cx, - BOOL_ASSERT_COMPARISON, - span, - &format!("used `{}!` with a literal bool", mac), - "replace it with", - format!("{}!(..)", non_eq_mac), - Applicability::MaybeIncorrect, - ); - return; } } } diff --git a/clippy_lints/src/bytecount.rs b/clippy_lints/src/bytecount.rs index c444984bc133a..a07cd5e5f4e53 100644 --- a/clippy_lints/src/bytecount.rs +++ b/clippy_lints/src/bytecount.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::match_type; -use clippy_utils::visitors::LocalUsedVisitor; +use clippy_utils::visitors::is_local_used; use clippy_utils::{path_to_local_id, paths, peel_ref_operators, remove_blocks, strip_pat_refs}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for ByteCount { return; }; if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind(); - if !LocalUsedVisitor::new(cx, arg_id).check_expr(needle); + if !is_local_used(cx, needle, arg_id); then { let haystack = if let ExprKind::MethodCall(path, _, args, _) = filter_recv.kind { diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index 5dcf1824ef0b7..248b35b024ee1 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -19,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { cx.typeck_results().expr_ty(expr), ); lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); - } else if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind { + } else if let ExprKind::MethodCall(method_path, _, [self_arg, ..], _) = &expr.kind { if_chain! { if method_path.ident.name == sym!(cast); if let Some(generic_args) = method_path.args; @@ -28,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !is_hir_ty_cfg_dependant(cx, cast_to); then { let (cast_from, cast_to) = - (cx.typeck_results().expr_ty(&args[0]), cx.typeck_results().expr_ty(expr)); + (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr)); lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); } } diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index a42eee53459eb..a4693fa213bc4 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -1,9 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::visitors::LocalUsedVisitor; -use clippy_utils::{higher, is_lang_ctor, is_unit_expr, path_to_local, peel_ref_operators, SpanlessEq}; +use clippy_utils::higher::IfLetOrMatch; +use clippy_utils::visitors::is_local_used; +use clippy_utils::{is_lang_ctor, is_unit_expr, path_to_local, peel_ref_operators, SpanlessEq}; use if_chain::if_chain; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, MatchSource, Pat, PatKind, StmtKind}; +use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{MultiSpan, Span}; @@ -56,11 +57,11 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch { check_arm(cx, true, arm.pat, arm.body, arm.guard.as_ref(), Some(els_arm.body)); } } - } + }, Some(IfLetOrMatch::IfLet(_, pat, body, els)) => { check_arm(cx, false, pat, body, None, els); - } - None => {} + }, + None => {}, } } } @@ -71,7 +72,7 @@ fn check_arm<'tcx>( outer_pat: &'tcx Pat<'tcx>, outer_then_body: &'tcx Expr<'tcx>, outer_guard: Option<&'tcx Guard<'tcx>>, - outer_else_body: Option<&'tcx Expr<'tcx>> + outer_else_body: Option<&'tcx Expr<'tcx>>, ) { let inner_expr = strip_singleton_blocks(outer_then_body); if_chain! { @@ -106,14 +107,13 @@ fn check_arm<'tcx>( (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b), }; // the binding must not be used in the if guard - let mut used_visitor = LocalUsedVisitor::new(cx, binding_id); - if outer_guard.map_or(true, |(Guard::If(e) | Guard::IfLet(_, e))| !used_visitor.check_expr(e)); + if outer_guard.map_or(true, |(Guard::If(e) | Guard::IfLet(_, e))| !is_local_used(cx, *e, binding_id)); // ...or anywhere in the inner expression if match inner { IfLetOrMatch::IfLet(_, _, body, els) => { - !used_visitor.check_expr(body) && els.map_or(true, |e| !used_visitor.check_expr(e)) + !is_local_used(cx, body, binding_id) && els.map_or(true, |e| !is_local_used(cx, e, binding_id)) }, - IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| used_visitor.check_arm(arm)), + IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| is_local_used(cx, arm, binding_id)), }; then { let msg = format!( @@ -151,23 +151,6 @@ fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> expr } -enum IfLetOrMatch<'hir> { - Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource), - /// scrutinee, pattern, then block, else block - IfLet(&'hir Expr<'hir>, &'hir Pat<'hir>, &'hir Expr<'hir>, Option<&'hir Expr<'hir>>), -} - -impl<'hir> IfLetOrMatch<'hir> { - fn parse(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option { - match expr.kind { - ExprKind::Match(expr, arms, source) => Some(Self::Match(expr, arms, source)), - _ => higher::IfLet::hir(cx, expr).map(|higher::IfLet { let_expr, let_pat, if_then, if_else }| { - Self::IfLet(let_expr, let_pat, if_then, if_else) - }) - } - } -} - /// A "wild-like" arm has a wild (`_`) or `None` pattern and no guard. Such arms can be "collapsed" /// into a single wild arm without any significant loss in semantics or readability. fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index 5eb99cfe24f49..d58e49491203c 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -148,7 +148,7 @@ declare_clippy_lint! { /// }; /// ``` pub BRANCHES_SHARING_CODE, - complexity, + nursery, "`if` statement with shared code in all blocks" } diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs new file mode 100644 index 0000000000000..b4c4ca016aace --- /dev/null +++ b/clippy_lints/src/derivable_impls.rs @@ -0,0 +1,108 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{in_macro, is_automatically_derived, is_default_equivalent, remove_blocks}; +use rustc_hir::{ + def::{DefKind, Res}, + Body, Expr, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node, QPath, +}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TypeFoldable; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Detects manual `std::default::Default` implementations that are identical to a derived implementation. + /// + /// ### Why is this bad? + /// It is less concise. + /// + /// ### Example + /// ```rust + /// struct Foo { + /// bar: bool + /// } + /// + /// impl std::default::Default for Foo { + /// fn default() -> Self { + /// Self { + /// bar: false + /// } + /// } + /// } + /// ``` + /// + /// Could be written as: + /// + /// ```rust + /// #[derive(Default)] + /// struct Foo { + /// bar: bool + /// } + /// ``` + /// + /// ### Known problems + /// Derive macros [sometimes use incorrect bounds](https://github.com/rust-lang/rust/issues/26925) + /// in generic types and the user defined `impl` maybe is more generalized or + /// specialized than what derive will produce. This lint can't detect the manual `impl` + /// has exactly equal bounds, and therefore this lint is disabled for types with + /// generic parameters. + /// + pub DERIVABLE_IMPLS, + complexity, + "manual implementation of the `Default` trait which is equal to a derive" +} + +declare_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]); + +fn is_path_self(e: &Expr<'_>) -> bool { + if let ExprKind::Path(QPath::Resolved(_, p)) = e.kind { + matches!(p.res, Res::SelfCtor(..) | Res::Def(DefKind::Ctor(..), _)) + } else { + false + } +} + +impl<'tcx> LateLintPass<'tcx> for DerivableImpls { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if_chain! { + if let ItemKind::Impl(Impl { + of_trait: Some(ref trait_ref), + items: [child], + .. + }) = item.kind; + if let attrs = cx.tcx.hir().attrs(item.hir_id()); + if !is_automatically_derived(attrs); + if !in_macro(item.span); + if let Some(def_id) = trait_ref.trait_def_id(); + if cx.tcx.is_diagnostic_item(sym::Default, def_id); + if let impl_item_hir = child.id.hir_id(); + if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir); + if let ImplItemKind::Fn(_, b) = &impl_item.kind; + if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b); + if let Some(adt_def) = cx.tcx.type_of(item.def_id).ty_adt_def(); + then { + if cx.tcx.type_of(item.def_id).definitely_has_param_types_or_consts(cx.tcx) { + return; + } + let should_emit = match remove_blocks(func_expr).kind { + ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)), + ExprKind::Call(callee, args) + if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)), + ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)), + _ => false, + }; + if should_emit { + let path_string = cx.tcx.def_path_str(adt_def.did); + span_lint_and_help( + cx, + DERIVABLE_IMPLS, + item.span, + "this `impl` can be derived", + None, + &format!("try annotating `{}` with `#[derive(Default)]`", path_string), + ); + } + } + } + } +} diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index dcfa5253f8341..8416b8440dfbe 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -105,9 +105,6 @@ declare_clippy_lint! { /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]` /// gets you. /// - /// ### Known problems - /// Bounds of generic types are sometimes wrong: https://github.com/rust-lang/rust/issues/26925 - /// /// ### Example /// ```rust,ignore /// #[derive(Copy)] diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 627f746ec9971..ac6824672f66c 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -9,8 +9,9 @@ use clippy_utils::{ use core::fmt::Write; use rustc_errors::Applicability; use rustc_hir::{ + hir_id::HirIdSet, intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}, - Block, Expr, ExprKind, Guard, HirId, Local, Stmt, StmtKind, UnOp, + Block, Expr, ExprKind, Guard, HirId, Pat, Stmt, StmtKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -338,6 +339,8 @@ struct InsertSearcher<'cx, 'tcx> { edits: Vec>, /// A stack of loops the visitor is currently in. loops: Vec, + /// Local variables created in the expression. These don't need to be captured. + locals: HirIdSet, } impl<'tcx> InsertSearcher<'_, 'tcx> { /// Visit the expression as a branch in control flow. Multiple insert calls can be used, but @@ -385,13 +388,16 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { } }, StmtKind::Expr(e) => self.visit_expr(e), - StmtKind::Local(Local { init: Some(e), .. }) => { - self.allow_insert_closure &= !self.in_tail_pos; - self.in_tail_pos = false; - self.is_single_insert = false; - self.visit_expr(e); + StmtKind::Local(l) => { + self.visit_pat(l.pat); + if let Some(e) = l.init { + self.allow_insert_closure &= !self.in_tail_pos; + self.in_tail_pos = false; + self.is_single_insert = false; + self.visit_expr(e); + } }, - _ => { + StmtKind::Item(_) => { self.allow_insert_closure &= !self.in_tail_pos; self.is_single_insert = false; }, @@ -473,6 +479,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { // Each branch may contain it's own insert expression. let mut is_map_used = self.is_map_used; for arm in arms { + self.visit_pat(arm.pat); if let Some(Guard::If(guard) | Guard::IfLet(_, guard)) = arm.guard { self.visit_non_tail_expr(guard); } @@ -498,7 +505,8 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { }, _ => { self.allow_insert_closure &= !self.in_tail_pos; - self.allow_insert_closure &= can_move_expr_to_closure_no_visit(self.cx, expr, &self.loops); + self.allow_insert_closure &= + can_move_expr_to_closure_no_visit(self.cx, expr, &self.loops, &self.locals); // Sub expressions are no longer in the tail position. self.is_single_insert = false; self.in_tail_pos = false; @@ -507,6 +515,12 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { }, } } + + fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) { + p.each_binding_or_first(&mut |_, id, _, _| { + self.locals.insert(id); + }); + } } struct InsertSearchResults<'tcx> { @@ -632,6 +646,7 @@ fn find_insert_calls( in_tail_pos: true, is_single_insert: true, loops: Vec::new(), + locals: HirIdSet::default(), }; s.visit_expr(expr); let allow_insert_closure = s.allow_insert_closure; diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs index f72a1e446d55c..8714ce90164c8 100644 --- a/clippy_lints/src/eval_order_dependence.rs +++ b/clippy_lints/src/eval_order_dependence.rs @@ -15,8 +15,8 @@ declare_clippy_lint! { /// order of sub-expressions. /// /// ### Why is this bad? - /// It is often confusing to read. In addition, the - /// sub-expression evaluation order for Rust is not well documented. + /// It is often confusing to read. As described [here](https://doc.rust-lang.org/reference/expressions.html?highlight=subexpression#evaluation-order-of-operands), + /// the operands of these expressions are evaluated before applying the effects of the expression. /// /// ### Known problems /// Code which intentionally depends on the evaluation diff --git a/clippy_lints/src/feature_name.rs b/clippy_lints/src/feature_name.rs new file mode 100644 index 0000000000000..eef1407a80cf1 --- /dev/null +++ b/clippy_lints/src/feature_name.rs @@ -0,0 +1,164 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::{diagnostics::span_lint, is_lint_allowed}; +use rustc_hir::{Crate, CRATE_HIR_ID}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::source_map::DUMMY_SP; + +declare_clippy_lint! { + /// ### What it does + /// Checks for feature names with prefix `use-`, `with-` or suffix `-support` + /// + /// ### Why is this bad? + /// These prefixes and suffixes have no significant meaning. + /// + /// ### Example + /// ```toml + /// # The `Cargo.toml` with feature name redundancy + /// [features] + /// default = ["use-abc", "with-def", "ghi-support"] + /// use-abc = [] // redundant + /// with-def = [] // redundant + /// ghi-support = [] // redundant + /// ``` + /// + /// Use instead: + /// ```toml + /// [features] + /// default = ["abc", "def", "ghi"] + /// abc = [] + /// def = [] + /// ghi = [] + /// ``` + /// + pub REDUNDANT_FEATURE_NAMES, + cargo, + "usage of a redundant feature name" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for negative feature names with prefix `no-` or `not-` + /// + /// ### Why is this bad? + /// Features are supposed to be additive, and negatively-named features violate it. + /// + /// ### Example + /// ```toml + /// # The `Cargo.toml` with negative feature names + /// [features] + /// default = [] + /// no-abc = [] + /// not-def = [] + /// + /// ``` + /// Use instead: + /// ```toml + /// [features] + /// default = ["abc", "def"] + /// abc = [] + /// def = [] + /// + /// ``` + pub NEGATIVE_FEATURE_NAMES, + cargo, + "usage of a negative feature name" +} + +declare_lint_pass!(FeatureName => [REDUNDANT_FEATURE_NAMES, NEGATIVE_FEATURE_NAMES]); + +static PREFIXES: [&str; 8] = ["no-", "no_", "not-", "not_", "use-", "use_", "with-", "with_"]; +static SUFFIXES: [&str; 2] = ["-support", "_support"]; + +fn is_negative_prefix(s: &str) -> bool { + s.starts_with("no") +} + +fn lint(cx: &LateContext<'_>, feature: &str, substring: &str, is_prefix: bool) { + let is_negative = is_prefix && is_negative_prefix(substring); + span_lint_and_help( + cx, + if is_negative { + NEGATIVE_FEATURE_NAMES + } else { + REDUNDANT_FEATURE_NAMES + }, + DUMMY_SP, + &format!( + "the \"{}\" {} in the feature name \"{}\" is {}", + substring, + if is_prefix { "prefix" } else { "suffix" }, + feature, + if is_negative { "negative" } else { "redundant" } + ), + None, + &format!( + "consider renaming the feature to \"{}\"{}", + if is_prefix { + feature.strip_prefix(substring) + } else { + feature.strip_suffix(substring) + } + .unwrap(), + if is_negative { + ", but make sure the feature adds functionality" + } else { + "" + } + ), + ); +} + +impl LateLintPass<'_> for FeatureName { + fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { + if is_lint_allowed(cx, REDUNDANT_FEATURE_NAMES, CRATE_HIR_ID) + && is_lint_allowed(cx, NEGATIVE_FEATURE_NAMES, CRATE_HIR_ID) + { + return; + } + + let metadata = unwrap_cargo_metadata!(cx, REDUNDANT_FEATURE_NAMES, false); + + for package in metadata.packages { + let mut features: Vec<&String> = package.features.keys().collect(); + features.sort(); + for feature in features { + let prefix_opt = { + let i = PREFIXES.partition_point(|prefix| prefix < &feature.as_str()); + if i > 0 && feature.starts_with(PREFIXES[i - 1]) { + Some(PREFIXES[i - 1]) + } else { + None + } + }; + if let Some(prefix) = prefix_opt { + lint(cx, feature, prefix, true); + } + + let suffix_opt: Option<&str> = { + let i = SUFFIXES.partition_point(|suffix| { + suffix.bytes().rev().cmp(feature.bytes().rev()) == std::cmp::Ordering::Less + }); + if i > 0 && feature.ends_with(SUFFIXES[i - 1]) { + Some(SUFFIXES[i - 1]) + } else { + None + } + }; + if let Some(suffix) = suffix_opt { + lint(cx, feature, suffix, false); + } + } + } + } +} + +#[test] +fn test_prefixes_sorted() { + let mut sorted_prefixes = PREFIXES; + sorted_prefixes.sort_unstable(); + assert_eq!(PREFIXES, sorted_prefixes); + let mut sorted_suffixes = SUFFIXES; + sorted_suffixes.sort_by(|a, b| a.bytes().rev().cmp(b.bytes().rev())); + assert_eq!(SUFFIXES, sorted_suffixes); +} diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index d12482e7b7bb9..eda611117babf 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -332,8 +332,6 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { ), Applicability::MachineApplicable, ); - - return; } } } @@ -364,22 +362,22 @@ fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option { if_chain! { if let ExprKind::MethodCall( PathSegment { ident: lmethod_name, .. }, - ref _lspan, - largs, + _lspan, + [largs_0, largs_1, ..], _ - ) = add_lhs.kind; + ) = &add_lhs.kind; if let ExprKind::MethodCall( PathSegment { ident: rmethod_name, .. }, - ref _rspan, - rargs, + _rspan, + [rargs_0, rargs_1, ..], _ - ) = add_rhs.kind; + ) = &add_rhs.kind; if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"; - if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), &largs[1]); - if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), &rargs[1]); + if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), largs_1); + if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), rargs_1); if Int(2) == lvalue && Int(2) == rvalue; then { - return Some(format!("{}.hypot({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."))); + return Some(format!("{}.hypot({})", Sugg::hir(cx, largs_0, ".."), Sugg::hir(cx, rargs_0, ".."))); } } } @@ -409,8 +407,8 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { if cx.typeck_results().expr_ty(lhs).is_floating_point(); if let Some((value, _)) = constant(cx, cx.typeck_results(), rhs); if F32(1.0) == value || F64(1.0) == value; - if let ExprKind::MethodCall(path, _, method_args, _) = lhs.kind; - if cx.typeck_results().expr_ty(&method_args[0]).is_floating_point(); + if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &lhs.kind; + if cx.typeck_results().expr_ty(self_arg).is_floating_point(); if path.ident.name.as_str() == "exp"; then { span_lint_and_sugg( @@ -421,7 +419,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { "consider using", format!( "{}.exp_m1()", - Sugg::hir(cx, &method_args[0], "..") + Sugg::hir(cx, self_arg, "..") ), Applicability::MachineApplicable, ); @@ -619,8 +617,8 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { rhs, ) = &expr.kind; if are_same_base_logs(cx, lhs, rhs); - if let ExprKind::MethodCall(_, _, largs, _) = lhs.kind; - if let ExprKind::MethodCall(_, _, rargs, _) = rhs.kind; + if let ExprKind::MethodCall(_, _, [largs_self, ..], _) = &lhs.kind; + if let ExprKind::MethodCall(_, _, [rargs_self, ..], _) = &rhs.kind; then { span_lint_and_sugg( cx, @@ -628,7 +626,7 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.span, "log base can be expressed more clearly", "consider using", - format!("{}.log({})", Sugg::hir(cx, &largs[0], ".."), Sugg::hir(cx, &rargs[0], ".."),), + format!("{}.log({})", Sugg::hir(cx, largs_self, ".."), Sugg::hir(cx, rargs_self, ".."),), Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index ea6193acbe845..77d08081c07f1 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -26,7 +26,6 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); - return; } else if is_public && !is_proc_macro(cx.sess(), attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) { check_must_use_candidate( cx, diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs index 7dad1c31150e2..ef72b88b3c773 100644 --- a/clippy_lints/src/if_let_mutex.rs +++ b/clippy_lints/src/if_let_mutex.rs @@ -138,12 +138,12 @@ impl<'tcx, 'l> ArmVisitor<'tcx, 'l> { fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { if_chain! { - if let ExprKind::MethodCall(path, _span, args, _) = &expr.kind; + if let ExprKind::MethodCall(path, _span, [self_arg, ..], _) = &expr.kind; if path.ident.as_str() == "lock"; - let ty = cx.typeck_results().expr_ty(&args[0]); + let ty = cx.typeck_results().expr_ty(self_arg); if is_type_diagnostic_item(cx, ty, sym!(mutex_type)); then { - Some(&args[0]) + Some(self_arg) } else { None } diff --git a/clippy_lints/src/if_let_some_result.rs b/clippy_lints/src/if_let_some_result.rs index fb5637fcec183..adcd78ed0d427 100644 --- a/clippy_lints/src/if_let_some_result.rs +++ b/clippy_lints/src/if_let_some_result.rs @@ -46,10 +46,10 @@ impl<'tcx> LateLintPass<'tcx> for OkIfLet { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { //begin checking variables if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr); - if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = let_expr.kind; //check is expr.ok() has type Result.ok(, _) - if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = let_pat.kind; //get operation + if let ExprKind::MethodCall(_, ok_span, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result.ok(, _) + if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = let_pat.kind; //get operation if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized; - if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym::result_type); + if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::result_type); if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some"; then { diff --git a/clippy_lints/src/let_if_seq.rs b/clippy_lints/src/let_if_seq.rs index 0594b73dd3837..7f2c7b707f0b1 100644 --- a/clippy_lints/src/let_if_seq.rs +++ b/clippy_lints/src/let_if_seq.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; -use clippy_utils::{path_to_local_id, visitors::LocalUsedVisitor}; +use clippy_utils::{path_to_local_id, visitors::is_local_used}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -65,11 +65,10 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq { if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind; if let hir::StmtKind::Expr(if_) = expr.kind; if let hir::ExprKind::If(hir::Expr { kind: hir::ExprKind::DropTemps(cond), ..}, then, else_) = if_.kind; - let mut used_visitor = LocalUsedVisitor::new(cx, canonical_id); - if !used_visitor.check_expr(cond); + if !is_local_used(cx, *cond, canonical_id); if let hir::ExprKind::Block(then, _) = then.kind; if let Some(value) = check_assign(cx, canonical_id, &*then); - if !used_visitor.check_expr(value); + if !is_local_used(cx, value, canonical_id); then { let span = stmt.span.to(if_.span); @@ -148,15 +147,13 @@ fn check_assign<'tcx>( if let hir::ExprKind::Assign(var, value, _) = expr.kind; if path_to_local_id(var, decl); then { - let mut v = LocalUsedVisitor::new(cx, decl); - - if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| v.check_stmt(stmt)) { - return None; + if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| is_local_used(cx, stmt, decl)) { + None + } else { + Some(value) } - - return Some(value); + } else { + None } } - - None } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 19719502870bd..92a13be81f411 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -187,6 +187,7 @@ mod dbg_macro; mod default; mod default_numeric_fallback; mod dereference; +mod derivable_impls; mod derive; mod disallowed_method; mod disallowed_script_idents; @@ -211,6 +212,7 @@ mod exhaustive_items; mod exit; mod explicit_write; mod fallible_impl_from; +mod feature_name; mod float_equality_without_abs; mod float_literal; mod floating_point_arithmetic; @@ -272,6 +274,7 @@ mod missing_const_for_fn; mod missing_doc; mod missing_enforced_import_rename; mod missing_inline; +mod module_style; mod modulo_arithmetic; mod multiple_crate_versions; mod mut_key; @@ -287,6 +290,7 @@ mod needless_borrow; mod needless_borrowed_ref; mod needless_continue; mod needless_for_each; +mod needless_option_as_deref; mod needless_pass_by_value; mod needless_question_mark; mod needless_update; @@ -584,6 +588,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: default::FIELD_REASSIGN_WITH_DEFAULT, default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK, dereference::EXPLICIT_DEREF_METHODS, + derivable_impls::DERIVABLE_IMPLS, derive::DERIVE_HASH_XOR_EQ, derive::DERIVE_ORD_XOR_PARTIAL_ORD, derive::EXPL_IMPL_CLONE_ON_COPY, @@ -625,6 +630,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: exit::EXIT, explicit_write::EXPLICIT_WRITE, fallible_impl_from::FALLIBLE_IMPL_FROM, + feature_name::NEGATIVE_FEATURE_NAMES, + feature_name::REDUNDANT_FEATURE_NAMES, float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS, float_literal::EXCESSIVE_PRECISION, float_literal::LOSSY_FLOAT_LITERAL, @@ -770,6 +777,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: methods::MANUAL_FILTER_MAP, methods::MANUAL_FIND_MAP, methods::MANUAL_SATURATING_ARITHMETIC, + methods::MANUAL_SPLIT_ONCE, methods::MANUAL_STR_REPEAT, methods::MAP_COLLECT_RESULT_UNIT, methods::MAP_FLATTEN, @@ -822,6 +830,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES, missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, + module_style::MOD_MODULE_FILES, + module_style::SELF_NAMED_MODULE_FILES, modulo_arithmetic::MODULO_ARITHMETIC, multiple_crate_versions::MULTIPLE_CRATE_VERSIONS, mut_key::MUTABLE_KEY_TYPE, @@ -840,6 +850,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, needless_continue::NEEDLESS_CONTINUE, needless_for_each::NEEDLESS_FOR_EACH, + needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF, needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, needless_question_mark::NEEDLESS_QUESTION_MARK, needless_update::NEEDLESS_UPDATE, @@ -1031,6 +1042,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES), LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), + LintId::of(module_style::MOD_MODULE_FILES), + LintId::of(module_style::SELF_NAMED_MODULE_FILES), LintId::of(modulo_arithmetic::MODULO_ARITHMETIC), LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN), LintId::of(panic_unimplemented::PANIC), @@ -1122,7 +1135,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(needless_for_each::NEEDLESS_FOR_EACH), LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), LintId::of(non_expressive_names::SIMILAR_NAMES), - LintId::of(option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE), LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF), LintId::of(ranges::RANGE_MINUS_ONE), @@ -1193,10 +1205,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(collapsible_if::COLLAPSIBLE_IF), LintId::of(collapsible_match::COLLAPSIBLE_MATCH), LintId::of(comparison_chain::COMPARISON_CHAIN), - LintId::of(copies::BRANCHES_SHARING_CODE), LintId::of(copies::IFS_SAME_COND), LintId::of(copies::IF_SAME_THEN_ELSE), LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), + LintId::of(derivable_impls::DERIVABLE_IMPLS), LintId::of(derive::DERIVE_HASH_XOR_EQ), LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), LintId::of(doc::MISSING_SAFETY_DOC), @@ -1316,6 +1328,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(methods::MANUAL_FILTER_MAP), LintId::of(methods::MANUAL_FIND_MAP), LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), + LintId::of(methods::MANUAL_SPLIT_ONCE), LintId::of(methods::MANUAL_STR_REPEAT), LintId::of(methods::MAP_COLLECT_RESULT_UNIT), LintId::of(methods::MAP_IDENTITY), @@ -1366,6 +1379,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(needless_bool::NEEDLESS_BOOL), LintId::of(needless_borrow::NEEDLESS_BORROW), LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF), LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(needless_update::NEEDLESS_UPDATE), LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), @@ -1581,7 +1595,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(booleans::NONMINIMAL_BOOL), LintId::of(casts::CHAR_LIT_AS_U8), LintId::of(casts::UNNECESSARY_CAST), - LintId::of(copies::BRANCHES_SHARING_CODE), + LintId::of(derivable_impls::DERIVABLE_IMPLS), LintId::of(double_comparison::DOUBLE_COMPARISONS), LintId::of(double_parens::DOUBLE_PARENS), LintId::of(duration_subsec::DURATION_SUBSEC), @@ -1614,6 +1628,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(methods::ITER_COUNT), LintId::of(methods::MANUAL_FILTER_MAP), LintId::of(methods::MANUAL_FIND_MAP), + LintId::of(methods::MANUAL_SPLIT_ONCE), LintId::of(methods::MAP_IDENTITY), LintId::of(methods::OPTION_AS_REF_DEREF), LintId::of(methods::OPTION_FILTER_MAP), @@ -1628,6 +1643,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(needless_bool::BOOL_COMPARISON), LintId::of(needless_bool::NEEDLESS_BOOL), LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), + LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF), LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), LintId::of(needless_update::NEEDLESS_UPDATE), LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), @@ -1779,6 +1795,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![ LintId::of(cargo_common_metadata::CARGO_COMMON_METADATA), + LintId::of(feature_name::NEGATIVE_FEATURE_NAMES), + LintId::of(feature_name::REDUNDANT_FEATURE_NAMES), LintId::of(multiple_crate_versions::MULTIPLE_CRATE_VERSIONS), LintId::of(wildcard_dependencies::WILDCARD_DEPENDENCIES), ]); @@ -1786,6 +1804,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), + LintId::of(copies::BRANCHES_SHARING_CODE), LintId::of(disallowed_method::DISALLOWED_METHOD), LintId::of(disallowed_type::DISALLOWED_TYPE), LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), @@ -1797,6 +1816,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), LintId::of(mutex_atomic::MUTEX_INTEGER), LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), + LintId::of(option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE), LintId::of(regex::TRIVIAL_REGEX), @@ -1835,7 +1855,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(serde_api::SerdeApi)); let vec_box_size_threshold = conf.vec_box_size_threshold; let type_complexity_threshold = conf.type_complexity_threshold; - store.register_late_pass(move || Box::new(types::Types::new(vec_box_size_threshold, type_complexity_threshold))); + let avoid_breaking_exported_api = conf.avoid_breaking_exported_api; + store.register_late_pass(move || Box::new(types::Types::new( + vec_box_size_threshold, + type_complexity_threshold, + avoid_breaking_exported_api, + ))); store.register_late_pass(|| Box::new(booleans::NonminimalBool)); store.register_late_pass(|| Box::new(needless_bitwise_bool::NeedlessBitwiseBool)); store.register_late_pass(|| Box::new(eq_op::EqOp)); @@ -1846,9 +1871,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(ptr::Ptr)); store.register_late_pass(|| Box::new(ptr_eq::PtrEq)); store.register_late_pass(|| Box::new(needless_bool::NeedlessBool)); + store.register_late_pass(|| Box::new(needless_option_as_deref::OptionNeedlessDeref)); store.register_late_pass(|| Box::new(needless_bool::BoolComparison)); store.register_late_pass(|| Box::new(needless_for_each::NeedlessForEach)); - store.register_late_pass(|| Box::new(approx_const::ApproxConstant)); store.register_late_pass(|| Box::new(misc::MiscLints)); store.register_late_pass(|| Box::new(eta_reduction::EtaReduction)); store.register_late_pass(|| Box::new(identity_op::IdentityOp)); @@ -1877,6 +1902,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); let avoid_breaking_exported_api = conf.avoid_breaking_exported_api; + store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv))); store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv))); store.register_late_pass(move || Box::new(matches::Matches::new(msrv))); store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustive::new(msrv))); @@ -1920,6 +1946,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(panic_unimplemented::PanicUnimplemented)); store.register_late_pass(|| Box::new(strings::StringLitAsBytes)); store.register_late_pass(|| Box::new(derive::Derive)); + store.register_late_pass(|| Box::new(derivable_impls::DerivableImpls)); store.register_late_pass(|| Box::new(get_last_with_len::GetLastWithLen)); store.register_late_pass(|| Box::new(drop_forget_ref::DropForgetRef)); store.register_late_pass(|| Box::new(empty_enum::EmptyEnum)); @@ -2092,7 +2119,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10)); store.register_late_pass(|| Box::new(manual_map::ManualMap)); store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv))); - store.register_early_pass(|| Box::new(bool_assert_comparison::BoolAssertComparison)); + store.register_late_pass(|| Box::new(bool_assert_comparison::BoolAssertComparison)); + store.register_early_pass(move || Box::new(module_style::ModStyle)); store.register_late_pass(|| Box::new(unused_async::UnusedAsync)); let disallowed_types = conf.disallowed_types.iter().cloned().collect::>(); store.register_late_pass(move || Box::new(disallowed_type::DisallowedType::new(&disallowed_types))); @@ -2102,6 +2130,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(&scripts))); store.register_late_pass(|| Box::new(strlen_on_c_strings::StrlenOnCStrings)); store.register_late_pass(move || Box::new(self_named_constructors::SelfNamedConstructors)); + store.register_late_pass(move || Box::new(feature_name::FeatureName)); } #[rustfmt::skip] diff --git a/clippy_lints/src/loops/for_kv_map.rs b/clippy_lints/src/loops/for_kv_map.rs index 82bf49f5b49a4..68bef2f4c8bbb 100644 --- a/clippy_lints/src/loops/for_kv_map.rs +++ b/clippy_lints/src/loops/for_kv_map.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::visitors::LocalUsedVisitor; +use clippy_utils::visitors::is_local_used; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -66,9 +66,7 @@ pub(super) fn check<'tcx>( fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool { match *pat { PatKind::Wild => true, - PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => { - !LocalUsedVisitor::new(cx, id).check_expr(body) - }, + PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => !is_local_used(cx, body, id), _ => false, } } diff --git a/clippy_lints/src/loops/manual_flatten.rs b/clippy_lints/src/loops/manual_flatten.rs index 5852674da5780..5b6e27085d580 100644 --- a/clippy_lints/src/loops/manual_flatten.rs +++ b/clippy_lints/src/loops/manual_flatten.rs @@ -2,6 +2,7 @@ use super::utils::make_iterator_snippet; use super::MANUAL_FLATTEN; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher; +use clippy_utils::visitors::is_local_used; use clippy_utils::{is_lang_ctor, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -37,7 +38,8 @@ pub(super) fn check<'tcx>( if_chain! { if let Some(inner_expr) = inner_expr; - if let Some(higher::IfLet { let_pat, let_expr, if_else: None, .. }) = higher::IfLet::hir(cx, inner_expr); + if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: None }) + = higher::IfLet::hir(cx, inner_expr); // Ensure match_expr in `if let` statement is the same as the pat from the for-loop if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; if path_to_local_id(let_expr, pat_hir_id); @@ -46,6 +48,8 @@ pub(super) fn check<'tcx>( let some_ctor = is_lang_ctor(cx, qpath, OptionSome); let ok_ctor = is_lang_ctor(cx, qpath, ResultOk); if some_ctor || ok_ctor; + // Ensure epxr in `if let` is not used afterwards + if !is_local_used(cx, if_then, pat_hir_id); then { let if_let_type = if some_ctor { "Some" } else { "Ok" }; // Prepare the error message diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index bd9de5e08d736..2860cb68f42f2 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -397,6 +397,21 @@ declare_clippy_lint! { /// ### Why is this bad? /// One might think that modifying the mutable variable changes the loop bounds /// + /// ### Known problems + /// False positive when mutation is followed by a `break`, but the `break` is not immediately + /// after the mutation: + /// + /// ```rust + /// let mut x = 5; + /// for _ in 0..x { + /// x += 1; // x is a range bound that is mutated + /// ..; // some other expression + /// break; // leaves the loop, so mutation is not an issue + /// } + /// ``` + /// + /// False positive on nested loops ([#6072](https://github.com/rust-lang/rust-clippy/issues/6072)) + /// /// ### Example /// ```rust /// let mut foo = 42; @@ -580,8 +595,8 @@ impl<'tcx> LateLintPass<'tcx> for Loops { while_let_on_iterator::check(cx, expr); - if let Some(higher::While { if_cond, if_then, .. }) = higher::While::hir(&expr) { - while_immutable_condition::check(cx, if_cond, if_then); + if let Some(higher::While { condition, body }) = higher::While::hir(expr) { + while_immutable_condition::check(cx, condition, body); } needless_collect::check(expr, cx); diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 344dc5074d369..358d53e8859d0 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -1,24 +1,27 @@ use super::MUT_RANGE_BOUND; -use clippy_utils::diagnostics::span_lint; -use clippy_utils::{higher, path_to_local}; +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::{get_enclosing_block, higher, path_to_local}; use if_chain::if_chain; -use rustc_hir::{BindingAnnotation, Expr, HirId, Node, PatKind}; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; +use rustc_middle::hir::map::Map; use rustc_middle::{mir::FakeReadCause, ty}; use rustc_span::source_map::Span; use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) { - if let Some(higher::Range { - start: Some(start), - end: Some(end), - .. - }) = higher::Range::hir(arg) - { - let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)]; - if mut_ids[0].is_some() || mut_ids[1].is_some() { - let (span_low, span_high) = check_for_mutation(cx, body, &mut_ids); + if_chain! { + if let Some(higher::Range { + start: Some(start), + end: Some(end), + .. + }) = higher::Range::hir(arg); + let (mut_id_start, mut_id_end) = (check_for_mutability(cx, start), check_for_mutability(cx, end)); + if mut_id_start.is_some() || mut_id_end.is_some(); + then { + let (span_low, span_high) = check_for_mutation(cx, body, mut_id_start, mut_id_end); mut_warn_with_span(cx, span_low); mut_warn_with_span(cx, span_high); } @@ -27,11 +30,13 @@ pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) { fn mut_warn_with_span(cx: &LateContext<'_>, span: Option) { if let Some(sp) = span { - span_lint( + span_lint_and_note( cx, MUT_RANGE_BOUND, sp, - "attempt to mutate range bound within loop; note that the range of the loop is unchanged", + "attempt to mutate range bound within loop", + None, + "the range of the loop is unchanged", ); } } @@ -51,12 +56,13 @@ fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option fn check_for_mutation<'tcx>( cx: &LateContext<'tcx>, body: &Expr<'_>, - bound_ids: &[Option], + bound_id_start: Option, + bound_id_end: Option, ) -> (Option, Option) { let mut delegate = MutatePairDelegate { cx, - hir_id_low: bound_ids[0], - hir_id_high: bound_ids[1], + hir_id_low: bound_id_start, + hir_id_high: bound_id_end, span_low: None, span_high: None, }; @@ -70,6 +76,7 @@ fn check_for_mutation<'tcx>( ) .walk_expr(body); }); + delegate.mutation_span() } @@ -87,10 +94,10 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) { if let ty::BorrowKind::MutBorrow = bk { if let PlaceBase::Local(id) = cmt.place.base { - if Some(id) == self.hir_id_low { + if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)); } - if Some(id) == self.hir_id_high { + if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)); } } @@ -99,10 +106,10 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) { if let PlaceBase::Local(id) = cmt.place.base { - if Some(id) == self.hir_id_low { + if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id)); } - if Some(id) == self.hir_id_high { + if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) { self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id)); } } @@ -116,3 +123,52 @@ impl MutatePairDelegate<'_, '_> { (self.span_low, self.span_high) } } + +struct BreakAfterExprVisitor { + hir_id: HirId, + past_expr: bool, + past_candidate: bool, + break_after_expr: bool, +} + +impl BreakAfterExprVisitor { + pub fn is_found(cx: &LateContext<'_>, hir_id: HirId) -> bool { + let mut visitor = BreakAfterExprVisitor { + hir_id, + past_expr: false, + past_candidate: false, + break_after_expr: false, + }; + + get_enclosing_block(cx, hir_id).map_or(false, |block| { + visitor.visit_block(block); + visitor.break_after_expr + }) + } +} + +impl intravisit::Visitor<'tcx> for BreakAfterExprVisitor { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::None + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + if self.past_candidate { + return; + } + + if expr.hir_id == self.hir_id { + self.past_expr = true; + } else if self.past_expr { + if matches!(&expr.kind, ExprKind::Break(..)) { + self.break_after_expr = true; + } + + self.past_candidate = true; + } else { + intravisit::walk_expr(self, expr); + } + } +} diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index 51d7def137e40..f90ed7397e18e 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -26,7 +26,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator); then { let ty = cx.typeck_results().expr_ty(&args[0]); - let mut applicability = Applicability::MachineApplicable; + let mut applicability = Applicability::MaybeIncorrect; let is_empty_sugg = "next().is_none()".to_string(); let method_name = &*method.ident.name.as_str(); let sugg = if is_type_diagnostic_item(cx, ty, sym::vec_type) || @@ -113,7 +113,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo (stmt.span, String::new()), (iter_call.span, iter_replacement) ], - Applicability::MachineApplicable,// MaybeIncorrect, + Applicability::MaybeIncorrect, ); }, ); diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 3f77e7af927ad..e8f3550283a49 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -2,10 +2,8 @@ use super::NEEDLESS_RANGE_LOOP; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; -use clippy_utils::visitors::LocalUsedVisitor; -use clippy_utils::{ - contains_name, higher, is_integer_const, match_trait_method, path_to_local_id, paths, sugg, SpanlessEq, -}; +use clippy_utils::visitors::is_local_used; +use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq}; use if_chain::if_chain; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -256,43 +254,36 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> { if let ExprKind::Path(ref seqpath) = seqexpr.kind; if let QPath::Resolved(None, seqvar) = *seqpath; if seqvar.segments.len() == 1; - let index_used_directly = path_to_local_id(idx, self.var); - let indexed_indirectly = { - let mut used_visitor = LocalUsedVisitor::new(self.cx, self.var); - walk_expr(&mut used_visitor, idx); - used_visitor.used - }; - if indexed_indirectly || index_used_directly; + if is_local_used(self.cx, idx, self.var); then { if self.prefer_mutable { self.indexed_mut.insert(seqvar.segments[0].ident.name); } + let index_used_directly = matches!(idx.kind, ExprKind::Path(_)); let res = self.cx.qpath_res(seqpath, seqexpr.hir_id); match res { Res::Local(hir_id) => { let parent_id = self.cx.tcx.hir().get_parent_item(expr.hir_id); let parent_def_id = self.cx.tcx.hir().local_def_id(parent_id); let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id); - if indexed_indirectly { - self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent)); - } if index_used_directly { self.indexed_directly.insert( seqvar.segments[0].ident.name, (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)), ); + } else { + self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent)); } return false; // no need to walk further *on the variable* } Res::Def(DefKind::Static | DefKind::Const, ..) => { - if indexed_indirectly { - self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None); - } if index_used_directly { self.indexed_directly.insert( seqvar.segments[0].ident.name, (None, self.cx.typeck_results().node_type(seqexpr.hir_id)), ); + } else { + self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None); } return false; // no need to walk further *on the variable* } @@ -310,10 +301,10 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { if_chain! { // a range index op - if let ExprKind::MethodCall(meth, _, args, _) = expr.kind; + if let ExprKind::MethodCall(meth, _, [args_0, args_1, ..], _) = &expr.kind; if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX)) || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT)); - if !self.check(&args[1], &args[0], expr); + if !self.check(args_1, args_0, expr); then { return } } diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 2c46971d5f741..41956650c9f4e 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -87,7 +87,7 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult { let stmts = block.stmts.iter().map(stmt_to_expr); - let expr = once(block.expr.as_deref()); + let expr = once(block.expr); let mut iter = stmts.chain(expr).flatten(); never_loop_expr_seq(&mut iter, main_loop_id) } @@ -100,7 +100,7 @@ fn never_loop_expr_seq<'a, T: Iterator>>(es: &mut T, main_lo fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> { match stmt.kind { StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some(e), - StmtKind::Local(local) => local.init.as_deref(), + StmtKind::Local(local) => local.init, StmtKind::Item(..) => None, } } diff --git a/clippy_lints/src/loops/while_let_loop.rs b/clippy_lints/src/loops/while_let_loop.rs index d6d3315e0a83e..1848f5b5de2f2 100644 --- a/clippy_lints/src/loops/while_let_loop.rs +++ b/clippy_lints/src/loops/while_let_loop.rs @@ -24,13 +24,13 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &' } } - if let ExprKind::Match(ref matchexpr, ref arms, MatchSource::Normal) = inner.kind { + if let ExprKind::Match(matchexpr, arms, MatchSource::Normal) = inner.kind { if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() - && is_simple_break_expr(&arms[1].body) + && is_simple_break_expr(arms[1].body) { - could_be_while_let(cx, expr, &arms[0].pat, matchexpr); + could_be_while_let(cx, expr, arms[0].pat, matchexpr); } } } diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 0757d329125cb..79527e3bfa92f 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -14,12 +14,7 @@ use rustc_span::{symbol::sym, Span, Symbol}; pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain! { - if let Some(higher::WhileLet { - if_then, - let_pat, - let_expr, - .. - }) = higher::WhileLet::hir(expr); + if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr); // check for `Some(..)` pattern if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind; if let Res::Def(_, pat_did) = pat_path.res; diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 39f7ade3f81f6..7627e0fb28956 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -48,8 +48,7 @@ pub struct MacroRefData { impl MacroRefData { pub fn new(name: String, callee: Span, cx: &LateContext<'_>) -> Self { let sm = cx.sess().source_map(); - let mut path = sm.filename_for_diagnostics(&sm.span_to_filename(callee)) - .to_string(); + let mut path = sm.filename_for_diagnostics(&sm.span_to_filename(callee)).to_string(); // std lib paths are <::std::module::file type> // so remove brackets, space and type. diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 161d884149075..b5f573cb104e9 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -1,16 +1,18 @@ use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF}; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher; +use clippy_utils::higher::IfLetOrMatch; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; use clippy_utils::{ can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id, - peel_hir_expr_refs, + peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, }; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; -use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, Mutability, Pat, PatKind}; +use rustc_hir::{ + def::Res, Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath, +}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -44,168 +46,169 @@ declare_lint_pass!(ManualMap => [MANUAL_MAP]); impl LateLintPass<'_> for ManualMap { #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some(higher::IfLet { - let_pat, - let_expr, - if_then, - if_else: Some(if_else), - }) = higher::IfLet::hir(cx, expr) - { - manage_lint(cx, expr, (&let_pat.kind, if_then), (&PatKind::Wild, if_else), let_expr); + let (scrutinee, then_pat, then_body, else_pat, else_body) = match IfLetOrMatch::parse(cx, expr) { + Some(IfLetOrMatch::IfLet(scrutinee, pat, body, Some(r#else))) => (scrutinee, pat, body, None, r#else), + Some(IfLetOrMatch::Match( + scrutinee, + [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }], + _, + )) => (scrutinee, arm1.pat, arm1.body, Some(arm2.pat), arm2.body), + _ => return, + }; + if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { + return; } - if let ExprKind::Match(scrutinee, [then @ Arm { guard: None, .. }, r#else @ Arm { guard: None, .. }], _) = - expr.kind + let (scrutinee_ty, ty_ref_count, ty_mutability) = + peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee)); + if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type) + && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type)) { - manage_lint( - cx, - expr, - (&then.pat.kind, then.body), - (&r#else.pat.kind, r#else.body), - scrutinee, - ); + return; } - } -} - -fn manage_lint<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - then: (&'tcx PatKind<'_>, &'tcx Expr<'_>), - r#else: (&'tcx PatKind<'_>, &'tcx Expr<'_>), - scrut: &'tcx Expr<'_>, -) { - if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { - return; - } - let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrut)); - if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type) - && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type)) - { - return; - } + let expr_ctxt = expr.span.ctxt(); + let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( + try_parse_pattern(cx, then_pat, expr_ctxt), + else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)), + ) { + (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (else_body, pattern, ref_count, true) + }, + (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => { + (else_body, pattern, ref_count, false) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => { + (then_body, pattern, ref_count, true) + }, + (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => { + (then_body, pattern, ref_count, false) + }, + _ => return, + }; - let (then_pat, then_expr) = then; - let (else_pat, else_expr) = r#else; + // Top level or patterns aren't allowed in closures. + if matches!(some_pat.kind, PatKind::Or(_)) { + return; + } - let expr_ctxt = expr.span.ctxt(); - let (some_expr, some_pat, pat_ref_count, is_wild_none) = match ( - try_parse_pattern(cx, then_pat, expr_ctxt), - try_parse_pattern(cx, else_pat, expr_ctxt), - ) { - (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_expr) => { - (else_expr, pattern, ref_count, true) - }, - (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_expr) => { - (else_expr, pattern, ref_count, false) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_expr) => { - (then_expr, pattern, ref_count, true) - }, - (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_expr) => { - (then_expr, pattern, ref_count, false) - }, - _ => return, - }; + let some_expr = match get_some_expr(cx, some_expr, expr_ctxt) { + Some(expr) => expr, + None => return, + }; - // Top level or patterns aren't allowed in closures. - if matches!(some_pat.kind, PatKind::Or(_)) { - return; - } + // These two lints will go back and forth with each other. + if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit + && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) + { + return; + } - let some_expr = match get_some_expr(cx, some_expr, expr_ctxt) { - Some(expr) => expr, - None => return, - }; + // `map` won't perform any adjustments. + if !cx.typeck_results().expr_adjustments(some_expr).is_empty() { + return; + } - if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) { - return; - } + // Determine which binding mode to use. + let explicit_ref = some_pat.contains_explicit_ref_binding(); + let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability)); - // `map` won't perform any adjustments. - if !cx.typeck_results().expr_adjustments(some_expr).is_empty() { - return; - } + let as_ref_str = match binding_ref { + Some(Mutability::Mut) => ".as_mut()", + Some(Mutability::Not) => ".as_ref()", + None => "", + }; - if !can_move_expr_to_closure(cx, some_expr) { - return; - } - - // Determine which binding mode to use. - let explicit_ref = some_pat.contains_explicit_ref_binding(); - let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability)); + match can_move_expr_to_closure(cx, some_expr) { + Some(captures) => { + // Check if captures the closure will need conflict with borrows made in the scrutinee. + // TODO: check all the references made in the scrutinee expression. This will require interacting + // with the borrow checker. Currently only `[.]*` is checked for. + if let Some(binding_ref_mutability) = binding_ref { + let e = peel_hir_expr_while(scrutinee, |e| match e.kind { + ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), + _ => None, + }); + if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind { + match captures.get(l) { + Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return, + Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => { + return; + }, + Some(CaptureKind::Ref(Mutability::Not)) | None => (), + } + } + } + }, + None => return, + }; - let as_ref_str = match binding_ref { - Some(Mutability::Mut) => ".as_mut()", - Some(Mutability::Not) => ".as_ref()", - None => "", - }; + let mut app = Applicability::MachineApplicable; - let mut app = Applicability::MachineApplicable; + // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or + // it's being passed by value. + let scrutinee = peel_hir_expr_refs(scrutinee).0; + let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); + let scrutinee_str = + if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { + format!("({})", scrutinee_str) + } else { + scrutinee_str.into() + }; - // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or - // it's being passed by value. - let scrutinee = peel_hir_expr_refs(scrut).0; - let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); - let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { - format!("({})", scrutinee_str) - } else { - scrutinee_str.into() - }; + let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { + match can_pass_as_func(cx, id, some_expr) { + Some(func) if func.span.ctxt() == some_expr.span.ctxt() => { + snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() + }, + _ => { + if path_to_local_id(some_expr, id) + && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) + && binding_ref.is_some() + { + return; + } - let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind { - match can_pass_as_func(cx, id, some_expr) { - Some(func) if func.span.ctxt() == some_expr.span.ctxt() => { - snippet_with_applicability(cx, func.span, "..", &mut app).into_owned() - }, - _ => { - if path_to_local_id(some_expr, id) - && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id) - && binding_ref.is_some() - { - return; - } + // `ref` and `ref mut` annotations were handled earlier. + let annotation = if matches!(annotation, BindingAnnotation::Mutable) { + "mut " + } else { + "" + }; + format!( + "|{}{}| {}", + annotation, + some_binding, + snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0 + ) + }, + } + } else if !is_wild_none && explicit_ref.is_none() { + // TODO: handle explicit reference annotations. + format!( + "|{}| {}", + snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0, + snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0 + ) + } else { + // Refutable bindings and mixed reference annotations can't be handled by `map`. + return; + }; - // `ref` and `ref mut` annotations were handled earlier. - let annotation = if matches!(annotation, BindingAnnotation::Mutable) { - "mut " - } else { - "" - }; - format!( - "|{}{}| {}", - annotation, - some_binding, - snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0 - ) + span_lint_and_sugg( + cx, + MANUAL_MAP, + expr.span, + "manual implementation of `Option::map`", + "try this", + if else_pat.is_none() && is_else_clause(cx.tcx, expr) { + format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str) + } else { + format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str) }, - } - } else if !is_wild_none && explicit_ref.is_none() { - // TODO: handle explicit reference annotations. - format!( - "|{}| {}", - snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0, - snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0 - ) - } else { - // Refutable bindings and mixed reference annotations can't be handled by `map`. - return; - }; - - span_lint_and_sugg( - cx, - MANUAL_MAP, - expr.span, - "manual implementation of `Option::map`", - "try this", - if is_else_clause(cx.tcx, expr) { - format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str) - } else { - format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str) - }, - app, - ); + app, + ); + } } // Checks whether the expression could be passed as a function, or whether a closure is needed. @@ -213,7 +216,7 @@ fn manage_lint<'tcx>( fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { match expr.kind { ExprKind::Call(func, [arg]) - if path_to_local_id (arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() => + if path_to_local_id(arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() => { Some(func) }, @@ -235,28 +238,21 @@ enum OptionPat<'a> { // Try to parse into a recognized `Option` pattern. // i.e. `_`, `None`, `Some(..)`, or a reference to any of those. -fn try_parse_pattern( - cx: &LateContext<'tcx>, - pat_kind: &'tcx PatKind<'_>, - ctxt: SyntaxContext, -) -> Option> { - fn f( - cx: &LateContext<'tcx>, - pat_kind: &'tcx PatKind<'_>, - ref_count: usize, - ctxt: SyntaxContext, - ) -> Option> { - match pat_kind { +fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option> { + fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize, ctxt: SyntaxContext) -> Option> { + match pat.kind { PatKind::Wild => Some(OptionPat::Wild), - PatKind::Ref(ref_pat, _) => f(cx, &ref_pat.kind, ref_count + 1, ctxt), + PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None), - PatKind::TupleStruct(ref qpath, [pattern], _) if is_lang_ctor(cx, qpath, OptionSome) => { + PatKind::TupleStruct(ref qpath, [pattern], _) + if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt => + { Some(OptionPat::Some { pattern, ref_count }) }, _ => None, } } - f(cx, pat_kind, 0, ctxt) + f(cx, pat, 0, ctxt) } // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 3d0da472ddcbf..2f1ff567e844f 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -6,7 +6,7 @@ use clippy_utils::higher; use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs}; -use clippy_utils::visitors::LocalUsedVisitor; +use clippy_utils::visitors::is_local_used; use clippy_utils::{ get_parent_expr, in_macro, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs, path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, @@ -631,7 +631,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { check_match_single_binding(cx, ex, arms, expr); } } - if let ExprKind::Match(ref ex, ref arms, _) = expr.kind { + if let ExprKind::Match(ex, arms, _) = expr.kind { check_match_ref_pats(cx, ex, arms.iter().map(|el| el.pat), expr); } if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) { @@ -959,9 +959,7 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm // Looking for unused bindings (i.e.: `_e`) for pat in inner.iter() { if let PatKind::Binding(_, id, ident, None) = pat.kind { - if ident.as_str().starts_with('_') - && !LocalUsedVisitor::new(cx, id).check_expr(arm.body) - { + if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) { ident_bind_name = (&ident.name.as_str()).to_string(); matching_wild = true; } @@ -1196,7 +1194,7 @@ where let (first_sugg, msg, title); let span = ex.span.source_callsite(); - if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = ex.kind { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind { first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string())); msg = "try"; title = "you don't need to add `&` to both the expression and the patterns"; @@ -1207,7 +1205,7 @@ where } let remaining_suggs = pats.filter_map(|pat| { - if let PatKind::Ref(ref refp, _) = pat.kind { + if let PatKind::Ref(refp, _) = pat.kind { Some((pat.span, snippet(cx, refp.span, "..").to_string())) } else { None @@ -1367,7 +1365,7 @@ where find_bool_lit(&arm.2.kind, is_if_let).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty() }); then { - if let Some(ref last_pat) = last_pat_opt { + if let Some(last_pat) = last_pat_opt { if !is_wild(last_pat) { return false; } @@ -1829,13 +1827,13 @@ mod redundant_pattern_match { .. }) = higher::IfLet::hir(cx, expr) { - find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some()) + find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some()); } if let ExprKind::Match(op, arms, MatchSource::Normal) = &expr.kind { - find_sugg_for_match(cx, expr, op, arms) + find_sugg_for_match(cx, expr, op, arms); } if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) { - find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false) + find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false); } } diff --git a/clippy_lints/src/mem_forget.rs b/clippy_lints/src/mem_forget.rs index 07202a59c4b96..eb437dc47afb4 100644 --- a/clippy_lints/src/mem_forget.rs +++ b/clippy_lints/src/mem_forget.rs @@ -28,11 +28,11 @@ declare_lint_pass!(MemForget => [MEM_FORGET]); impl<'tcx> LateLintPass<'tcx> for MemForget { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Call(path_expr, args) = e.kind { + if let ExprKind::Call(path_expr, [ref first_arg, ..]) = e.kind { if let ExprKind::Path(ref qpath) = path_expr.kind { if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id() { if match_def_path(cx, def_id, &paths::MEM_FORGET) { - let forgot_ty = cx.typeck_results().expr_ty(&args[0]); + let forgot_ty = cx.typeck_results().expr_ty(first_arg); if forgot_ty.ty_adt_def().map_or(false, |def| def.has_dtor(cx.tcx)) { span_lint(cx, MEM_FORGET, e.span, "usage of `mem::forget` on `Drop` type"); diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 3d071c9081be1..1e6057a8fe969 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::{in_macro, is_diag_trait_item, is_lang_ctor, match_def_path, meets_msrv, msrvs, paths}; +use clippy_utils::ty::is_non_aggregate_primitive_type; +use clippy_utils::{in_macro, is_default_equivalent, is_lang_ctor, match_def_path, meets_msrv, msrvs, paths}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::def_id::DefId; use rustc_hir::LangItem::OptionNone; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -194,64 +194,37 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<' } } -/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent" -/// constructor from the std library -fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool { - let std_types_symbols = &[ - sym::string_type, - sym::vec_type, - sym::vecdeque_type, - sym::LinkedList, - sym::hashmap_type, - sym::BTreeMap, - sym::hashset_type, - sym::BTreeSet, - sym::BinaryHeap, - ]; - - if let QPath::TypeRelative(_, method) = path { - if method.ident.name == sym::new { - if let Some(impl_did) = cx.tcx.impl_of_method(def_id) { - if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() { - return std_types_symbols - .iter() - .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did)); - } - } +fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { + // disable lint for primitives + let expr_type = cx.typeck_results().expr_ty_adjusted(src); + if is_non_aggregate_primitive_type(expr_type) { + return; + } + // disable lint for Option since it is covered in another lint + if let ExprKind::Path(q) = &src.kind { + if is_lang_ctor(cx, q, OptionNone) { + return; } } - false -} + if is_default_equivalent(cx, src) && !in_external_macro(cx.tcx.sess, expr_span) { + span_lint_and_then( + cx, + MEM_REPLACE_WITH_DEFAULT, + expr_span, + "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`", + |diag| { + if !in_macro(expr_span) { + let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, "")); -fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) { - if_chain! { - if let ExprKind::Call(repl_func, _) = src.kind; - if !in_external_macro(cx.tcx.sess, expr_span); - if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; - if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); - if is_diag_trait_item(cx, repl_def_id, sym::Default) - || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath); - - then { - span_lint_and_then( - cx, - MEM_REPLACE_WITH_DEFAULT, - expr_span, - "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`", - |diag| { - if !in_macro(expr_span) { - let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, "")); - - diag.span_suggestion( - expr_span, - "consider using", - suggestion, - Applicability::MachineApplicable - ); - } + diag.span_suggestion( + expr_span, + "consider using", + suggestion, + Applicability::MachineApplicable, + ); } - ); - } + }, + ); } } diff --git a/clippy_lints/src/methods/filter_next.rs b/clippy_lints/src/methods/filter_next.rs index 172714f6b01c6..bcf8d93b602ef 100644 --- a/clippy_lints/src/methods/filter_next.rs +++ b/clippy_lints/src/methods/filter_next.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::is_trait_method; use clippy_utils::source::snippet; +use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; @@ -16,7 +16,10 @@ pub(super) fn check<'tcx>( filter_arg: &'tcx hir::Expr<'_>, ) { // lint if caller of `.filter().next()` is an Iterator - if is_trait_method(cx, expr, sym::Iterator) { + let recv_impls_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| { + implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[]) + }); + if recv_impls_iterator { let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \ `.find(..)` instead"; let filter_snippet = snippet(cx, filter_arg.span, ".."); diff --git a/clippy_lints/src/methods/manual_split_once.rs b/clippy_lints/src/methods/manual_split_once.rs new file mode 100644 index 0000000000000..e273186d05190 --- /dev/null +++ b/clippy_lints/src/methods/manual_split_once.rs @@ -0,0 +1,213 @@ +use clippy_utils::consts::{constant, Constant}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_context; +use clippy_utils::{is_diag_item_method, match_def_path, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, adjustment::Adjust}; +use rustc_span::{symbol::sym, Span, SyntaxContext}; + +use super::MANUAL_SPLIT_ONCE; + +pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) { + if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { + return; + } + + let ctxt = expr.span.ctxt(); + let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id)) { + Some(x) => x, + None => return, + }; + let (method_name, msg) = if method_name == "splitn" { + ("split_once", "manual implementation of `split_once`") + } else { + ("rsplit_once", "manual implementation of `rsplit_once`") + }; + + let mut app = Applicability::MachineApplicable; + let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0; + let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0; + + match usage.kind { + IterUsageKind::NextTuple => { + span_lint_and_sugg( + cx, + MANUAL_SPLIT_ONCE, + usage.span, + msg, + "try this", + format!("{}.{}({})", self_snip, method_name, pat_snip), + app, + ); + }, + IterUsageKind::Next => { + let self_deref = { + let adjust = cx.typeck_results().expr_adjustments(self_arg); + if adjust.is_empty() { + String::new() + } else if cx.typeck_results().expr_ty(self_arg).is_box() + || adjust + .iter() + .any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box()) + { + format!("&{}", "*".repeat(adjust.len() - 1)) + } else { + "*".repeat(adjust.len() - 2) + } + }; + let sugg = if usage.unwrap_kind.is_some() { + format!( + "{}.{}({}).map_or({}{}, |x| x.0)", + &self_snip, method_name, pat_snip, self_deref, &self_snip + ) + } else { + format!( + "Some({}.{}({}).map_or({}{}, |x| x.0))", + &self_snip, method_name, pat_snip, self_deref, &self_snip + ) + }; + + span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app); + }, + IterUsageKind::Second => { + let access_str = match usage.unwrap_kind { + Some(UnwrapKind::Unwrap) => ".unwrap().1", + Some(UnwrapKind::QuestionMark) => "?.1", + None => ".map(|x| x.1)", + }; + span_lint_and_sugg( + cx, + MANUAL_SPLIT_ONCE, + usage.span, + msg, + "try this", + format!("{}.{}({}){}", self_snip, method_name, pat_snip, access_str), + app, + ); + }, + } +} + +enum IterUsageKind { + Next, + Second, + NextTuple, +} + +enum UnwrapKind { + Unwrap, + QuestionMark, +} + +struct IterUsage { + kind: IterUsageKind, + unwrap_kind: Option, + span: Span, +} + +fn parse_iter_usage( + cx: &LateContext<'tcx>, + ctxt: SyntaxContext, + mut iter: impl Iterator)>, +) -> Option { + let (kind, span) = match iter.next() { + Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => { + let (name, args) = if let ExprKind::MethodCall(name, _, [_, args @ ..], _) = e.kind { + (name, args) + } else { + return None; + }; + let did = cx.typeck_results().type_dependent_def_id(e.hir_id)?; + let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?; + + match (&*name.ident.as_str(), args) { + ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Next, e.span), + ("next_tuple", []) => { + if_chain! { + if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE); + if let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind(); + if cx.tcx.is_diagnostic_item(sym::option_type, adt_def.did); + if let ty::Tuple(subs) = subs.type_at(0).kind(); + if subs.len() == 2; + then { + return Some(IterUsage { kind: IterUsageKind::NextTuple, span: e.span, unwrap_kind: None }); + } else { + return None; + } + } + }, + ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => { + if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) { + let span = if name.ident.as_str() == "nth" { + e.span + } else { + if_chain! { + if let Some((_, Node::Expr(next_expr))) = iter.next(); + if let ExprKind::MethodCall(next_name, _, [_], _) = next_expr.kind; + if next_name.ident.name == sym::next; + if next_expr.span.ctxt() == ctxt; + if let Some(next_id) = cx.typeck_results().type_dependent_def_id(next_expr.hir_id); + if cx.tcx.trait_of_item(next_id) == Some(iter_id); + then { + next_expr.span + } else { + return None; + } + } + }; + match idx { + 0 => (IterUsageKind::Next, span), + 1 => (IterUsageKind::Second, span), + _ => return None, + } + } else { + return None; + } + }, + _ => return None, + } + }, + _ => return None, + }; + + let (unwrap_kind, span) = if let Some((_, Node::Expr(e))) = iter.next() { + match e.kind { + ExprKind::Call( + Expr { + kind: ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, _)), + .. + }, + _, + ) => { + let parent_span = e.span.parent().unwrap(); + if parent_span.ctxt() == ctxt { + (Some(UnwrapKind::QuestionMark), parent_span) + } else { + (None, span) + } + }, + _ if e.span.ctxt() != ctxt => (None, span), + ExprKind::MethodCall(name, _, [_], _) + if name.ident.name == sym::unwrap + && cx + .typeck_results() + .type_dependent_def_id(e.hir_id) + .map_or(false, |id| is_diag_item_method(cx, id, sym::option_type)) => + { + (Some(UnwrapKind::Unwrap), e.span) + }, + _ => (None, span), + } + } else { + (None, span) + }; + + Some(IterUsage { + kind, + unwrap_kind, + span, + }) +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 9626cf79dc129..e89b2d295b923 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -33,6 +33,7 @@ mod iter_nth_zero; mod iter_skip_next; mod iterator_step_by_zero; mod manual_saturating_arithmetic; +mod manual_split_once; mod manual_str_repeat; mod map_collect_result_unit; mod map_flatten; @@ -64,6 +65,7 @@ mod wrong_self_convention; mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; +use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item}; use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, meets_msrv, msrvs, paths, return_ty}; @@ -1771,6 +1773,29 @@ declare_clippy_lint! { "manual implementation of `str::repeat`" } +declare_clippy_lint! { + /// **What it does:** Checks for usages of `str::splitn(2, _)` + /// + /// **Why is this bad?** `split_once` is both clearer in intent and slightly more efficient. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust,ignore + /// // Bad + /// let (key, value) = _.splitn(2, '=').next_tuple()?; + /// let value = _.splitn(2, '=').nth(1)?; + /// + /// // Good + /// let (key, value) = _.split_once('=')?; + /// let value = _.split_once('=')?.1; + /// ``` + pub MANUAL_SPLIT_ONCE, + complexity, + "replace `.splitn(2, pat)` with `.split_once(pat)`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -1848,7 +1873,8 @@ impl_lint_pass!(Methods => [ IMPLICIT_CLONE, SUSPICIOUS_SPLITN, MANUAL_STR_REPEAT, - EXTEND_WITH_DRAIN + EXTEND_WITH_DRAIN, + MANUAL_SPLIT_ONCE ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2176,8 +2202,18 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); } }, - ("splitn" | "splitn_mut" | "rsplitn" | "rsplitn_mut", [count_arg, _]) => { - suspicious_splitn::check(cx, name, expr, recv, count_arg); + ("splitn" | "rsplitn", [count_arg, pat_arg]) => { + if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { + suspicious_splitn::check(cx, name, expr, recv, count); + if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) { + manual_split_once::check(cx, name, expr, recv, pat_arg); + } + } + }, + ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { + if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { + suspicious_splitn::check(cx, name, expr, recv, count); + } }, ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index c1d22e5d72c13..30ed1d665a907 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -96,9 +96,9 @@ pub(super) fn check<'tcx>( (&paths::RESULT, true, &["or", "unwrap_or"], "else"), ]; - if let hir::ExprKind::MethodCall(path, _, args, _) = &arg.kind { + if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &arg.kind { if path.ident.name == sym::len { - let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); + let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); match ty.kind() { ty::Slice(_) | ty::Array(_, _) | ty::Str => return, diff --git a/clippy_lints/src/methods/suspicious_splitn.rs b/clippy_lints/src/methods/suspicious_splitn.rs index a271df60572a2..1c546a15bf62b 100644 --- a/clippy_lints/src/methods/suspicious_splitn.rs +++ b/clippy_lints/src/methods/suspicious_splitn.rs @@ -1,4 +1,3 @@ -use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_note; use if_chain::if_chain; use rustc_ast::LitKind; @@ -8,15 +7,8 @@ use rustc_span::source_map::Spanned; use super::SUSPICIOUS_SPLITN; -pub(super) fn check( - cx: &LateContext<'_>, - method_name: &str, - expr: &Expr<'_>, - self_arg: &Expr<'_>, - count_arg: &Expr<'_>, -) { +pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, count: u128) { if_chain! { - if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg); if count <= 1; if let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(impl_id) = cx.tcx.impl_of_method(call_id); @@ -24,9 +16,9 @@ pub(super) fn check( if lang_items.slice_impl() == Some(impl_id) || lang_items.str_impl() == Some(impl_id); then { // Ignore empty slice and string literals when used with a literal count. - if (matches!(self_arg.kind, ExprKind::Array([])) + if matches!(self_arg.kind, ExprKind::Array([])) || matches!(self_arg.kind, ExprKind::Lit(Spanned { node: LitKind::Str(s, _), .. }) if s.is_empty()) - ) && matches!(count_arg.kind, ExprKind::Lit(_)) + { return; } diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs index 0daea47816a51..30d6665a920ba 100644 --- a/clippy_lints/src/methods/utils.rs +++ b/clippy_lints/src/methods/utils.rs @@ -24,9 +24,9 @@ pub(super) fn derefs_to_slice<'tcx>( } } - if let hir::ExprKind::MethodCall(path, _, args, _) = expr.kind { - if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) { - Some(&args[0]) + if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind { + if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(self_arg)) { + Some(self_arg) } else { None } diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index c796abe9815a4..538fa4e1678fc 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -513,12 +513,12 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } if_chain! { - if let ExprKind::MethodCall(method_name, _, expressions, _) = expr.kind; + if let ExprKind::MethodCall(method_name, _, [ref self_arg, ..], _) = expr.kind; if sym!(signum) == method_name.ident.name; // Check that the receiver of the signum() is a float (expressions[0] is the receiver of // the method call) then { - return is_float(cx, &expressions[0]); + return is_float(cx, self_arg); } } false diff --git a/clippy_lints/src/module_style.rs b/clippy_lints/src/module_style.rs new file mode 100644 index 0000000000000..80a930d0c547b --- /dev/null +++ b/clippy_lints/src/module_style.rs @@ -0,0 +1,178 @@ +use std::{ + ffi::OsString, + path::{Component, Path}, +}; + +use rustc_ast::ast; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{FileName, RealFileName, SourceFile, Span, SyntaxContext}; + +declare_clippy_lint! { + /// ### What it does + /// Checks that module layout uses only self named module files, bans mod.rs files. + /// + /// ### Why is this bad? + /// Having multiple module layout styles in a project can be confusing. + /// + /// ### Example + /// ```text + /// src/ + /// stuff/ + /// stuff_files.rs + /// mod.rs + /// lib.rs + /// ``` + /// Use instead: + /// ```text + /// src/ + /// stuff/ + /// stuff_files.rs + /// stuff.rs + /// lib.rs + /// ``` + pub MOD_MODULE_FILES, + restriction, + "checks that module layout is consistent" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks that module layout uses only mod.rs files. + /// + /// ### Why is this bad? + /// Having multiple module layout styles in a project can be confusing. + /// + /// ### Example + /// ```text + /// src/ + /// stuff/ + /// stuff_files.rs + /// stuff.rs + /// lib.rs + /// ``` + /// Use instead: + /// ```text + /// src/ + /// stuff/ + /// stuff_files.rs + /// mod.rs + /// lib.rs + /// ``` + + pub SELF_NAMED_MODULE_FILES, + restriction, + "checks that module layout is consistent" +} + +pub struct ModStyle; + +impl_lint_pass!(ModStyle => [MOD_MODULE_FILES, SELF_NAMED_MODULE_FILES]); + +impl EarlyLintPass for ModStyle { + fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { + if cx.builder.lint_level(MOD_MODULE_FILES).0 == Level::Allow + && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).0 == Level::Allow + { + return; + } + + let files = cx.sess.source_map().files(); + + let trim_to_src = if let RealFileName::LocalPath(p) = &cx.sess.opts.working_dir { + p.to_string_lossy() + } else { + return; + }; + + // `folder_segments` is all unique folder path segments `path/to/foo.rs` gives + // `[path, to]` but not foo + let mut folder_segments = FxHashSet::default(); + // `mod_folders` is all the unique folder names that contain a mod.rs file + let mut mod_folders = FxHashSet::default(); + // `file_map` maps file names to the full path including the file name + // `{ foo => path/to/foo.rs, .. } + let mut file_map = FxHashMap::default(); + for file in files.iter() { + match &file.name { + FileName::Real(RealFileName::LocalPath(lp)) + if lp.to_string_lossy().starts_with(trim_to_src.as_ref()) => + { + let p = lp.to_string_lossy(); + let path = Path::new(p.trim_start_matches(trim_to_src.as_ref())); + if let Some(stem) = path.file_stem() { + file_map.insert(stem.to_os_string(), (file, path.to_owned())); + } + process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders); + check_self_named_mod_exists(cx, path, file); + } + _ => {}, + } + } + + for folder in &folder_segments { + if !mod_folders.contains(folder) { + if let Some((file, path)) = file_map.get(folder) { + let mut correct = path.clone(); + correct.pop(); + correct.push(folder); + correct.push("mod.rs"); + cx.struct_span_lint( + SELF_NAMED_MODULE_FILES, + Span::new(file.start_pos, file.start_pos, SyntaxContext::root()), + |build| { + let mut lint = + build.build(&format!("`mod.rs` files are required, found `{}`", path.display())); + lint.help(&format!("move `{}` to `{}`", path.display(), correct.display(),)); + lint.emit(); + }, + ); + } + } + } + } +} + +/// For each `path` we add each folder component to `folder_segments` and if the file name +/// is `mod.rs` we add it's parent folder to `mod_folders`. +fn process_paths_for_mod_files( + path: &Path, + folder_segments: &mut FxHashSet, + mod_folders: &mut FxHashSet, +) { + let mut comp = path.components().rev().peekable(); + let _ = comp.next(); + if path.ends_with("mod.rs") { + mod_folders.insert(comp.peek().map(|c| c.as_os_str().to_owned()).unwrap_or_default()); + } + let folders = comp + .filter_map(|c| { + if let Component::Normal(s) = c { + Some(s.to_os_string()) + } else { + None + } + }) + .collect::>(); + folder_segments.extend(folders); +} + +/// Checks every path for the presence of `mod.rs` files and emits the lint if found. +fn check_self_named_mod_exists(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) { + if path.ends_with("mod.rs") { + let mut mod_file = path.to_path_buf(); + mod_file.pop(); + mod_file.set_extension("rs"); + + cx.struct_span_lint( + MOD_MODULE_FILES, + Span::new(file.start_pos, file.start_pos, SyntaxContext::root()), + |build| { + let mut lint = build.build(&format!("`mod.rs` files are not allowed, found `{}`", path.display())); + lint.help(&format!("move `{}` to `{}`", path.display(), mod_file.display(),)); + lint.emit(); + }, + ); + } +} diff --git a/clippy_lints/src/mut_mutex_lock.rs b/clippy_lints/src/mut_mutex_lock.rs index 85e870632a5ca..e9dcc7b227d76 100644 --- a/clippy_lints/src/mut_mutex_lock.rs +++ b/clippy_lints/src/mut_mutex_lock.rs @@ -47,9 +47,9 @@ declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]); impl<'tcx> LateLintPass<'tcx> for MutMutexLock { fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) { if_chain! { - if let ExprKind::MethodCall(path, method_span, args, _) = &ex.kind; + if let ExprKind::MethodCall(path, method_span, [self_arg, ..], _) = &ex.kind; if path.ident.name == sym!(lock); - let ty = cx.typeck_results().expr_ty(&args[0]); + let ty = cx.typeck_results().expr_ty(self_arg); if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind(); if is_type_diagnostic_item(cx, inner_ty, sym!(mutex_type)); then { diff --git a/clippy_lints/src/needless_option_as_deref.rs b/clippy_lints/src/needless_option_as_deref.rs new file mode 100644 index 0000000000000..5024a881d2aa8 --- /dev/null +++ b/clippy_lints/src/needless_option_as_deref.rs @@ -0,0 +1,66 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_macro; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyS; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for no-op uses of Option::{as_deref,as_deref_mut}, + /// for example, `Option<&T>::as_deref()` returns the same type. + /// + /// ### Why is this bad? + /// Redundant code and improving readability. + /// + /// ### Example + /// ```rust + /// let a = Some(&1); + /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32> + /// ``` + /// Could be written as: + /// ```rust + /// let a = Some(&1); + /// let b = a; + /// ``` + pub NEEDLESS_OPTION_AS_DEREF, + complexity, + "no-op use of `deref` or `deref_mut` method to `Option`." +} + +declare_lint_pass!(OptionNeedlessDeref=> [ + NEEDLESS_OPTION_AS_DEREF, +]); + +impl<'tcx> LateLintPass<'tcx> for OptionNeedlessDeref { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if expr.span.from_expansion() || in_macro(expr.span) { + return; + } + let typeck = cx.typeck_results(); + let outer_ty = typeck.expr_ty(expr); + + if_chain! { + if is_type_diagnostic_item(cx,outer_ty,sym::option_type); + if let ExprKind::MethodCall(path, _, [sub_expr], _) = expr.kind; + let symbol = path.ident.as_str(); + if symbol=="as_deref" || symbol=="as_deref_mut"; + if TyS::same_type( outer_ty, typeck.expr_ty(sub_expr) ); + then{ + span_lint_and_sugg( + cx, + NEEDLESS_OPTION_AS_DEREF, + expr.span, + "derefed type is same as origin", + "try this", + snippet_opt(cx,sub_expr.span).unwrap(), + Applicability::MachineApplicable + ); + } + } + } +} diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 28e9e6f438e3d..c5a5cde4b110f 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -96,28 +96,63 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { if has_no_effect(cx, expr) { span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect"); } else if let Some(reduced) = reduce_expression(cx, expr) { - let mut snippet = String::new(); - for e in reduced { + for e in &reduced { if e.span.from_expansion() { return; } - if let Some(snip) = snippet_opt(cx, e.span) { - snippet.push_str(&snip); - snippet.push(';'); - } else { - return; + } + if let ExprKind::Index(..) = &expr.kind { + let snippet; + if_chain! { + if let Some(arr) = snippet_opt(cx, reduced[0].span); + if let Some(func) = snippet_opt(cx, reduced[1].span); + then { + snippet = format!("assert!({}.len() > {});", &arr, &func); + } else { + return; + } + } + span_lint_hir_and_then( + cx, + UNNECESSARY_OPERATION, + expr.hir_id, + stmt.span, + "unnecessary operation", + |diag| { + diag.span_suggestion( + stmt.span, + "statement can be written as", + snippet, + Applicability::MaybeIncorrect, + ); + }, + ); + } else { + let mut snippet = String::new(); + for e in reduced { + if let Some(snip) = snippet_opt(cx, e.span) { + snippet.push_str(&snip); + snippet.push(';'); + } else { + return; + } } + span_lint_hir_and_then( + cx, + UNNECESSARY_OPERATION, + expr.hir_id, + stmt.span, + "unnecessary operation", + |diag| { + diag.span_suggestion( + stmt.span, + "statement can be reduced to", + snippet, + Applicability::MachineApplicable, + ); + }, + ); } - span_lint_hir_and_then( - cx, - UNNECESSARY_OPERATION, - expr.hir_id, - stmt.span, - "statement can be reduced", - |diag| { - diag.span_suggestion(stmt.span, "replace it with", snippet, Applicability::MachineApplicable); - }, - ); } } } diff --git a/clippy_lints/src/open_options.rs b/clippy_lints/src/open_options.rs index 4064d94da2abf..5752342cf623b 100644 --- a/clippy_lints/src/open_options.rs +++ b/clippy_lints/src/open_options.rs @@ -31,11 +31,11 @@ declare_lint_pass!(OpenOptions => [NONSENSICAL_OPEN_OPTIONS]); impl<'tcx> LateLintPass<'tcx> for OpenOptions { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::MethodCall(path, _, arguments, _) = e.kind { - let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs(); + if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &e.kind { + let obj_ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); if path.ident.name == sym!(open) && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) { let mut options = Vec::new(); - get_open_options(cx, &arguments[0], &mut options); + get_open_options(cx, self_arg, &mut options); check_open_options(cx, &options, e.span); } } diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index eff3d3abff80c..15f6dcae8870b 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -2,12 +2,14 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::usage::contains_return_break_continue_macro; -use clippy_utils::{eager_or_lazy, in_macro, is_else_clause, is_lang_ctor}; +use clippy_utils::{ + can_move_expr_to_closure, eager_or_lazy, in_constant, in_macro, is_else_clause, is_lang_ctor, peel_hir_expr_while, + CaptureKind, +}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionSome; -use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Mutability, PatKind, UnOp}; +use rustc_hir::{def::Res, BindingAnnotation, Block, Expr, ExprKind, Mutability, PatKind, Path, QPath, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -58,7 +60,7 @@ declare_clippy_lint! { /// }, |foo| foo); /// ``` pub OPTION_IF_LET_ELSE, - pedantic, + nursery, "reimplementation of Option::map_or" } @@ -125,20 +127,30 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { if_chain! { if !in_macro(expr.span); // Don't lint macros, because it behaves weirdly - if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(cx, expr); + if !in_constant(cx, expr.hir_id); + if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) + = higher::IfLet::hir(cx, expr); if !is_else_clause(cx.tcx, expr); if !is_result_ok(cx, let_expr); // Don't lint on Result::ok because a different lint does it already if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &let_pat.kind; if is_lang_ctor(cx, struct_qpath, OptionSome); if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind; - if !contains_return_break_continue_macro(if_then); - if !contains_return_break_continue_macro(if_else); + if let Some(some_captures) = can_move_expr_to_closure(cx, if_then); + if let Some(none_captures) = can_move_expr_to_closure(cx, if_else); + if some_captures + .iter() + .filter_map(|(id, &c)| none_captures.get(id).map(|&c2| (c, c2))) + .all(|(x, y)| x.is_imm_ref() && y.is_imm_ref()); then { let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" }; let some_body = extract_body_from_expr(if_then)?; let none_body = extract_body_from_expr(if_else)?; - let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) { "map_or" } else { "map_or_else" }; + let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) { + "map_or" + } else { + "map_or_else" + }; let capture_name = id.name.to_ident_string(); let (as_ref, as_mut) = match &let_expr.kind { ExprKind::AddrOf(_, Mutability::Not, _) => (true, false), @@ -150,6 +162,24 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> ExprKind::Unary(UnOp::Deref, expr) | ExprKind::AddrOf(_, _, expr) => expr, _ => let_expr, }; + // Check if captures the closure will need conflict with borrows made in the scrutinee. + // TODO: check all the references made in the scrutinee expression. This will require interacting + // with the borrow checker. Currently only `[.]*` is checked for. + if as_ref || as_mut { + let e = peel_hir_expr_while(cond_expr, |e| match e.kind { + ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e), + _ => None, + }); + if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(local_id), .. })) = e.kind { + match some_captures.get(local_id) + .or_else(|| (method_sugg == "map_or_else").then(|| ()).and_then(|_| none_captures.get(local_id))) + { + Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None, + Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None, + Some(CaptureKind::Ref(Mutability::Not)) | None => (), + } + } + } Some(OptionIfLetElseOccurence { option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut), method_sugg: method_sugg.to_string(), diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index 35cff4141a903..e7bc24465908b 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -118,7 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch { } } if let ExprKind::Let(let_pat, let_expr, _) = expr.kind { - if let Some(ref expr_ty) = cx.typeck_results().node_type_opt(let_expr.hir_id) { + if let Some(expr_ty) = cx.typeck_results().node_type_opt(let_expr.hir_id) { if in_external_macro(cx.sess(), let_pat.span) { return; } diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index f1975056ddc9b..cfb5287c6673f 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -92,13 +92,13 @@ fn expr_as_ptr_offset_call<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> { - if let ExprKind::MethodCall(path_segment, _, args, _) = expr.kind { - if is_expr_ty_raw_ptr(cx, &args[0]) { + if let ExprKind::MethodCall(path_segment, _, [arg_0, arg_1, ..], _) = &expr.kind { + if is_expr_ty_raw_ptr(cx, arg_0) { if path_segment.ident.name == sym::offset { - return Some((&args[0], &args[1], Method::Offset)); + return Some((arg_0, arg_1, Method::Offset)); } if path_segment.ident.name == sym!(wrapping_offset) { - return Some((&args[0], &args[1], Method::WrappingOffset)); + return Some((arg_0, arg_1, Method::WrappingOffset)); } } } diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index 7b6a0894e6d2f..e79cd7ed4ec4a 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -97,7 +97,8 @@ impl QuestionMark { fn check_if_let_some_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { - if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(cx, expr); + if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) + = higher::IfLet::hir(cx, expr); if Self::is_option(cx, let_expr); if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind; @@ -105,7 +106,7 @@ impl QuestionMark { if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind; let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut); - if let ExprKind::Block(ref block, None) = if_then.kind; + if let ExprKind::Block(block, None) = if_then.kind; if block.stmts.is_empty(); if let Some(trailing_expr) = &block.expr; if path_to_local_id(trailing_expr, bind_id); diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index f5e43264a5c69..cfa12ef3a321d 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -625,7 +625,10 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { .flat_map(HybridBitSet::iter) .collect(); - if ContainsRegion(self.cx.tcx).visit_ty(self.body.local_decls[*dest].ty).is_break() { + if ContainsRegion(self.cx.tcx) + .visit_ty(self.body.local_decls[*dest].ty) + .is_break() + { mutable_variables.push(*dest); } diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 7314bce83e038..90e3c3f4b3e98 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -51,9 +51,7 @@ impl ReturnVisitor { impl<'ast> ast_visit::Visitor<'ast> for ReturnVisitor { fn visit_expr(&mut self, ex: &'ast ast::Expr) { - if let ast::ExprKind::Ret(_) = ex.kind { - self.found_return = true; - } else if let ast::ExprKind::Try(_) = ex.kind { + if let ast::ExprKind::Ret(_) | ast::ExprKind::Try(_) = ex.kind { self.found_return = true; } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 681baed8c3696..341b5a61631df 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -206,13 +206,10 @@ fn check_final_expr<'tcx>( // an if/if let expr, check both exprs // note, if without else is going to be a type checking error anyways // (except for unit type functions) so we don't match it - ExprKind::Match(_, arms, source) => match source { - MatchSource::Normal => { - for arm in arms.iter() { - check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Block); - } - }, - _ => (), + ExprKind::Match(_, arms, MatchSource::Normal) => { + for arm in arms.iter() { + check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Block); + } }, ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty), _ => (), diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 1a78a4968e5a3..13d8f954c4413 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -345,9 +345,9 @@ declare_lint_pass!(StrToString => [STR_TO_STRING]); impl LateLintPass<'_> for StrToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(path, _, args, _) = &expr.kind; + if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind; if path.ident.name == sym!(to_string); - let ty = cx.typeck_results().expr_ty(&args[0]); + let ty = cx.typeck_results().expr_ty(self_arg); if let ty::Ref(_, ty, ..) = ty.kind(); if *ty.kind() == ty::Str; then { @@ -394,9 +394,9 @@ declare_lint_pass!(StringToString => [STRING_TO_STRING]); impl LateLintPass<'_> for StringToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { if_chain! { - if let ExprKind::MethodCall(path, _, args, _) = &expr.kind; + if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind; if path.ident.name == sym!(to_string); - let ty = cx.typeck_results().expr_ty(&args[0]); + let ty = cx.typeck_results().expr_ty(self_arg); if is_type_diagnostic_item(cx, ty, sym::string_type); then { span_lint_and_help( diff --git a/clippy_lints/src/to_string_in_display.rs b/clippy_lints/src/to_string_in_display.rs index b036ed9a3d2e7..b7414cec87cd2 100644 --- a/clippy_lints/src/to_string_in_display.rs +++ b/clippy_lints/src/to_string_in_display.rs @@ -92,11 +92,11 @@ impl LateLintPass<'_> for ToStringInDisplay { if_chain! { if self.in_display_impl; if let Some(self_hir_id) = self.self_hir_id; - if let ExprKind::MethodCall(path, _, args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, [ref self_arg, ..], _) = expr.kind; if path.ident.name == sym!(to_string); if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if is_diag_trait_item(cx, expr_def_id, sym::ToString); - if path_to_local_id(&args[0], self_hir_id); + if path_to_local_id(self_arg, self_hir_id); then { span_lint( cx, diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 371bb8b445a71..9588de8459cfe 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -295,6 +295,7 @@ declare_clippy_lint! { pub struct Types { vec_box_size_threshold: u64, type_complexity_threshold: u64, + avoid_breaking_exported_api: bool, } impl_lint_pass!(Types => [BOX_VEC, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]); @@ -308,19 +309,31 @@ impl<'tcx> LateLintPass<'tcx> for Types { false }; + let is_exported = cx.access_levels.is_exported(cx.tcx.hir().local_def_id(id)); + self.check_fn_decl( cx, decl, CheckTyContext { is_in_trait_impl, + is_exported, ..CheckTyContext::default() }, ); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + let is_exported = cx.access_levels.is_exported(item.def_id); + match item.kind { - ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty(cx, ty, CheckTyContext::default()), + ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty( + cx, + ty, + CheckTyContext { + is_exported, + ..CheckTyContext::default() + }, + ), // functions, enums, structs, impls and traits are covered _ => (), } @@ -342,15 +355,31 @@ impl<'tcx> LateLintPass<'tcx> for Types { } fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { - self.check_ty(cx, field.ty, CheckTyContext::default()); + let is_exported = cx.access_levels.is_exported(cx.tcx.hir().local_def_id(field.hir_id)); + + self.check_ty( + cx, + field.ty, + CheckTyContext { + is_exported, + ..CheckTyContext::default() + }, + ); } - fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &TraitItem<'_>) { + let is_exported = cx.access_levels.is_exported(item.def_id); + + let context = CheckTyContext { + is_exported, + ..CheckTyContext::default() + }; + match item.kind { TraitItemKind::Const(ty, _) | TraitItemKind::Type(_, Some(ty)) => { - self.check_ty(cx, ty, CheckTyContext::default()); + self.check_ty(cx, ty, context); }, - TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, CheckTyContext::default()), + TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, context), TraitItemKind::Type(..) => (), } } @@ -370,10 +399,11 @@ impl<'tcx> LateLintPass<'tcx> for Types { } impl Types { - pub fn new(vec_box_size_threshold: u64, type_complexity_threshold: u64) -> Self { + pub fn new(vec_box_size_threshold: u64, type_complexity_threshold: u64, avoid_breaking_exported_api: bool) -> Self { Self { vec_box_size_threshold, type_complexity_threshold, + avoid_breaking_exported_api, } } @@ -410,17 +440,24 @@ impl Types { let hir_id = hir_ty.hir_id; let res = cx.qpath_res(qpath, hir_id); if let Some(def_id) = res.opt_def_id() { - let mut triggered = false; - triggered |= box_vec::check(cx, hir_ty, qpath, def_id); - triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id); - triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id); - triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold); - triggered |= option_option::check(cx, hir_ty, qpath, def_id); - triggered |= linked_list::check(cx, hir_ty, def_id); - triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id); - - if triggered { - return; + if self.is_type_change_allowed(context) { + // All lints that are being checked in this block are guarded by + // the `avoid_breaking_exported_api` configuration. When adding a + // new lint, please also add the name to the configuration documentation + // in `clippy_lints::utils::conf.rs` + + let mut triggered = false; + triggered |= box_vec::check(cx, hir_ty, qpath, def_id); + triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id); + triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id); + triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold); + triggered |= option_option::check(cx, hir_ty, qpath, def_id); + triggered |= linked_list::check(cx, hir_ty, def_id); + triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id); + + if triggered { + return; + } } } match *qpath { @@ -487,11 +524,21 @@ impl Types { _ => {}, } } + + /// This function checks if the type is allowed to change in the current context + /// based on the `avoid_breaking_exported_api` configuration + fn is_type_change_allowed(&self, context: CheckTyContext) -> bool { + !(context.is_exported && self.avoid_breaking_exported_api) + } } +#[allow(clippy::struct_excessive_bools)] #[derive(Clone, Copy, Default)] struct CheckTyContext { is_in_trait_impl: bool, + /// `true` for types on local variables. is_local: bool, + /// `true` for types that are part of the public API. + is_exported: bool, is_nested_call: bool, } diff --git a/clippy_lints/src/types/rc_mutex.rs b/clippy_lints/src/types/rc_mutex.rs index bd7a0ee6408fa..12db7afb81ca1 100644 --- a/clippy_lints/src/types/rc_mutex.rs +++ b/clippy_lints/src/types/rc_mutex.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_ty_param_diagnostic_item; use if_chain::if_chain; use rustc_hir::{self as hir, def_id::DefId, QPath}; @@ -11,13 +11,14 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ if_chain! { if cx.tcx.is_diagnostic_item(sym::Rc, def_id) ; if let Some(_) = is_ty_param_diagnostic_item(cx, qpath, sym!(mutex_type)) ; - - then{ - span_lint( + then { + span_lint_and_help( cx, RC_MUTEX, hir_ty.span, - "found `Rc>`. Consider using `Rc>` or `Arc>` instead", + "usage of `Rc>`", + None, + "consider using `Rc>` or `Arc>` instead", ); return true; } diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index 8e83dcbf704e0..ac7bdd6a1ebeb 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -54,7 +54,13 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ _ => return false, }; let inner_span = match get_qpath_generic_tys(inner_qpath).next() { - Some(ty) => ty.span, + Some(ty) => { + // Box> is smaller than Box because of wide pointers + if matches!(ty.kind, TyKind::TraitObject(..)) { + return false; + } + ty.span + }, None => return false, }; if inner_sym == outer_sym { diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs index 47571e608c780..0957061659362 100644 --- a/clippy_lints/src/undropped_manually_drops.rs +++ b/clippy_lints/src/undropped_manually_drops.rs @@ -37,8 +37,8 @@ declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]); impl LateLintPass<'tcx> for UndroppedManuallyDrops { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some(args) = match_function_call(cx, expr, &paths::DROP) { - let ty = cx.typeck_results().expr_ty(&args[0]); + if let Some([arg_0, ..]) = match_function_call(cx, expr, &paths::DROP) { + let ty = cx.typeck_results().expr_ty(arg_0); if is_type_lang_item(cx, ty, lang_items::LangItem::ManuallyDrop) { span_lint_and_help( cx, diff --git a/clippy_lints/src/unnecessary_sort_by.rs b/clippy_lints/src/unnecessary_sort_by.rs index 97b1b2dae3c1a..61670fe124e36 100644 --- a/clippy_lints/src/unnecessary_sort_by.rs +++ b/clippy_lints/src/unnecessary_sort_by.rs @@ -218,7 +218,10 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); - matches!(ty.kind(), ty::Ref(..)) || ty.walk(cx.tcx).any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))) + matches!(ty.kind(), ty::Ref(..)) + || ty + .walk(cx.tcx) + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_))) } impl LateLintPass<'_> for UnnecessarySortBy { diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index 3a6a07c522630..f4808682b6927 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor}; -use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnHeader, HirId, IsAsync, Item, ItemKind, YieldSource}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnHeader, HirId, IsAsync, YieldSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -57,11 +57,6 @@ impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { } impl<'tcx> LateLintPass<'tcx> for UnusedAsync { - fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if let ItemKind::Trait(..) = item.kind { - return; - } - } fn check_fn( &mut self, cx: &LateContext<'tcx>, diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 82bc4a6d15343..031b182bd2fa0 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -45,20 +45,20 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { match expr.kind { hir::ExprKind::Match(res, _, _) if is_try(cx, expr).is_some() => { - if let hir::ExprKind::Call(func, args) = res.kind { + if let hir::ExprKind::Call(func, [ref arg_0, ..]) = res.kind { if matches!( func.kind, hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, _)) ) { - check_map_error(cx, &args[0], expr); + check_map_error(cx, arg_0, expr); } } else { check_map_error(cx, res, expr); } }, - hir::ExprKind::MethodCall(path, _, args, _) => match &*path.ident.as_str() { + hir::ExprKind::MethodCall(path, _, [ref arg_0, ..], _) => match &*path.ident.as_str() { "expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => { - check_map_error(cx, &args[0], expr); + check_map_error(cx, arg_0, expr); }, _ => (), }, diff --git a/clippy_lints/src/unused_self.rs b/clippy_lints/src/unused_self.rs index 658ac81f6eac8..e7e249c79a2fa 100644 --- a/clippy_lints/src/unused_self.rs +++ b/clippy_lints/src/unused_self.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::visitors::LocalUsedVisitor; +use clippy_utils::visitors::is_local_used; use if_chain::if_chain; use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -50,8 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf { if let ImplItemKind::Fn(.., body_id) = &impl_item.kind; let body = cx.tcx.hir().body(*body_id); if let [self_param, ..] = body.params; - let self_hir_id = self_param.pat.hir_id; - if !LocalUsedVisitor::new(cx, self_hir_id).check_body(body); + if !is_local_used(cx, body, self_param.pat.hir_id); then { span_lint_and_help( cx, diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index bffd9f3612b0a..b2ab300c2e937 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{differing_macro_contexts, usage::is_potentially_mutated}; +use clippy_utils::{differing_macro_contexts, path_to_local, usage::is_potentially_mutated}; use if_chain::if_chain; +use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor}; -use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Path, QPath, UnOp}; +use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, PathSegment, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; @@ -74,26 +75,61 @@ struct UnwrappableVariablesVisitor<'a, 'tcx> { unwrappables: Vec>, cx: &'a LateContext<'tcx>, } + +/// What kind of unwrappable this is. +#[derive(Copy, Clone, Debug)] +enum UnwrappableKind { + Option, + Result, +} + +impl UnwrappableKind { + fn success_variant_pattern(self) -> &'static str { + match self { + UnwrappableKind::Option => "Some(..)", + UnwrappableKind::Result => "Ok(..)", + } + } + + fn error_variant_pattern(self) -> &'static str { + match self { + UnwrappableKind::Option => "None", + UnwrappableKind::Result => "Err(..)", + } + } +} + /// Contains information about whether a variable can be unwrapped. #[derive(Copy, Clone, Debug)] struct UnwrapInfo<'tcx> { /// The variable that is checked - ident: &'tcx Path<'tcx>, + local_id: HirId, + /// The if itself + if_expr: &'tcx Expr<'tcx>, /// The check, like `x.is_ok()` check: &'tcx Expr<'tcx>, + /// The check's name, like `is_ok` + check_name: &'tcx PathSegment<'tcx>, /// The branch where the check takes place, like `if x.is_ok() { .. }` branch: &'tcx Expr<'tcx>, /// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`). safe_to_unwrap: bool, + /// What kind of unwrappable this is. + kind: UnwrappableKind, + /// If the check is the entire condition (`if x.is_ok()`) or only a part of it (`foo() && + /// x.is_ok()`) + is_entire_condition: bool, } /// Collects the information about unwrappable variables from an if condition /// The `invert` argument tells us whether the condition is negated. fn collect_unwrap_info<'tcx>( cx: &LateContext<'tcx>, + if_expr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>, branch: &'tcx Expr<'_>, invert: bool, + is_entire_condition: bool, ) -> Vec> { fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool { is_type_diagnostic_item(cx, ty, sym::option_type) && ["is_some", "is_none"].contains(&method_name) @@ -106,18 +142,18 @@ fn collect_unwrap_info<'tcx>( if let ExprKind::Binary(op, left, right) = &expr.kind { match (invert, op.node) { (false, BinOpKind::And | BinOpKind::BitAnd) | (true, BinOpKind::Or | BinOpKind::BitOr) => { - let mut unwrap_info = collect_unwrap_info(cx, left, branch, invert); - unwrap_info.append(&mut collect_unwrap_info(cx, right, branch, invert)); + let mut unwrap_info = collect_unwrap_info(cx, if_expr, left, branch, invert, false); + unwrap_info.append(&mut collect_unwrap_info(cx, if_expr, right, branch, invert, false)); return unwrap_info; }, _ => (), } } else if let ExprKind::Unary(UnOp::Not, expr) = &expr.kind { - return collect_unwrap_info(cx, expr, branch, !invert); + return collect_unwrap_info(cx, if_expr, expr, branch, !invert, false); } else { if_chain! { if let ExprKind::MethodCall(method_name, _, args, _) = &expr.kind; - if let ExprKind::Path(QPath::Resolved(None, path)) = &args[0].kind; + if let Some(local_id) = path_to_local(&args[0]); let ty = cx.typeck_results().expr_ty(&args[0]); let name = method_name.ident.as_str(); if is_relevant_option_call(cx, ty, &name) || is_relevant_result_call(cx, ty, &name); @@ -129,7 +165,24 @@ fn collect_unwrap_info<'tcx>( _ => unreachable!(), }; let safe_to_unwrap = unwrappable != invert; - return vec![UnwrapInfo { ident: path, check: expr, branch, safe_to_unwrap }]; + let kind = if is_type_diagnostic_item(cx, ty, sym::option_type) { + UnwrappableKind::Option + } else { + UnwrappableKind::Result + }; + + return vec![ + UnwrapInfo { + local_id, + if_expr, + check: expr, + check_name: method_name, + branch, + safe_to_unwrap, + kind, + is_entire_condition, + } + ] } } } @@ -137,11 +190,17 @@ fn collect_unwrap_info<'tcx>( } impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> { - fn visit_branch(&mut self, cond: &'tcx Expr<'_>, branch: &'tcx Expr<'_>, else_branch: bool) { + fn visit_branch( + &mut self, + if_expr: &'tcx Expr<'_>, + cond: &'tcx Expr<'_>, + branch: &'tcx Expr<'_>, + else_branch: bool, + ) { let prev_len = self.unwrappables.len(); - for unwrap_info in collect_unwrap_info(self.cx, cond, branch, else_branch) { - if is_potentially_mutated(unwrap_info.ident, cond, self.cx) - || is_potentially_mutated(unwrap_info.ident, branch, self.cx) + for unwrap_info in collect_unwrap_info(self.cx, if_expr, cond, branch, else_branch, true) { + if is_potentially_mutated(unwrap_info.local_id, cond, self.cx) + || is_potentially_mutated(unwrap_info.local_id, branch, self.cx) { // if the variable is mutated, we don't know whether it can be unwrapped: continue; @@ -163,32 +222,62 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> { } if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) { walk_expr(self, cond); - self.visit_branch(cond, then, false); + self.visit_branch(expr, cond, then, false); if let Some(else_inner) = r#else { - self.visit_branch(cond, else_inner, true); + self.visit_branch(expr, cond, else_inner, true); } } else { // find `unwrap[_err]()` calls: if_chain! { if let ExprKind::MethodCall(method_name, _, args, _) = expr.kind; - if let ExprKind::Path(QPath::Resolved(None, path)) = args[0].kind; - if [sym::unwrap, sym!(unwrap_err)].contains(&method_name.ident.name); - let call_to_unwrap = method_name.ident.name == sym::unwrap; + if let Some(id) = path_to_local(&args[0]); + if [sym::unwrap, sym::expect, sym!(unwrap_err)].contains(&method_name.ident.name); + let call_to_unwrap = [sym::unwrap, sym::expect].contains(&method_name.ident.name); if let Some(unwrappable) = self.unwrappables.iter() - .find(|u| u.ident.res == path.res); + .find(|u| u.local_id == id); // Span contexts should not differ with the conditional branch if !differing_macro_contexts(unwrappable.branch.span, expr.span); if !differing_macro_contexts(unwrappable.branch.span, unwrappable.check.span); then { if call_to_unwrap == unwrappable.safe_to_unwrap { + let is_entire_condition = unwrappable.is_entire_condition; + let unwrappable_variable_name = self.cx.tcx.hir().name(unwrappable.local_id); + let suggested_pattern = if call_to_unwrap { + unwrappable.kind.success_variant_pattern() + } else { + unwrappable.kind.error_variant_pattern() + }; + span_lint_and_then( self.cx, UNNECESSARY_UNWRAP, expr.span, - &format!("you checked before that `{}()` cannot fail, \ - instead of checking and unwrapping, it's better to use `if let` or `match`", - method_name.ident.name), - |diag| { diag.span_label(unwrappable.check.span, "the check is happening here"); }, + &format!( + "called `{}` on `{}` after checking its variant with `{}`", + method_name.ident.name, + unwrappable_variable_name, + unwrappable.check_name.ident.as_str(), + ), + |diag| { + if is_entire_condition { + diag.span_suggestion( + unwrappable.check.span.with_lo(unwrappable.if_expr.span.lo()), + "try", + format!( + "if let {} = {}", + suggested_pattern, + unwrappable_variable_name, + ), + // We don't track how the unwrapped value is used inside the + // block or suggest deleting the unwrap, so we can't offer a + // fixable solution. + Applicability::Unspecified, + ); + } else { + diag.span_label(unwrappable.check.span, "the check is happening here"); + diag.help("try using `if let` or `match`"); + } + }, ); } else { span_lint_and_then( diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index c192f9094a8a9..8651fa6fcf9ea 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -31,9 +31,6 @@ impl TryConf { } } -/// Note that the configuration parsing currently doesn't support documentation that will -/// that spans over several lines. This will be possible with the new implementation -/// See (rust-clippy#7172) macro_rules! define_Conf { ($( $(#[doc = $doc:literal])+ @@ -130,13 +127,12 @@ macro_rules! define_Conf { }; } -// N.B., this macro is parsed by util/lintlib.py define_Conf! { - /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION. + /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_VEC, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX. /// /// Suppress lints whenever the suggested change would cause breakage for other crates. (avoid_breaking_exported_api: bool = true), - /// Lint: MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE. + /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT. /// /// The minimum rust version that the project supports (msrv: Option = None), diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs index e97983a2e1451..43590cc786236 100644 --- a/clippy_lints/src/utils/inspector.rs +++ b/clippy_lints/src/utils/inspector.rs @@ -142,7 +142,7 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { print_expr(cx, arg, indent + 1); } }, - hir::ExprKind::Let(ref pat, ref expr, _) => { + hir::ExprKind::Let(pat, expr, _) => { print_pat(cx, pat, indent + 1); print_expr(cx, expr, indent + 1); }, diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index d660008e7d184..756c33d70c26d 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,5 +1,6 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::higher; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; use clippy_utils::{ @@ -17,8 +18,8 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::CRATE_HIR_ID; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::{ - BinOpKind, Block, Crate, Expr, ExprKind, HirId, Item, Local, MatchSource, MutTy, Mutability, Node, Path, Stmt, - StmtKind, Ty, TyKind, UnOp, + BinOpKind, Block, Crate, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty, + TyKind, UnOp, }; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; @@ -503,10 +504,10 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions { } if_chain! { - if let ExprKind::MethodCall(path, _, args, _) = expr.kind; + if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind; let fn_name = path.ident; if let Some(sugg) = self.map.get(&*fn_name.as_str()); - let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs(); + let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); if match_type(cx, ty, &paths::EARLY_CONTEXT) || match_type(cx, ty, &paths::LATE_CONTEXT); then { @@ -1106,16 +1107,10 @@ impl<'tcx> LateLintPass<'tcx> for IfChainStyle { } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - let (cond, then, els) = match expr.kind { - ExprKind::If(cond, then, els) => (Some(cond), then, els.is_some()), - ExprKind::Match( - _, - [arm, ..], - MatchSource::IfLetDesugar { - contains_else_clause: els, - }, - ) => (None, arm.body, els), - _ => return, + let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) { + (cond, then, r#else.is_some()) + } else { + return; }; let then_block = match then.kind { ExprKind::Block(block, _) => block, @@ -1131,7 +1126,6 @@ impl<'tcx> LateLintPass<'tcx> for IfChainStyle { }; // check for `if a && b;` if_chain! { - if let Some(cond) = cond; if let ExprKind::Binary(op, _, _) = cond.kind; if op.node == BinOpKind::And; if cx.sess().source_map().is_multiline(cond.span); @@ -1166,9 +1160,7 @@ fn check_nested_if_chains( _ => return, }; if_chain! { - if matches!(tail.kind, - ExprKind::If(_, _, None) - | ExprKind::Match(.., MatchSource::IfLetDesugar { contains_else_clause: false })); + if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail); let sm = cx.sess().source_map(); if head .iter() diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index a48a53850830f..188d0419c3993 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -82,7 +82,7 @@ This lint has the following configuration variables: /// `default` macro_rules! CONFIGURATION_VALUE_TEMPLATE { () => { - "* {name}: `{ty}`: {doc} (defaults to `{default}`)\n" + "* `{name}`: `{ty}`: {doc} (defaults to `{default}`)\n" }; } @@ -786,8 +786,6 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> { } }; - // TODO xFrednet 2021-03-01: support function arguments? - intravisit::walk_expr(self, expr); } } diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index e76d5f81c9640..d124d948b5e69 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -49,7 +49,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { if_chain! { if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind(); if let ty::Slice(..) = ty.kind(); - if let ExprKind::AddrOf(BorrowKind::Ref, mutability, ref addressee) = expr.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind; if let Some(vec_args) = higher::VecArgs::hir(cx, addressee); then { self.check_vec_macro(cx, &vec_args, mutability, expr.span); diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index c65b2958ec56d..4c038a997952a 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -1,15 +1,11 @@ [package] name = "clippy_utils" -version = "0.1.56" +version = "0.1.57" edition = "2018" publish = false [dependencies] if_chain = "1.0.0" -itertools = "0.9" -regex-syntax = "0.6" -serde = { version = "1.0", features = ["derive"] } -unicode-normalization = "0.1" rustc-semver="1.1.0" [features] diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 71cfa196fc335..9302e5c21faa4 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -21,7 +21,7 @@ fn docs_link(diag: &mut DiagnosticBuilder<'_>, lint: &'static Lint) { "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}", &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| { // extract just major + minor version and ignore patch versions - format!("rust-{}", n.rsplitn(2, '.').nth(1).unwrap()) + format!("rust-{}", n.rsplit_once('.').unwrap().1) }), lint )); diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index c06b894a73871..05a4a01431950 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -6,33 +6,38 @@ use crate::{is_expn_of, match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::{self, LitKind}; use rustc_hir as hir; -use rustc_hir::{Block, BorrowKind, Expr, ExprKind, LoopSource, Node, Pat, StmtKind, UnOp}; +use rustc_hir::{Arm, Block, BorrowKind, Expr, ExprKind, LoopSource, MatchSource, Node, Pat, StmtKind, UnOp}; use rustc_lint::LateContext; use rustc_span::{sym, ExpnKind, Span, Symbol}; /// The essential nodes of a desugared for loop as well as the entire span: /// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`. pub struct ForLoop<'tcx> { + /// `for` loop item pub pat: &'tcx hir::Pat<'tcx>, + /// `IntoIterator` argument pub arg: &'tcx hir::Expr<'tcx>, + /// `for` loop body pub body: &'tcx hir::Expr<'tcx>, + /// entire `for` loop span pub span: Span, } impl<'tcx> ForLoop<'tcx> { #[inline] + /// Parses a desugared `for` loop pub fn hir(expr: &Expr<'tcx>) -> Option { if_chain! { - if let hir::ExprKind::Match(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.kind; + if let hir::ExprKind::Match(iterexpr, arms, hir::MatchSource::ForLoopDesugar) = expr.kind; if let Some(first_arm) = arms.get(0); - if let hir::ExprKind::Call(_, ref iterargs) = iterexpr.kind; + if let hir::ExprKind::Call(_, iterargs) = iterexpr.kind; if let Some(first_arg) = iterargs.get(0); if iterargs.len() == 1 && arms.len() == 1 && first_arm.guard.is_none(); - if let hir::ExprKind::Loop(ref block, ..) = first_arm.body.kind; + if let hir::ExprKind::Loop(block, ..) = first_arm.body.kind; if block.expr.is_none(); if let [ _, _, ref let_stmt, ref body ] = *block.stmts; - if let hir::StmtKind::Local(ref local) = let_stmt.kind; - if let hir::StmtKind::Expr(ref body_expr) = body.kind; + if let hir::StmtKind::Local(local) = let_stmt.kind; + if let hir::StmtKind::Expr(body_expr) = body.kind; then { return Some(Self { pat: &*local.pat, @@ -46,14 +51,19 @@ impl<'tcx> ForLoop<'tcx> { } } +/// An `if` expression without `DropTemps` pub struct If<'hir> { + /// `if` condition pub cond: &'hir Expr<'hir>, - pub r#else: Option<&'hir Expr<'hir>>, + /// `if` then expression pub then: &'hir Expr<'hir>, + /// `else` expression + pub r#else: Option<&'hir Expr<'hir>>, } impl<'hir> If<'hir> { #[inline] + /// Parses an `if` expression pub const fn hir(expr: &Expr<'hir>) -> Option { if let ExprKind::If( Expr { @@ -64,21 +74,27 @@ impl<'hir> If<'hir> { r#else, ) = expr.kind { - Some(Self { cond, r#else, then }) + Some(Self { cond, then, r#else }) } else { None } } } +/// An `if let` expression pub struct IfLet<'hir> { + /// `if let` pattern pub let_pat: &'hir Pat<'hir>, + /// `if let` scrutinee pub let_expr: &'hir Expr<'hir>, + /// `if let` then expression pub if_then: &'hir Expr<'hir>, + /// `if let` else expression pub if_else: Option<&'hir Expr<'hir>>, } impl<'hir> IfLet<'hir> { + /// Parses an `if let` expression pub fn hir(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option { if let ExprKind::If( Expr { @@ -92,7 +108,14 @@ impl<'hir> IfLet<'hir> { let hir = cx.tcx.hir(); let mut iter = hir.parent_iter(expr.hir_id); if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next() { - if let Some((_, Node::Expr(Expr { kind: ExprKind::Loop(_, _, LoopSource::While, _), .. }))) = iter.next() { + if let Some(( + _, + Node::Expr(Expr { + kind: ExprKind::Loop(_, _, LoopSource::While, _), + .. + }), + )) = iter.next() + { // while loop desugar return None; } @@ -108,14 +131,49 @@ impl<'hir> IfLet<'hir> { } } +/// An `if let` or `match` expression. Useful for lints that trigger on one or the other. +pub enum IfLetOrMatch<'hir> { + /// Any `match` expression + Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource), + /// scrutinee, pattern, then block, else block + IfLet( + &'hir Expr<'hir>, + &'hir Pat<'hir>, + &'hir Expr<'hir>, + Option<&'hir Expr<'hir>>, + ), +} + +impl<'hir> IfLetOrMatch<'hir> { + /// Parses an `if let` or `match` expression + pub fn parse(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option { + match expr.kind { + ExprKind::Match(expr, arms, source) => Some(Self::Match(expr, arms, source)), + _ => IfLet::hir(cx, expr).map( + |IfLet { + let_expr, + let_pat, + if_then, + if_else, + }| { Self::IfLet(let_expr, let_pat, if_then, if_else) }, + ), + } + } +} + +/// An `if` or `if let` expression pub struct IfOrIfLet<'hir> { + /// `if` condition that is maybe a `let` expression pub cond: &'hir Expr<'hir>, - pub r#else: Option<&'hir Expr<'hir>>, + /// `if` then expression pub then: &'hir Expr<'hir>, + /// `else` expression + pub r#else: Option<&'hir Expr<'hir>>, } impl<'hir> IfOrIfLet<'hir> { #[inline] + /// Parses an `if` or `if let` expression pub const fn hir(expr: &Expr<'hir>) -> Option { if let ExprKind::If(cond, then, r#else) = expr.kind { if let ExprKind::DropTemps(new_cond) = cond.kind { @@ -126,7 +184,7 @@ impl<'hir> IfOrIfLet<'hir> { }); } if let ExprKind::Let(..) = cond.kind { - return Some(Self { cond, r#else, then }); + return Some(Self { cond, then, r#else }); } } None @@ -155,7 +213,7 @@ impl<'a> Range<'a> { } match expr.kind { - hir::ExprKind::Call(ref path, ref args) + hir::ExprKind::Call(path, args) if matches!( path.kind, hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _)) @@ -167,7 +225,7 @@ impl<'a> Range<'a> { limits: ast::RangeLimits::Closed, }) }, - hir::ExprKind::Struct(ref path, ref fields, None) => match path { + hir::ExprKind::Struct(path, fields, None) => match &path { hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range { start: None, end: None, @@ -213,7 +271,7 @@ impl<'a> VecArgs<'a> { /// from `vec!`. pub fn hir(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option> { if_chain! { - if let hir::ExprKind::Call(ref fun, ref args) = expr.kind; + if let hir::ExprKind::Call(fun, args) = expr.kind; if let hir::ExprKind::Path(ref qpath) = fun.kind; if is_expn_of(fun.span, "vec").is_some(); if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); @@ -225,10 +283,10 @@ impl<'a> VecArgs<'a> { else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 { // `vec![a, b, c]` case if_chain! { - if let hir::ExprKind::Box(ref boxed) = args[0].kind; - if let hir::ExprKind::Array(ref args) = boxed.kind; + if let hir::ExprKind::Box(boxed) = args[0].kind; + if let hir::ExprKind::Array(args) = boxed.kind; then { - return Some(VecArgs::Vec(&*args)); + return Some(VecArgs::Vec(args)); } } @@ -247,14 +305,17 @@ impl<'a> VecArgs<'a> { } } +/// A desugared `while` loop pub struct While<'hir> { - pub if_cond: &'hir Expr<'hir>, - pub if_then: &'hir Expr<'hir>, - pub if_else: Option<&'hir Expr<'hir>>, + /// `while` loop condition + pub condition: &'hir Expr<'hir>, + /// `while` loop body + pub body: &'hir Expr<'hir>, } impl<'hir> While<'hir> { #[inline] + /// Parses a desugared `while` loop pub const fn hir(expr: &Expr<'hir>) -> Option { if let ExprKind::Loop( Block { @@ -263,11 +324,11 @@ impl<'hir> While<'hir> { kind: ExprKind::If( Expr { - kind: ExprKind::DropTemps(if_cond), + kind: ExprKind::DropTemps(condition), .. }, - if_then, - if_else_ref, + body, + _, ), .. }), @@ -278,59 +339,53 @@ impl<'hir> While<'hir> { _, ) = expr.kind { - let if_else = *if_else_ref; - return Some(Self { - if_cond, - if_then, - if_else, - }); + return Some(Self { condition, body }); } None } } +/// A desugared `while let` loop pub struct WhileLet<'hir> { - pub if_expr: &'hir Expr<'hir>, + /// `while let` loop item pattern pub let_pat: &'hir Pat<'hir>, + /// `while let` loop scrutinee pub let_expr: &'hir Expr<'hir>, + /// `while let` loop body pub if_then: &'hir Expr<'hir>, - pub if_else: Option<&'hir Expr<'hir>>, } impl<'hir> WhileLet<'hir> { #[inline] + /// Parses a desugared `while let` loop pub const fn hir(expr: &Expr<'hir>) -> Option { if let ExprKind::Loop( Block { - expr: Some(if_expr), .. + expr: + Some(Expr { + kind: + ExprKind::If( + Expr { + kind: ExprKind::Let(let_pat, let_expr, _), + .. + }, + if_then, + _, + ), + .. + }), + .. }, _, LoopSource::While, _, ) = expr.kind { - if let Expr { - kind: - ExprKind::If( - Expr { - kind: ExprKind::Let(let_pat, let_expr, _), - .. - }, - if_then, - if_else_ref, - ), - .. - } = if_expr - { - let if_else = *if_else_ref; - return Some(Self { - if_expr, - let_pat, - let_expr, - if_then, - if_else, - }); - } + return Some(Self { + let_pat, + let_expr, + if_then, + }); } None } @@ -532,7 +587,7 @@ pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool { // } // ``` if_chain! { - if let Some(ref expr) = local.init; + if let Some(expr) = local.init; if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind; then { return true; diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index a44f2df2fd631..6e9a1de21eef5 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -232,9 +232,7 @@ impl HirEqInterExpr<'_, '_, '_> { (&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => { self.eq_expr(lc, rc) && self.eq_expr(&**lt, &**rt) && both(le, re, |l, r| self.eq_expr(l, r)) }, - (&ExprKind::Let(ref lp, ref le, _), &ExprKind::Let(ref rp, ref re, _)) => { - self.eq_pat(lp, rp) && self.eq_expr(le, re) - }, + (&ExprKind::Let(lp, le, _), &ExprKind::Let(rp, re, _)) => self.eq_pat(lp, rp) && self.eq_expr(le, re), (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node, (&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => { lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name) @@ -668,7 +666,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { } } }, - ExprKind::Let(ref pat, ref expr, _) => { + ExprKind::Let(pat, expr, _) => { self.hash_expr(expr); self.hash_pat(pat); }, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index ddff1686ba2ce..757485d19d25a 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2,6 +2,7 @@ #![feature(in_band_lifetimes)] #![feature(iter_zip)] #![feature(rustc_private)] +#![feature(control_flow_enum)] #![recursion_limit = "512"] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)] @@ -62,23 +63,27 @@ use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; use if_chain::if_chain; -use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind}; +use rustc_ast::ast::{self, Attribute, LitKind}; use rustc_data_structures::unhash::UnhashMap; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; +use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor}; -use rustc_hir::LangItem::{ResultErr, ResultOk}; +use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::{ def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, - ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Node, Param, Pat, PatKind, Path, - PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, + ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node, Param, Pat, + PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, }; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::exports::Export; use rustc_middle::hir::map::Map; +use rustc_middle::hir::place::PlaceBase; use rustc_middle::ty as rustc_ty; -use rustc_middle::ty::{layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; +use rustc_middle::ty::binding::BindingMode; +use rustc_middle::ty::{layout::IntegerExt, BorrowKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeFoldable, UpvarCapture}; use rustc_semver::RustcVersion; use rustc_session::Session; use rustc_span::hygiene::{ExpnKind, MacroKind}; @@ -89,7 +94,7 @@ use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::Integer; use crate::consts::{constant, Constant}; -use crate::ty::{can_partially_move_ty, is_recursively_primitive_type}; +use crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type}; pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Option { if let Ok(version) = RustcVersion::parse(msrv) { @@ -255,7 +260,17 @@ pub fn in_macro(span: Span) -> bool { } pub fn is_unit_expr(expr: &Expr<'_>) -> bool { - matches!(expr.kind, ExprKind::Block(Block { stmts: [], expr: None, .. }, _) | ExprKind::Tup([])) + matches!( + expr.kind, + ExprKind::Block( + Block { + stmts: [], + expr: None, + .. + }, + _ + ) | ExprKind::Tup([]) + ) } /// Checks if given pattern is a wildcard (`_`) @@ -629,12 +644,106 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) - false } +/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent" +/// constructor from the std library +fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool { + let std_types_symbols = &[ + sym::string_type, + sym::vec_type, + sym::vecdeque_type, + sym::LinkedList, + sym::hashmap_type, + sym::BTreeMap, + sym::hashset_type, + sym::BTreeSet, + sym::BinaryHeap, + ]; + + if let QPath::TypeRelative(_, method) = path { + if method.ident.name == sym::new { + if let Some(impl_did) = cx.tcx.impl_of_method(def_id) { + if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() { + return std_types_symbols + .iter() + .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did)); + } + } + } + } + false +} + +/// Returns true if the expr is equal to `Default::default()` of it's type when evaluated. +/// It doesn't cover all cases, for example indirect function calls (some of std +/// functions are supported) but it is the best we have. +pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + match &e.kind { + ExprKind::Lit(lit) => match lit.node { + LitKind::Bool(false) | LitKind::Int(0, _) => true, + LitKind::Str(s, _) => s.is_empty(), + _ => false, + }, + ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)), + ExprKind::Repeat(x, _) => is_default_equivalent(cx, x), + ExprKind::Call(repl_func, _) => if_chain! { + if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind; + if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id(); + if is_diag_trait_item(cx, repl_def_id, sym::Default) + || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath); + then { + true + } + else { + false + } + }, + ExprKind::Path(qpath) => is_lang_ctor(cx, qpath, OptionNone), + ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])), + _ => false, + } +} + /// Checks if the top level expression can be moved into a closure as is. -pub fn can_move_expr_to_closure_no_visit(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, jump_targets: &[HirId]) -> bool { +/// Currently checks for: +/// * Break/Continue outside the given loop HIR ids. +/// * Yield/Return statments. +/// * Inline assembly. +/// * Usages of a field of a local where the type of the local can be partially moved. +/// +/// For example, given the following function: +/// +/// ``` +/// fn f<'a>(iter: &mut impl Iterator) { +/// for item in iter { +/// let s = item.1; +/// if item.0 > 10 { +/// continue; +/// } else { +/// s.clear(); +/// } +/// } +/// } +/// ``` +/// +/// When called on the expression `item.0` this will return false unless the local `item` is in the +/// `ignore_locals` set. The type `(usize, &mut String)` can have the second element moved, so it +/// isn't always safe to move into a closure when only a single field is needed. +/// +/// When called on the `continue` expression this will return false unless the outer loop expression +/// is in the `loop_ids` set. +/// +/// Note that this check is not recursive, so passing the `if` expression will always return true +/// even though sub-expressions might return false. +pub fn can_move_expr_to_closure_no_visit( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + loop_ids: &[HirId], + ignore_locals: &HirIdSet, +) -> bool { match expr.kind { ExprKind::Break(Destination { target_id: Ok(id), .. }, _) | ExprKind::Continue(Destination { target_id: Ok(id), .. }) - if jump_targets.contains(&id) => + if loop_ids.contains(&id) => { true }, @@ -646,25 +755,170 @@ pub fn can_move_expr_to_closure_no_visit(cx: &LateContext<'tcx>, expr: &'tcx Exp | ExprKind::LlvmInlineAsm(_) => false, // Accessing a field of a local value can only be done if the type isn't // partially moved. - ExprKind::Field(base_expr, _) - if matches!( - base_expr.kind, - ExprKind::Path(QPath::Resolved(_, Path { res: Res::Local(_), .. })) - ) && can_partially_move_ty(cx, cx.typeck_results().expr_ty(base_expr)) => - { + ExprKind::Field( + &Expr { + hir_id, + kind: + ExprKind::Path(QPath::Resolved( + _, + Path { + res: Res::Local(local_id), + .. + }, + )), + .. + }, + _, + ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => { // TODO: check if the local has been partially moved. Assume it has for now. false - } + }, _ => true, } } -/// Checks if the expression can be moved into a closure as is. -pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { +/// How a local is captured by a closure +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum CaptureKind { + Value, + Ref(Mutability), +} +impl CaptureKind { + pub fn is_imm_ref(self) -> bool { + self == Self::Ref(Mutability::Not) + } +} +impl std::ops::BitOr for CaptureKind { + type Output = Self; + fn bitor(self, rhs: Self) -> Self::Output { + match (self, rhs) { + (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value, + (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_)) + | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut), + (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not), + } + } +} +impl std::ops::BitOrAssign for CaptureKind { + fn bitor_assign(&mut self, rhs: Self) { + *self = *self | rhs; + } +} + +/// Given an expression referencing a local, determines how it would be captured in a closure. +/// Note as this will walk up to parent expressions until the capture can be determined it should +/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or +/// function argument (other than a receiver). +pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind { + fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind { + let mut capture = CaptureKind::Ref(Mutability::Not); + pat.each_binding_or_first(&mut |_, id, span, _| match cx + .typeck_results() + .extract_binding_mode(cx.sess(), id, span) + .unwrap() + { + BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => { + capture = CaptureKind::Value; + }, + BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => { + capture = CaptureKind::Ref(Mutability::Mut); + }, + _ => (), + }); + capture + } + + debug_assert!(matches!( + e.kind, + ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. })) + )); + + let map = cx.tcx.hir(); + let mut child_id = e.hir_id; + let mut capture = CaptureKind::Value; + let mut capture_expr_ty = e; + + for (parent_id, parent) in map.parent_iter(e.hir_id) { + if let [Adjustment { + kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)), + target, + }, ref adjust @ ..] = *cx + .typeck_results() + .adjustments() + .get(child_id) + .map_or(&[][..], |x| &**x) + { + if let rustc_ty::RawPtr(TypeAndMut { mutbl: mutability, .. }) | rustc_ty::Ref(_, _, mutability) = + *adjust.last().map_or(target, |a| a.target).kind() + { + return CaptureKind::Ref(mutability); + } + } + + match parent { + Node::Expr(e) => match e.kind { + ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability), + ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not), + ExprKind::Assign(lhs, ..) | ExprKind::Assign(_, lhs, _) if lhs.hir_id == child_id => { + return CaptureKind::Ref(Mutability::Mut); + }, + ExprKind::Field(..) => { + if capture == CaptureKind::Value { + capture_expr_ty = e; + } + }, + ExprKind::Let(pat, ..) => { + let mutability = match pat_capture_kind(cx, pat) { + CaptureKind::Value => Mutability::Not, + CaptureKind::Ref(m) => m, + }; + return CaptureKind::Ref(mutability); + }, + ExprKind::Match(_, arms, _) => { + let mut mutability = Mutability::Not; + for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) { + match capture { + CaptureKind::Value => break, + CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut, + CaptureKind::Ref(Mutability::Not) => (), + } + } + return CaptureKind::Ref(mutability); + }, + _ => break, + }, + Node::Local(l) => match pat_capture_kind(cx, l.pat) { + CaptureKind::Value => break, + capture @ CaptureKind::Ref(_) => return capture, + }, + _ => break, + } + + child_id = parent_id; + } + + if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) { + // Copy types are never automatically captured by value. + CaptureKind::Ref(Mutability::Not) + } else { + capture + } +} + +/// Checks if the expression can be moved into a closure as is. This will return a list of captures +/// if so, otherwise, `None`. +pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option> { struct V<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, + // Stack of potential break targets contained in the expression. loops: Vec, + /// Local variables created in the expression. These don't need to be captured. + locals: HirIdSet, + /// Whether this expression can be turned into a closure. allow_closure: bool, + /// Locals which need to be captured, and whether they need to be by value, reference, or + /// mutable reference. + captures: HirIdMap, } impl Visitor<'tcx> for V<'_, 'tcx> { type Map = ErasedMap<'tcx>; @@ -676,24 +930,67 @@ pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> if !self.allow_closure { return; } - if let ExprKind::Loop(b, ..) = e.kind { - self.loops.push(e.hir_id); - self.visit_block(b); - self.loops.pop(); - } else { - self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops); - walk_expr(self, e); + + match e.kind { + ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => { + if !self.locals.contains(&l) { + let cap = capture_local_usage(self.cx, e); + self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap); + } + }, + ExprKind::Closure(..) => { + let closure_id = self.cx.tcx.hir().local_def_id(e.hir_id).to_def_id(); + for capture in self.cx.typeck_results().closure_min_captures_flattened(closure_id) { + let local_id = match capture.place.base { + PlaceBase::Local(id) => id, + PlaceBase::Upvar(var) => var.var_path.hir_id, + _ => continue, + }; + if !self.locals.contains(&local_id) { + let capture = match capture.info.capture_kind { + UpvarCapture::ByValue(_) => CaptureKind::Value, + UpvarCapture::ByRef(borrow) => match borrow.kind { + BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not), + BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => { + CaptureKind::Ref(Mutability::Mut) + }, + }, + }; + self.captures + .entry(local_id) + .and_modify(|e| *e |= capture) + .or_insert(capture); + } + } + }, + ExprKind::Loop(b, ..) => { + self.loops.push(e.hir_id); + self.visit_block(b); + self.loops.pop(); + }, + _ => { + self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals); + walk_expr(self, e); + }, } } + + fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) { + p.each_binding_or_first(&mut |_, id, _, _| { + self.locals.insert(id); + }); + } } let mut v = V { cx, allow_closure: true, loops: Vec::new(), + locals: HirIdSet::default(), + captures: HirIdMap::default(), }; v.visit_expr(expr); - v.allow_closure + v.allow_closure.then(|| v.captures) } /// Returns the method names and argument list of nested method call expressions that make up @@ -1365,13 +1662,13 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) { conds.push(&*cond); - if let ExprKind::Block(ref block, _) = then.kind { + if let ExprKind::Block(block, _) = then.kind { blocks.push(block); } else { panic!("ExprKind::If node is not an ExprKind::Block"); } - if let Some(ref else_expr) = r#else { + if let Some(else_expr) = r#else { expr = else_expr; } else { break; @@ -1708,7 +2005,7 @@ pub fn peel_hir_expr_while<'tcx>( pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) { let mut remaining = count; let e = peel_hir_expr_while(expr, |e| match e.kind { - ExprKind::AddrOf(BorrowKind::Ref, _, e) if remaining != 0 => { + ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => { remaining -= 1; Some(e) }, @@ -1722,7 +2019,7 @@ pub fn peel_n_hir_expr_refs(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, pub fn peel_hir_expr_refs(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) { let mut count = 0; let e = peel_hir_expr_while(expr, |e| match e.kind { - ExprKind::AddrOf(BorrowKind::Ref, _, e) => { + ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => { count += 1; Some(e) }, diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 4a9c4fd0276b3..fa57dfbb57edc 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -13,9 +13,12 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { 1,53,0 { OR_PATTERNS } + 1,52,0 { STR_SPLIT_ONCE } 1,50,0 { BOOL_THEN } + 1,47,0 { TAU } 1,46,0 { CONST_IF_MATCH } 1,45,0 { STR_STRIP_PREFIX } + 1,43,0 { LOG2_10, LOG10_2 } 1,42,0 { MATCHES_MACRO } 1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE } 1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index b0c3fe1e5a712..d7e46c2d3eb9d 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -68,6 +68,7 @@ pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"]; pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"]; pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; +pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; #[cfg(feature = "internal-lints")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; #[cfg(feature = "internal-lints")] diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index e5bbf75c3b0a1..6cb2bd7f6efba 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -36,9 +36,7 @@ pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&Ru ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {:#?}", predicate), ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {:#?}", predicate), ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {:#?}", predicate), - ty::PredicateKind::Coerce(_) => { - panic!("coerce predicate on function: {:#?}", predicate) - }, + ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {:#?}", predicate), ty::PredicateKind::Trait(pred) => { if Some(pred.def_id()) == tcx.lang_items().sized_trait() { continue; diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 65d93e8f86e43..ab05a0b423853 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -329,7 +329,7 @@ fn has_enclosing_paren(sugg: impl AsRef) -> bool { } } -// Copied from the rust standart library, and then edited +/// Copied from the rust standard library, and then edited macro_rules! forward_binop_impls_to_ref { (impl $imp:ident, $method:ident for $t:ty, type Output = $o:ty) => { impl $imp<$t> for &$t { diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 3cd8ed5aa2c8c..d6f9ebe89bc7f 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -10,7 +10,7 @@ use rustc_hir::{TyKind, Unsafety}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; -use rustc_middle::ty::{self, TyCtxt, AdtDef, IntTy, Ty, TypeFoldable, UintTy}; +use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, TypeFoldable, UintTy}; use rustc_span::sym; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::DUMMY_SP; @@ -224,6 +224,13 @@ fn is_normalizable_helper<'tcx>( result } +/// Returns true iff the given type is a non aggregate primitive (a bool or char, any integer or +/// floating-point number type). For checking aggregation of primitive types (e.g. tuples and slices +/// of primitive type) see `is_recursively_primitive_type` +pub fn is_non_aggregate_primitive_type(ty: Ty<'_>) -> bool { + matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_)) +} + /// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point /// number type, a str, or an array, slice, or tuple of those types). pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool { diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index ac885e9994404..098ec175fe2d9 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -1,10 +1,9 @@ use crate as utils; use rustc_hir as hir; -use rustc_hir::def::Res; use rustc_hir::intravisit; use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; use rustc_hir::HirIdSet; -use rustc_hir::{Expr, ExprKind, HirId, Path}; +use rustc_hir::{Expr, ExprKind, HirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; @@ -35,12 +34,8 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Some(delegate.used_mutably) } -pub fn is_potentially_mutated<'tcx>(variable: &'tcx Path<'_>, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool { - if let Res::Local(id) = variable.res { - mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&id)) - } else { - true - } +pub fn is_potentially_mutated<'tcx>(variable: HirId, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool { + mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&variable)) } struct MutVarsDelegate { diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index ce00106dd4d80..503effbdad572 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -4,6 +4,7 @@ use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visito use rustc_hir::{def::Res, Arm, Block, Body, BodyId, Destination, Expr, ExprKind, HirId, Stmt}; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; +use std::ops::ControlFlow; /// returns `true` if expr contains match expr desugared from try fn contains_try(expr: &hir::Expr<'_>) -> bool { @@ -133,62 +134,6 @@ where } } -pub struct LocalUsedVisitor<'hir> { - hir: Map<'hir>, - pub local_hir_id: HirId, - pub used: bool, -} - -impl<'hir> LocalUsedVisitor<'hir> { - pub fn new(cx: &LateContext<'hir>, local_hir_id: HirId) -> Self { - Self { - hir: cx.tcx.hir(), - local_hir_id, - used: false, - } - } - - fn check(&mut self, t: T, visit: fn(&mut Self, T)) -> bool { - visit(self, t); - std::mem::replace(&mut self.used, false) - } - - pub fn check_arm(&mut self, arm: &'hir Arm<'_>) -> bool { - self.check(arm, Self::visit_arm) - } - - pub fn check_body(&mut self, body: &'hir Body<'_>) -> bool { - self.check(body, Self::visit_body) - } - - pub fn check_expr(&mut self, expr: &'hir Expr<'_>) -> bool { - self.check(expr, Self::visit_expr) - } - - pub fn check_stmt(&mut self, stmt: &'hir Stmt<'_>) -> bool { - self.check(stmt, Self::visit_stmt) - } -} - -impl<'v> Visitor<'v> for LocalUsedVisitor<'v> { - type Map = Map<'v>; - - fn visit_expr(&mut self, expr: &'v Expr<'v>) { - if self.used { - return; - } - if path_to_local_id(expr, self.local_hir_id) { - self.used = true; - } else { - walk_expr(self, expr); - } - } - - fn nested_visit_map(&mut self) -> NestedVisitorMap { - NestedVisitorMap::OnlyBodies(self.hir) - } -} - /// A type which can be visited. pub trait Visitable<'tcx> { /// Calls the corresponding `visit_*` function on the visitor. @@ -203,7 +148,22 @@ macro_rules! visitable_ref { } }; } +visitable_ref!(Arm, visit_arm); visitable_ref!(Block, visit_block); +visitable_ref!(Body, visit_body); +visitable_ref!(Expr, visit_expr); +visitable_ref!(Stmt, visit_stmt); + +// impl<'tcx, I: IntoIterator> Visitable<'tcx> for I +// where +// I::Item: Visitable<'tcx>, +// { +// fn visit>(self, visitor: &mut V) { +// for x in self { +// x.visit(visitor); +// } +// } +// } /// Calls the given function for each break expression. pub fn visit_break_exprs<'tcx>( @@ -260,3 +220,48 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { v.visit_expr(&cx.tcx.hir().body(body).value); v.found } + +/// Calls the given function for each usage of the given local. +pub fn for_each_local_usage<'tcx, B>( + cx: &LateContext<'tcx>, + visitable: impl Visitable<'tcx>, + id: HirId, + f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow, +) -> ControlFlow { + struct V<'tcx, B, F> { + map: Map<'tcx>, + id: HirId, + f: F, + res: ControlFlow, + } + impl<'tcx, B, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow> Visitor<'tcx> for V<'tcx, B, F> { + type Map = Map<'tcx>; + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.map) + } + + fn visit_expr(&mut self, e: &'tcx Expr<'_>) { + if self.res.is_continue() { + if path_to_local_id(e, self.id) { + self.res = (self.f)(e); + } else { + walk_expr(self, e); + } + } + } + } + + let mut v = V { + map: cx.tcx.hir(), + id, + f, + res: ControlFlow::CONTINUE, + }; + visitable.visit(&mut v); + v.res +} + +/// Checks if the given local is used. +pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool { + for_each_local_usage(cx, visitable, id, |_| ControlFlow::BREAK).is_break() +} diff --git a/doc/adding_lints.md b/doc/adding_lints.md index f2260c3d1a267..004eb28b44640 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -556,14 +556,15 @@ directory. Adding a configuration to a lint can be useful for thresholds or to c behavior that can be seen as a false positive for some users. Adding a configuration is done in the following steps: -1. Adding a new configuration entry to [clippy_utils::conf](/clippy_utils/src/conf.rs) +1. Adding a new configuration entry to [clippy_lints::utils::conf](/clippy_lints/src/utils/conf.rs) like this: ```rust - /// Lint: LINT_NAME. + /// Lint: LINT_NAME. + /// + /// (configuration_ident: Type = DefaultValue), ``` - The configuration value and identifier should usually be the same. The doc comment will be - automatically added to the lint documentation. + The doc comment will be automatically added to the lint documentation. 2. Adding the configuration value to the lint impl struct: 1. This first requires the definition of a lint impl struct. Lint impl structs are usually generated with the `declare_lint_pass!` macro. This struct needs to be defined manually diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index 0a85f65001101..1a6b7c8cb47a9 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -11,6 +11,7 @@ You may need following tooltips to catch up with common operations. Useful Rustc dev guide links: - [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation) +- [Diagnostic items](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html) - [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html) - [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html) @@ -75,20 +76,21 @@ impl LateLintPass<'_> for MyStructLint { # Checking if a type implements a specific trait -There are two ways to do this, depending if the target trait is part of lang items. +There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither. ```rust -use clippy_utils::{implements_trait, match_trait_method, paths}; +use clippy_utils::{implements_trait, is_trait_method, match_trait_method, paths}; +use rustc_span::symbol::sym; impl LateLintPass<'_> for MyStructLint { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - // 1. Using expression and Clippy's convenient method - // we use `match_trait_method` function from Clippy's toolbox - if match_trait_method(cx, expr, &paths::INTO) { - // `expr` implements `Into` trait + // 1. Using diagnostic items with the expression + // we use `is_trait_method` function from Clippy's utils + if is_trait_method(cx, expr, sym::Iterator) { + // method call in `expr` belongs to `Iterator` trait } - // 2. Using type context `TyCtxt` + // 2. Using lang items with the expression type let ty = cx.typeck_results().expr_ty(expr); if cx.tcx.lang_items() // we are looking for the `DefId` of `Drop` trait in lang items @@ -97,15 +99,20 @@ impl LateLintPass<'_> for MyStructLint { .map_or(false, |id| implements_trait(cx, ty, id, &[])) { // `expr` implements `Drop` trait } + + // 3. Using the type path with the expression + // we use `match_trait_method` function from Clippy's utils + if match_trait_method(cx, expr, &paths::INTO) { + // `expr` implements `Into` trait + } } } ``` -> Prefer using lang items, if the target trait is available there. - -A list of defined paths for Clippy can be found in [paths.rs][paths] +> Prefer using diagnostic and lang items, if the target trait has one. We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate. +A list of defined paths for Clippy can be found in [paths.rs][paths] # Checking if a type defines a specific method diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index 8c33fa372eccb..ada033de6e3ab 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -19,6 +19,7 @@ serde_json = {version = "1.0"} tar = {version = "0.4.30"} toml = {version = "0.5"} ureq = {version = "2.0.0-rc3"} +walkdir = {version = "2.3.2"} [features] deny-warnings = [] diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 7d2f3173fb0e7..f1e03ba42966d 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -21,6 +21,7 @@ use clap::{App, Arg, ArgMatches}; use rayon::prelude::*; use serde::{Deserialize, Serialize}; use serde_json::Value; +use walkdir::{DirEntry, WalkDir}; #[cfg(not(windows))] const CLIPPY_DRIVER_PATH: &str = "target/debug/clippy-driver"; @@ -193,32 +194,41 @@ impl CrateSource { } }, CrateSource::Path { name, path, options } => { - use fs_extra::dir; - - // simply copy the entire directory into our target dir - let copy_dest = PathBuf::from(format!("{}/", LINTCHECK_SOURCES)); + // copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file. + // The target/ directory contains a CACHEDIR.TAG file so it is the most commonly skipped directory + // as a result of this filter. + let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name); + if dest_crate_root.exists() { + println!("Deleting existing directory at {:?}", dest_crate_root); + std::fs::remove_dir_all(&dest_crate_root).unwrap(); + } - // the source path of the crate we copied, ${copy_dest}/crate_name - let crate_root = copy_dest.join(name); // .../crates/local_crate + println!("Copying {:?} to {:?}", path, dest_crate_root); - if crate_root.exists() { - println!( - "Not copying {} to {}, destination already exists", - path.display(), - crate_root.display() - ); - } else { - println!("Copying {} to {}", path.display(), copy_dest.display()); + fn is_cache_dir(entry: &DirEntry) -> bool { + std::fs::read(entry.path().join("CACHEDIR.TAG")) + .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55")) + .unwrap_or(false) + } - dir::copy(path, ©_dest, &dir::CopyOptions::new()).unwrap_or_else(|_| { - panic!("Failed to copy from {}, to {}", path.display(), crate_root.display()) - }); + for entry in WalkDir::new(path).into_iter().filter_entry(|e| !is_cache_dir(e)) { + let entry = entry.unwrap(); + let entry_path = entry.path(); + let relative_entry_path = entry_path.strip_prefix(path).unwrap(); + let dest_path = dest_crate_root.join(relative_entry_path); + let metadata = entry_path.symlink_metadata().unwrap(); + + if metadata.is_dir() { + std::fs::create_dir(dest_path).unwrap(); + } else if metadata.is_file() { + std::fs::copy(entry_path, dest_path).unwrap(); + } } Crate { version: String::from("local"), name: name.clone(), - path: crate_root, + path: dest_crate_root, options: options.clone(), } }, diff --git a/rust-toolchain b/rust-toolchain index 23887f1784549..92bde3423a20a 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2021-08-12" +channel = "nightly-2021-09-08" components = ["llvm-tools-preview", "rustc-dev", "rust-src"] diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 0a82377a10e42..c63c47690d52a 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -1,10 +1,12 @@ #![feature(test)] // compiletest_rs requires this attribute #![feature(once_cell)] -#![feature(try_blocks)] +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![warn(rust_2018_idioms, unused_lifetimes)] use compiletest_rs as compiletest; use compiletest_rs::common::Mode as TestMode; +use std::collections::HashMap; use std::env::{self, remove_var, set_var, var_os}; use std::ffi::{OsStr, OsString}; use std::fs; @@ -16,6 +18,34 @@ mod cargo; // whether to run internal tests or not const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints"); +/// All crates used in UI tests are listed here +static TEST_DEPENDENCIES: &[&str] = &[ + "clippy_utils", + "derive_new", + "if_chain", + "itertools", + "quote", + "regex", + "serde", + "serde_derive", + "syn", +]; + +// Test dependencies may need an `extern crate` here to ensure that they show up +// in the depinfo file (otherwise cargo thinks they are unused) +#[allow(unused_extern_crates)] +extern crate clippy_utils; +#[allow(unused_extern_crates)] +extern crate derive_new; +#[allow(unused_extern_crates)] +extern crate if_chain; +#[allow(unused_extern_crates)] +extern crate itertools; +#[allow(unused_extern_crates)] +extern crate quote; +#[allow(unused_extern_crates)] +extern crate syn; + fn host_lib() -> PathBuf { option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from) } @@ -24,71 +54,58 @@ fn clippy_driver_path() -> PathBuf { option_env!("CLIPPY_DRIVER_PATH").map_or(cargo::TARGET_LIB.join("clippy-driver"), PathBuf::from) } -// When we'll want to use `extern crate ..` for a dependency that is used -// both by the crate and the compiler itself, we can't simply pass -L flags -// as we'll get a duplicate matching versions. Instead, disambiguate with -// `--extern dep=path`. -// See https://github.com/rust-lang/rust-clippy/issues/4015. -// -// FIXME: We cannot use `cargo build --message-format=json` to resolve to dependency files. -// Because it would force-rebuild if the options passed to `build` command is not the same -// as what we manually pass to `cargo` invocation -fn third_party_crates() -> String { - use std::collections::HashMap; - static CRATES: &[&str] = &[ - "clippy_lints", - "clippy_utils", - "if_chain", - "quote", - "regex", - "serde", - "serde_derive", - "syn", - ]; - let dep_dir = cargo::TARGET_LIB.join("deps"); - let mut crates: HashMap<&str, Vec> = HashMap::with_capacity(CRATES.len()); - let mut flags = String::new(); - for entry in fs::read_dir(dep_dir).unwrap().flatten() { - let path = entry.path(); - if let Some(name) = try { - let name = path.file_name()?.to_str()?; - let (name, _) = name.strip_suffix(".rlib")?.strip_prefix("lib")?.split_once('-')?; - CRATES.iter().copied().find(|&c| c == name)? - } { - flags += &format!(" --extern {}={}", name, path.display()); - crates.entry(name).or_default().push(path.clone()); +/// Produces a string with an `--extern` flag for all UI test crate +/// dependencies. +/// +/// The dependency files are located by parsing the depinfo file for this test +/// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test +/// dependencies must be added to Cargo.toml at the project root. Test +/// dependencies that are not *directly* used by this test module require an +/// `extern crate` declaration. +fn extern_flags() -> String { + let current_exe_depinfo = { + let mut path = env::current_exe().unwrap(); + path.set_extension("d"); + std::fs::read_to_string(path).unwrap() + }; + let mut crates: HashMap<&str, &str> = HashMap::with_capacity(TEST_DEPENDENCIES.len()); + for line in current_exe_depinfo.lines() { + // each dependency is expected to have a Makefile rule like `/path/to/crate-hash.rlib:` + let parse_name_path = || { + if line.starts_with(char::is_whitespace) { + return None; + } + let path_str = line.strip_suffix(':')?; + let path = Path::new(path_str); + if !matches!(path.extension()?.to_str()?, "rlib" | "so" | "dylib" | "dll") { + return None; + } + let (name, _hash) = path.file_stem()?.to_str()?.rsplit_once('-')?; + // the "lib" prefix is not present for dll files + let name = name.strip_prefix("lib").unwrap_or(name); + Some((name, path_str)) + }; + if let Some((name, path)) = parse_name_path() { + if TEST_DEPENDENCIES.contains(&name) { + // A dependency may be listed twice if it is available in sysroot, + // and the sysroot dependencies are listed first. As of the writing, + // this only seems to apply to if_chain. + crates.insert(name, path); + } } } - crates.retain(|_, paths| paths.len() > 1); - if !crates.is_empty() { - let crate_names = crates.keys().map(|s| format!("`{}`", s)).collect::>().join(", "); - // add backslashes for an easy copy-paste `rm` command - let paths = crates - .into_values() - .flatten() - .map(|p| strip_current_dir(&p).display().to_string()) - .collect::>() - .join(" \\\n"); - // Check which action should be done in order to remove compiled deps. - // If pre-installed version of compiler is used, `cargo clean` will do. - // Otherwise (for bootstrapped compiler), the dependencies directory - // must be removed manually. - let suggested_action = if std::env::var_os("RUSTC_BOOTSTRAP").is_some() { - "removing the stageN-tools directory" - } else { - "running `cargo clean`" - }; - - panic!( - "\n----------------------------------------------------------------------\n\ - ERROR: Found multiple rlibs for crates: {}\n\ - Try {} or remove the following files:\n\n{}\n\n\ - For details on this error see https://github.com/rust-lang/rust-clippy/issues/7343\n\ - ----------------------------------------------------------------------\n", - crate_names, suggested_action, paths - ); + let not_found: Vec<&str> = TEST_DEPENDENCIES + .iter() + .copied() + .filter(|n| !crates.contains_key(n)) + .collect(); + if !not_found.is_empty() { + panic!("dependencies not found in depinfo: {:?}", not_found); } - flags + crates + .into_iter() + .map(|(name, path)| format!("--extern {}={} ", name, path)) + .collect() } fn default_config() -> compiletest::Config { @@ -104,11 +121,14 @@ fn default_config() -> compiletest::Config { config.compile_lib_path = path; } + // Using `-L dependency={}` enforces that external dependencies are added with `--extern`. + // This is valuable because a) it allows us to monitor what external dependencies are used + // and b) it ensures that conflicting rlibs are resolved properly. config.target_rustcflags = Some(format!( - "--emit=metadata -L {0} -L {1} -Dwarnings -Zui-testing {2}", + "--emit=metadata -L dependency={} -L dependency={} -Dwarnings -Zui-testing {}", host_lib().join("deps").display(), cargo::TARGET_LIB.join("deps").display(), - third_party_crates(), + extern_flags(), )); config.build_base = host_lib().join("test_build_base"); @@ -315,12 +335,3 @@ impl Drop for VarGuard { } } } - -fn strip_current_dir(path: &Path) -> &Path { - if let Ok(curr) = env::current_dir() { - if let Ok(stripped) = path.strip_prefix(curr) { - return stripped; - } - } - path -} diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 4ede20c52583b..54f452172deb4 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -6,6 +6,8 @@ // Dogfood cannot run on Windows #![cfg(not(windows))] #![feature(once_cell)] +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![warn(rust_2018_idioms, unused_lifetimes)] use std::lazy::SyncLazy; use std::path::PathBuf; diff --git a/tests/fmt.rs b/tests/fmt.rs index 7616d8001e885..be42f1fbb2023 100644 --- a/tests/fmt.rs +++ b/tests/fmt.rs @@ -1,3 +1,6 @@ +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![warn(rust_2018_idioms, unused_lifetimes)] + use std::path::PathBuf; use std::process::Command; diff --git a/tests/integration.rs b/tests/integration.rs index 1718089e8bd27..7e3eff3c7324f 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,4 +1,6 @@ #![cfg(feature = "integration")] +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![warn(rust_2018_idioms, unused_lifetimes)] use std::env; use std::ffi::OsStr; diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index 2f8989c8e1140..b4d94dc983fec 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -1,3 +1,6 @@ +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![warn(rust_2018_idioms, unused_lifetimes)] + use std::ffi::OsStr; use std::path::PathBuf; diff --git a/tests/missing-test-files.rs b/tests/missing-test-files.rs index 9cef7438d225c..bd342e390f52f 100644 --- a/tests/missing-test-files.rs +++ b/tests/missing-test-files.rs @@ -1,3 +1,5 @@ +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![warn(rust_2018_idioms, unused_lifetimes)] #![allow(clippy::assertions_on_constants)] use std::fs::{self, DirEntry}; diff --git a/tests/ui-cargo/feature_name/fail/Cargo.toml b/tests/ui-cargo/feature_name/fail/Cargo.toml new file mode 100644 index 0000000000000..97d51462a946f --- /dev/null +++ b/tests/ui-cargo/feature_name/fail/Cargo.toml @@ -0,0 +1,21 @@ + +# Content that triggers the lint goes here + +[package] +name = "feature_name" +version = "0.1.0" +publish = false + +[workspace] + +[features] +use-qwq = [] +use_qwq = [] +with-owo = [] +with_owo = [] +qvq-support = [] +qvq_support = [] +no-qaq = [] +no_qaq = [] +not-orz = [] +not_orz = [] diff --git a/tests/ui-cargo/feature_name/fail/src/main.rs b/tests/ui-cargo/feature_name/fail/src/main.rs new file mode 100644 index 0000000000000..64f01a98c90e9 --- /dev/null +++ b/tests/ui-cargo/feature_name/fail/src/main.rs @@ -0,0 +1,7 @@ +// compile-flags: --crate-name=feature_name +#![warn(clippy::redundant_feature_names)] +#![warn(clippy::negative_feature_names)] + +fn main() { + // test code goes here +} diff --git a/tests/ui-cargo/feature_name/fail/src/main.stderr b/tests/ui-cargo/feature_name/fail/src/main.stderr new file mode 100644 index 0000000000000..b9e6cb49bc982 --- /dev/null +++ b/tests/ui-cargo/feature_name/fail/src/main.stderr @@ -0,0 +1,44 @@ +error: the "no-" prefix in the feature name "no-qaq" is negative + | + = note: `-D clippy::negative-feature-names` implied by `-D warnings` + = help: consider renaming the feature to "qaq", but make sure the feature adds functionality + +error: the "no_" prefix in the feature name "no_qaq" is negative + | + = help: consider renaming the feature to "qaq", but make sure the feature adds functionality + +error: the "not-" prefix in the feature name "not-orz" is negative + | + = help: consider renaming the feature to "orz", but make sure the feature adds functionality + +error: the "not_" prefix in the feature name "not_orz" is negative + | + = help: consider renaming the feature to "orz", but make sure the feature adds functionality + +error: the "-support" suffix in the feature name "qvq-support" is redundant + | + = note: `-D clippy::redundant-feature-names` implied by `-D warnings` + = help: consider renaming the feature to "qvq" + +error: the "_support" suffix in the feature name "qvq_support" is redundant + | + = help: consider renaming the feature to "qvq" + +error: the "use-" prefix in the feature name "use-qwq" is redundant + | + = help: consider renaming the feature to "qwq" + +error: the "use_" prefix in the feature name "use_qwq" is redundant + | + = help: consider renaming the feature to "qwq" + +error: the "with-" prefix in the feature name "with-owo" is redundant + | + = help: consider renaming the feature to "owo" + +error: the "with_" prefix in the feature name "with_owo" is redundant + | + = help: consider renaming the feature to "owo" + +error: aborting due to 10 previous errors + diff --git a/tests/ui-cargo/feature_name/pass/Cargo.toml b/tests/ui-cargo/feature_name/pass/Cargo.toml new file mode 100644 index 0000000000000..cf947312bd479 --- /dev/null +++ b/tests/ui-cargo/feature_name/pass/Cargo.toml @@ -0,0 +1,9 @@ + +# This file should not trigger the lint + +[package] +name = "feature_name" +version = "0.1.0" +publish = false + +[workspace] diff --git a/tests/ui-cargo/feature_name/pass/src/main.rs b/tests/ui-cargo/feature_name/pass/src/main.rs new file mode 100644 index 0000000000000..64f01a98c90e9 --- /dev/null +++ b/tests/ui-cargo/feature_name/pass/src/main.rs @@ -0,0 +1,7 @@ +// compile-flags: --crate-name=feature_name +#![warn(clippy::redundant_feature_names)] +#![warn(clippy::negative_feature_names)] + +fn main() { + // test code goes here +} diff --git a/tests/ui-cargo/module_style/fail_mod/Cargo.toml b/tests/ui-cargo/module_style/fail_mod/Cargo.toml new file mode 100644 index 0000000000000..27b61c09fb48b --- /dev/null +++ b/tests/ui-cargo/module_style/fail_mod/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "fail" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/fail_mod/src/bad/inner.rs b/tests/ui-cargo/module_style/fail_mod/src/bad/inner.rs new file mode 100644 index 0000000000000..91cd540a28fdd --- /dev/null +++ b/tests/ui-cargo/module_style/fail_mod/src/bad/inner.rs @@ -0,0 +1 @@ +pub mod stuff; diff --git a/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff.rs b/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff.rs new file mode 100644 index 0000000000000..7713fa9d35c4a --- /dev/null +++ b/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff.rs @@ -0,0 +1,3 @@ +pub mod most; + +pub struct Inner; diff --git a/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff/most.rs b/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff/most.rs new file mode 100644 index 0000000000000..5a5eaf9670f90 --- /dev/null +++ b/tests/ui-cargo/module_style/fail_mod/src/bad/inner/stuff/most.rs @@ -0,0 +1 @@ +pub struct Snarks; diff --git a/tests/ui-cargo/module_style/fail_mod/src/bad/mod.rs b/tests/ui-cargo/module_style/fail_mod/src/bad/mod.rs new file mode 100644 index 0000000000000..a12734db7cb5f --- /dev/null +++ b/tests/ui-cargo/module_style/fail_mod/src/bad/mod.rs @@ -0,0 +1,3 @@ +pub mod inner; + +pub struct Thing; diff --git a/tests/ui-cargo/module_style/fail_mod/src/main.rs b/tests/ui-cargo/module_style/fail_mod/src/main.rs new file mode 100644 index 0000000000000..3e985d4e904c1 --- /dev/null +++ b/tests/ui-cargo/module_style/fail_mod/src/main.rs @@ -0,0 +1,9 @@ +#![warn(clippy::self_named_module_files)] + +mod bad; + +fn main() { + let _ = bad::Thing; + let _ = bad::inner::stuff::Inner; + let _ = bad::inner::stuff::most::Snarks; +} diff --git a/tests/ui-cargo/module_style/fail_mod/src/main.stderr b/tests/ui-cargo/module_style/fail_mod/src/main.stderr new file mode 100644 index 0000000000000..af4c298b31085 --- /dev/null +++ b/tests/ui-cargo/module_style/fail_mod/src/main.stderr @@ -0,0 +1,19 @@ +error: `mod.rs` files are required, found `/bad/inner.rs` + --> $DIR/bad/inner.rs:1:1 + | +LL | pub mod stuff; + | ^ + | + = note: `-D clippy::self-named-module-files` implied by `-D warnings` + = help: move `/bad/inner.rs` to `/bad/inner/mod.rs` + +error: `mod.rs` files are required, found `/bad/inner/stuff.rs` + --> $DIR/bad/inner/stuff.rs:1:1 + | +LL | pub mod most; + | ^ + | + = help: move `/bad/inner/stuff.rs` to `/bad/inner/stuff/mod.rs` + +error: aborting due to 2 previous errors + diff --git a/tests/ui-cargo/module_style/fail_no_mod/Cargo.toml b/tests/ui-cargo/module_style/fail_no_mod/Cargo.toml new file mode 100644 index 0000000000000..27b61c09fb48b --- /dev/null +++ b/tests/ui-cargo/module_style/fail_no_mod/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "fail" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/fail_no_mod/src/bad/mod.rs b/tests/ui-cargo/module_style/fail_no_mod/src/bad/mod.rs new file mode 100644 index 0000000000000..f19ab10d5fb06 --- /dev/null +++ b/tests/ui-cargo/module_style/fail_no_mod/src/bad/mod.rs @@ -0,0 +1 @@ +pub struct Thing; diff --git a/tests/ui-cargo/module_style/fail_no_mod/src/main.rs b/tests/ui-cargo/module_style/fail_no_mod/src/main.rs new file mode 100644 index 0000000000000..c6e9045b8dc47 --- /dev/null +++ b/tests/ui-cargo/module_style/fail_no_mod/src/main.rs @@ -0,0 +1,7 @@ +#![warn(clippy::mod_module_files)] + +mod bad; + +fn main() { + let _ = bad::Thing; +} diff --git a/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr b/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr new file mode 100644 index 0000000000000..11e15db7fb96b --- /dev/null +++ b/tests/ui-cargo/module_style/fail_no_mod/src/main.stderr @@ -0,0 +1,11 @@ +error: `mod.rs` files are not allowed, found `/bad/mod.rs` + --> $DIR/bad/mod.rs:1:1 + | +LL | pub struct Thing; + | ^ + | + = note: `-D clippy::mod-module-files` implied by `-D warnings` + = help: move `/bad/mod.rs` to `/bad.rs` + +error: aborting due to previous error + diff --git a/tests/ui-cargo/module_style/pass_mod/Cargo.toml b/tests/ui-cargo/module_style/pass_mod/Cargo.toml new file mode 100644 index 0000000000000..27b61c09fb48b --- /dev/null +++ b/tests/ui-cargo/module_style/pass_mod/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "fail" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/pass_mod/src/bad/mod.rs b/tests/ui-cargo/module_style/pass_mod/src/bad/mod.rs new file mode 100644 index 0000000000000..f19ab10d5fb06 --- /dev/null +++ b/tests/ui-cargo/module_style/pass_mod/src/bad/mod.rs @@ -0,0 +1 @@ +pub struct Thing; diff --git a/tests/ui-cargo/module_style/pass_mod/src/main.rs b/tests/ui-cargo/module_style/pass_mod/src/main.rs new file mode 100644 index 0000000000000..9e08715fc0522 --- /dev/null +++ b/tests/ui-cargo/module_style/pass_mod/src/main.rs @@ -0,0 +1,10 @@ +#![warn(clippy::self_named_module_files)] + +mod bad; +mod more; + +fn main() { + let _ = bad::Thing; + let _ = more::foo::Foo; + let _ = more::inner::Inner; +} diff --git a/tests/ui-cargo/module_style/pass_mod/src/more/foo.rs b/tests/ui-cargo/module_style/pass_mod/src/more/foo.rs new file mode 100644 index 0000000000000..4a835673a596b --- /dev/null +++ b/tests/ui-cargo/module_style/pass_mod/src/more/foo.rs @@ -0,0 +1 @@ +pub struct Foo; diff --git a/tests/ui-cargo/module_style/pass_mod/src/more/inner/mod.rs b/tests/ui-cargo/module_style/pass_mod/src/more/inner/mod.rs new file mode 100644 index 0000000000000..aa84f78cc2ca1 --- /dev/null +++ b/tests/ui-cargo/module_style/pass_mod/src/more/inner/mod.rs @@ -0,0 +1 @@ +pub struct Inner; diff --git a/tests/ui-cargo/module_style/pass_mod/src/more/mod.rs b/tests/ui-cargo/module_style/pass_mod/src/more/mod.rs new file mode 100644 index 0000000000000..d79569f78ffbe --- /dev/null +++ b/tests/ui-cargo/module_style/pass_mod/src/more/mod.rs @@ -0,0 +1,2 @@ +pub mod foo; +pub mod inner; diff --git a/tests/ui-cargo/module_style/pass_no_mod/Cargo.toml b/tests/ui-cargo/module_style/pass_no_mod/Cargo.toml new file mode 100644 index 0000000000000..3c0896dd2cda7 --- /dev/null +++ b/tests/ui-cargo/module_style/pass_no_mod/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "pass" +version = "0.1.0" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/tests/ui-cargo/module_style/pass_no_mod/src/good.rs b/tests/ui-cargo/module_style/pass_no_mod/src/good.rs new file mode 100644 index 0000000000000..f19ab10d5fb06 --- /dev/null +++ b/tests/ui-cargo/module_style/pass_no_mod/src/good.rs @@ -0,0 +1 @@ +pub struct Thing; diff --git a/tests/ui-cargo/module_style/pass_no_mod/src/main.rs b/tests/ui-cargo/module_style/pass_no_mod/src/main.rs new file mode 100644 index 0000000000000..50211a340b91b --- /dev/null +++ b/tests/ui-cargo/module_style/pass_no_mod/src/main.rs @@ -0,0 +1,7 @@ +#![warn(clippy::mod_module_files)] + +mod good; + +fn main() { + let _ = good::Thing; +} diff --git a/tests/ui/approx_const.rs b/tests/ui/approx_const.rs index fb57a0becbb25..2ae4d613507ed 100644 --- a/tests/ui/approx_const.rs +++ b/tests/ui/approx_const.rs @@ -57,4 +57,8 @@ fn main() { let my_sq2 = 1.4142; let no_sq2 = 1.414; + + let my_tau = 6.2832; + let almost_tau = 6.28; + let no_tau = 6.3; } diff --git a/tests/ui/approx_const.stderr b/tests/ui/approx_const.stderr index 98b85443f0b70..4da1b8215ae04 100644 --- a/tests/ui/approx_const.stderr +++ b/tests/ui/approx_const.stderr @@ -1,130 +1,187 @@ -error: approximate value of `f{32, 64}::consts::E` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::E` found --> $DIR/approx_const.rs:4:16 | LL | let my_e = 2.7182; | ^^^^^^ | = note: `-D clippy::approx-constant` implied by `-D warnings` + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::E` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::E` found --> $DIR/approx_const.rs:5:20 | LL | let almost_e = 2.718; | ^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_1_PI` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_1_PI` found --> $DIR/approx_const.rs:8:24 | LL | let my_1_frac_pi = 0.3183; | ^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found --> $DIR/approx_const.rs:11:28 | LL | let my_frac_1_sqrt_2 = 0.70710678; | ^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_1_SQRT_2` found --> $DIR/approx_const.rs:12:32 | LL | let almost_frac_1_sqrt_2 = 0.70711; | ^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_2_PI` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_2_PI` found --> $DIR/approx_const.rs:15:24 | LL | let my_frac_2_pi = 0.63661977; | ^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_2_SQRT_PI` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_2_SQRT_PI` found --> $DIR/approx_const.rs:18:27 | LL | let my_frac_2_sq_pi = 1.128379; | ^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_PI_2` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_PI_2` found --> $DIR/approx_const.rs:21:24 | LL | let my_frac_pi_2 = 1.57079632679; | ^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_PI_3` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_PI_3` found --> $DIR/approx_const.rs:24:24 | LL | let my_frac_pi_3 = 1.04719755119; | ^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_PI_4` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_PI_4` found --> $DIR/approx_const.rs:27:24 | LL | let my_frac_pi_4 = 0.785398163397; | ^^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_PI_6` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_PI_6` found --> $DIR/approx_const.rs:30:24 | LL | let my_frac_pi_6 = 0.523598775598; | ^^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::FRAC_PI_8` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::FRAC_PI_8` found --> $DIR/approx_const.rs:33:24 | LL | let my_frac_pi_8 = 0.3926990816987; | ^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::LN_10` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::LN_10` found --> $DIR/approx_const.rs:36:20 | LL | let my_ln_10 = 2.302585092994046; | ^^^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::LN_2` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::LN_2` found --> $DIR/approx_const.rs:39:19 | LL | let my_ln_2 = 0.6931471805599453; | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::LOG10_E` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::LOG10_E` found --> $DIR/approx_const.rs:42:22 | LL | let my_log10_e = 0.4342944819032518; | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::LOG2_E` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::LOG2_E` found --> $DIR/approx_const.rs:45:21 | LL | let my_log2_e = 1.4426950408889634; | ^^^^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::LOG2_10` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::LOG2_10` found --> $DIR/approx_const.rs:48:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::LOG10_2` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::LOG10_2` found --> $DIR/approx_const.rs:51:19 | LL | let log10_2 = 0.301029995663981; | ^^^^^^^^^^^^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::PI` found --> $DIR/approx_const.rs:54:17 | LL | let my_pi = 3.1415; | ^^^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::PI` found --> $DIR/approx_const.rs:55:21 | LL | let almost_pi = 3.14; | ^^^^ + | + = help: consider using the constant directly -error: approximate value of `f{32, 64}::consts::SQRT_2` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::SQRT_2` found --> $DIR/approx_const.rs:58:18 | LL | let my_sq2 = 1.4142; | ^^^^^^ + | + = help: consider using the constant directly + +error: approximate value of `f{32, 64}::consts::TAU` found + --> $DIR/approx_const.rs:61:18 + | +LL | let my_tau = 6.2832; + | ^^^^^^ + | + = help: consider using the constant directly + +error: approximate value of `f{32, 64}::consts::TAU` found + --> $DIR/approx_const.rs:62:22 + | +LL | let almost_tau = 6.28; + | ^^^^ + | + = help: consider using the constant directly -error: aborting due to 21 previous errors +error: aborting due to 23 previous errors diff --git a/tests/ui/auxiliary/option_helpers.rs b/tests/ui/auxiliary/option_helpers.rs index 7dc3f4ebd4d46..86a637ce3093c 100644 --- a/tests/ui/auxiliary/option_helpers.rs +++ b/tests/ui/auxiliary/option_helpers.rs @@ -53,3 +53,12 @@ impl IteratorFalsePositives { self.foo as usize } } + +#[derive(Copy, Clone)] +pub struct IteratorMethodFalsePositives; + +impl IteratorMethodFalsePositives { + pub fn filter(&self, _s: i32) -> std::vec::IntoIter { + unimplemented!(); + } +} diff --git a/tests/ui/bool_assert_comparison.rs b/tests/ui/bool_assert_comparison.rs index 2de402fae8c7d..ec4d6f3ff8401 100644 --- a/tests/ui/bool_assert_comparison.rs +++ b/tests/ui/bool_assert_comparison.rs @@ -1,5 +1,7 @@ #![warn(clippy::bool_assert_comparison)] +use std::ops::Not; + macro_rules! a { () => { true @@ -11,7 +13,58 @@ macro_rules! b { }; } +// Implements the Not trait but with an output type +// that's not bool. Should not suggest a rewrite +#[derive(Debug)] +enum ImplNotTraitWithoutBool { + VariantX(bool), + VariantY(u32), +} + +impl PartialEq for ImplNotTraitWithoutBool { + fn eq(&self, other: &bool) -> bool { + match *self { + ImplNotTraitWithoutBool::VariantX(b) => b == *other, + _ => false, + } + } +} + +impl Not for ImplNotTraitWithoutBool { + type Output = Self; + + fn not(self) -> Self::Output { + match self { + ImplNotTraitWithoutBool::VariantX(b) => ImplNotTraitWithoutBool::VariantX(!b), + ImplNotTraitWithoutBool::VariantY(0) => ImplNotTraitWithoutBool::VariantY(1), + ImplNotTraitWithoutBool::VariantY(_) => ImplNotTraitWithoutBool::VariantY(0), + } + } +} + +// This type implements the Not trait with an Output of +// type bool. Using assert!(..) must be suggested +#[derive(Debug)] +struct ImplNotTraitWithBool; + +impl PartialEq for ImplNotTraitWithBool { + fn eq(&self, other: &bool) -> bool { + false + } +} + +impl Not for ImplNotTraitWithBool { + type Output = bool; + + fn not(self) -> Self::Output { + true + } +} + fn main() { + let a = ImplNotTraitWithoutBool::VariantX(true); + let b = ImplNotTraitWithBool; + assert_eq!("a".len(), 1); assert_eq!("a".is_empty(), false); assert_eq!("".is_empty(), true); @@ -19,6 +72,8 @@ fn main() { assert_eq!(a!(), b!()); assert_eq!(a!(), "".is_empty()); assert_eq!("".is_empty(), b!()); + assert_eq!(a, true); + assert_eq!(b, true); assert_ne!("a".len(), 1); assert_ne!("a".is_empty(), false); @@ -27,6 +82,8 @@ fn main() { assert_ne!(a!(), b!()); assert_ne!(a!(), "".is_empty()); assert_ne!("".is_empty(), b!()); + assert_ne!(a, true); + assert_ne!(b, true); debug_assert_eq!("a".len(), 1); debug_assert_eq!("a".is_empty(), false); @@ -35,6 +92,8 @@ fn main() { debug_assert_eq!(a!(), b!()); debug_assert_eq!(a!(), "".is_empty()); debug_assert_eq!("".is_empty(), b!()); + debug_assert_eq!(a, true); + debug_assert_eq!(b, true); debug_assert_ne!("a".len(), 1); debug_assert_ne!("a".is_empty(), false); @@ -43,6 +102,8 @@ fn main() { debug_assert_ne!(a!(), b!()); debug_assert_ne!(a!(), "".is_empty()); debug_assert_ne!("".is_empty(), b!()); + debug_assert_ne!(a, true); + debug_assert_ne!(b, true); // assert with error messages assert_eq!("a".len(), 1, "tadam {}", 1); @@ -50,10 +111,12 @@ fn main() { assert_eq!("a".is_empty(), false, "tadam {}", 1); assert_eq!("a".is_empty(), false, "tadam {}", true); assert_eq!(false, "a".is_empty(), "tadam {}", true); + assert_eq!(a, true, "tadam {}", false); debug_assert_eq!("a".len(), 1, "tadam {}", 1); debug_assert_eq!("a".len(), 1, "tadam {}", true); debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); debug_assert_eq!("a".is_empty(), false, "tadam {}", true); debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); + debug_assert_eq!(a, true, "tadam {}", false); } diff --git a/tests/ui/bool_assert_comparison.stderr b/tests/ui/bool_assert_comparison.stderr index f57acf520d5f1..da9b56aa7795d 100644 --- a/tests/ui/bool_assert_comparison.stderr +++ b/tests/ui/bool_assert_comparison.stderr @@ -1,5 +1,5 @@ error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:16:5 + --> $DIR/bool_assert_comparison.rs:69:5 | LL | assert_eq!("a".is_empty(), false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` @@ -7,106 +7,130 @@ LL | assert_eq!("a".is_empty(), false); = note: `-D clippy::bool-assert-comparison` implied by `-D warnings` error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:17:5 + --> $DIR/bool_assert_comparison.rs:70:5 | LL | assert_eq!("".is_empty(), true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:18:5 + --> $DIR/bool_assert_comparison.rs:71:5 | LL | assert_eq!(true, "".is_empty()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` +error: used `assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:76:5 + | +LL | assert_eq!(b, true); + | ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + error: used `assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:24:5 + --> $DIR/bool_assert_comparison.rs:79:5 | LL | assert_ne!("a".is_empty(), false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:25:5 + --> $DIR/bool_assert_comparison.rs:80:5 | LL | assert_ne!("".is_empty(), true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:26:5 + --> $DIR/bool_assert_comparison.rs:81:5 | LL | assert_ne!(true, "".is_empty()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` +error: used `assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:86:5 + | +LL | assert_ne!(b, true); + | ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` + error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:32:5 + --> $DIR/bool_assert_comparison.rs:89:5 | LL | debug_assert_eq!("a".is_empty(), false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:33:5 + --> $DIR/bool_assert_comparison.rs:90:5 | LL | debug_assert_eq!("".is_empty(), true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:34:5 + --> $DIR/bool_assert_comparison.rs:91:5 | LL | debug_assert_eq!(true, "".is_empty()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` +error: used `debug_assert_eq!` with a literal bool + --> $DIR/bool_assert_comparison.rs:96:5 + | +LL | debug_assert_eq!(b, true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + error: used `debug_assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:40:5 + --> $DIR/bool_assert_comparison.rs:99:5 | LL | debug_assert_ne!("a".is_empty(), false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:41:5 + --> $DIR/bool_assert_comparison.rs:100:5 | LL | debug_assert_ne!("".is_empty(), true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_ne!` with a literal bool - --> $DIR/bool_assert_comparison.rs:42:5 + --> $DIR/bool_assert_comparison.rs:101:5 | LL | debug_assert_ne!(true, "".is_empty()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` +error: used `debug_assert_ne!` with a literal bool + --> $DIR/bool_assert_comparison.rs:106:5 + | +LL | debug_assert_ne!(b, true); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` + error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:50:5 + --> $DIR/bool_assert_comparison.rs:111:5 | LL | assert_eq!("a".is_empty(), false, "tadam {}", 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:51:5 + --> $DIR/bool_assert_comparison.rs:112:5 | LL | assert_eq!("a".is_empty(), false, "tadam {}", true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:52:5 + --> $DIR/bool_assert_comparison.rs:113:5 | LL | assert_eq!(false, "a".is_empty(), "tadam {}", true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)` error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:56:5 + --> $DIR/bool_assert_comparison.rs:118:5 | LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:57:5 + --> $DIR/bool_assert_comparison.rs:119:5 | LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` error: used `debug_assert_eq!` with a literal bool - --> $DIR/bool_assert_comparison.rs:58:5 + --> $DIR/bool_assert_comparison.rs:120:5 | LL | debug_assert_eq!(false, "a".is_empty(), "tadam {}", true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)` -error: aborting due to 18 previous errors +error: aborting due to 22 previous errors diff --git a/tests/ui/box_vec.rs b/tests/ui/box_vec.rs index 87b67c23704c9..1d6366972dacf 100644 --- a/tests/ui/box_vec.rs +++ b/tests/ui/box_vec.rs @@ -1,6 +1,10 @@ #![warn(clippy::all)] -#![allow(clippy::boxed_local, clippy::needless_pass_by_value)] -#![allow(clippy::blacklisted_name)] +#![allow( + clippy::boxed_local, + clippy::needless_pass_by_value, + clippy::blacklisted_name, + unused +)] macro_rules! boxit { ($init:expr, $x:ty) => { @@ -11,22 +15,22 @@ macro_rules! boxit { fn test_macro() { boxit!(Vec::new(), Vec); } -pub fn test(foo: Box>) { - println!("{:?}", foo.get(0)) -} +fn test(foo: Box>) {} -pub fn test2(foo: Box)>) { +fn test2(foo: Box)>) { // pass if #31 is fixed foo(vec![1, 2, 3]) } -pub fn test_local_not_linted() { +fn test_local_not_linted() { let _: Box>; } -fn main() { - test(Box::new(Vec::new())); - test2(Box::new(|v| println!("{:?}", v))); - test_macro(); - test_local_not_linted(); +// All of these test should be allowed because they are part of the +// public api and `avoid_breaking_exported_api` is `false` by default. +pub fn pub_test(foo: Box>) {} +pub fn pub_test_ret() -> Box> { + Box::new(Vec::new()) } + +fn main() {} diff --git a/tests/ui/box_vec.stderr b/tests/ui/box_vec.stderr index 9b789334baeec..58c1f13fb877b 100644 --- a/tests/ui/box_vec.stderr +++ b/tests/ui/box_vec.stderr @@ -1,8 +1,8 @@ error: you seem to be trying to use `Box>`. Consider using just `Vec` - --> $DIR/box_vec.rs:14:18 + --> $DIR/box_vec.rs:18:14 | -LL | pub fn test(foo: Box>) { - | ^^^^^^^^^^^^^^ +LL | fn test(foo: Box>) {} + | ^^^^^^^^^^^^^^ | = note: `-D clippy::box-vec` implied by `-D warnings` = help: `Vec` is already on the heap, `Box>` makes an extra allocation diff --git a/tests/ui/checked_unwrap/complex_conditionals.stderr b/tests/ui/checked_unwrap/complex_conditionals.stderr index 33bb5136ef8e7..46c6f69708eb7 100644 --- a/tests/ui/checked_unwrap/complex_conditionals.stderr +++ b/tests/ui/checked_unwrap/complex_conditionals.stderr @@ -1,4 +1,4 @@ -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap` on `x` after checking its variant with `is_ok` --> $DIR/complex_conditionals.rs:8:9 | LL | if x.is_ok() && y.is_err() { @@ -11,6 +11,7 @@ note: the lint level is defined here | LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: try using `if let` or `match` error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:9:9 @@ -36,7 +37,7 @@ LL | if x.is_ok() && y.is_err() { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap_err` on `y` after checking its variant with `is_err` --> $DIR/complex_conditionals.rs:11:9 | LL | if x.is_ok() && y.is_err() { @@ -44,6 +45,8 @@ LL | if x.is_ok() && y.is_err() { ... LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ + | + = help: try using `if let` or `match` error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:25:9 @@ -54,7 +57,7 @@ LL | if x.is_ok() || y.is_ok() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap_err` on `x` after checking its variant with `is_ok` --> $DIR/complex_conditionals.rs:26:9 | LL | if x.is_ok() || y.is_ok() { @@ -62,6 +65,8 @@ LL | if x.is_ok() || y.is_ok() { ... LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ + | + = help: try using `if let` or `match` error: this call to `unwrap()` will always panic --> $DIR/complex_conditionals.rs:27:9 @@ -72,7 +77,7 @@ LL | if x.is_ok() || y.is_ok() { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap_err` on `y` after checking its variant with `is_ok` --> $DIR/complex_conditionals.rs:28:9 | LL | if x.is_ok() || y.is_ok() { @@ -80,14 +85,18 @@ LL | if x.is_ok() || y.is_ok() { ... LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ + | + = help: try using `if let` or `match` -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap` on `x` after checking its variant with `is_ok` --> $DIR/complex_conditionals.rs:32:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { | --------- the check is happening here LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ + | + = help: try using `if let` or `match` error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:33:9 @@ -107,7 +116,7 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { LL | y.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap_err` on `y` after checking its variant with `is_ok` --> $DIR/complex_conditionals.rs:35:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -115,8 +124,10 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { ... LL | y.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ + | + = help: try using `if let` or `match` -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap` on `z` after checking its variant with `is_err` --> $DIR/complex_conditionals.rs:36:9 | LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { @@ -124,6 +135,8 @@ LL | if x.is_ok() && !(y.is_ok() || z.is_err()) { ... LL | z.unwrap(); // unnecessary | ^^^^^^^^^^ + | + = help: try using `if let` or `match` error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:37:9 @@ -143,7 +156,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap_err` on `x` after checking its variant with `is_ok` --> $DIR/complex_conditionals.rs:46:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -151,8 +164,10 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { ... LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ + | + = help: try using `if let` or `match` -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap` on `y` after checking its variant with `is_ok` --> $DIR/complex_conditionals.rs:47:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -160,6 +175,8 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { ... LL | y.unwrap(); // unnecessary | ^^^^^^^^^^ + | + = help: try using `if let` or `match` error: this call to `unwrap_err()` will always panic --> $DIR/complex_conditionals.rs:48:9 @@ -179,7 +196,7 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { LL | z.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap_err` on `z` after checking its variant with `is_err` --> $DIR/complex_conditionals.rs:50:9 | LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { @@ -187,6 +204,8 @@ LL | if x.is_ok() || !(y.is_ok() && z.is_err()) { ... LL | z.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ + | + = help: try using `if let` or `match` error: aborting due to 20 previous errors diff --git a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr index a01f7f956f629..542ab53300c02 100644 --- a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr +++ b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr @@ -1,8 +1,8 @@ -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap` on `x` after checking its variant with `is_some` --> $DIR/complex_conditionals_nested.rs:8:13 | LL | if x.is_some() { - | ----------- the check is happening here + | -------------- help: try: `if let Some(..) = x` LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ | diff --git a/tests/ui/checked_unwrap/simple_conditionals.rs b/tests/ui/checked_unwrap/simple_conditionals.rs index 8f23fb28827a2..ee3fdfabe9d8a 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.rs +++ b/tests/ui/checked_unwrap/simple_conditionals.rs @@ -37,8 +37,10 @@ fn main() { let x = Some(()); if x.is_some() { x.unwrap(); // unnecessary + x.expect("an error message"); // unnecessary } else { x.unwrap(); // will panic + x.expect("an error message"); // will panic } if x.is_none() { x.unwrap(); // will panic @@ -52,9 +54,11 @@ fn main() { let mut x: Result<(), ()> = Ok(()); if x.is_ok() { x.unwrap(); // unnecessary + x.expect("an error message"); // unnecessary x.unwrap_err(); // will panic } else { x.unwrap(); // will panic + x.expect("an error message"); // will panic x.unwrap_err(); // unnecessary } if x.is_err() { diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr index a4bc058fe2020..82f269543800f 100644 --- a/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -1,8 +1,8 @@ -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap` on `x` after checking its variant with `is_some` --> $DIR/simple_conditionals.rs:39:9 | LL | if x.is_some() { - | ----------- the check is happening here + | -------------- help: try: `if let Some(..) = x` LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ | @@ -12,8 +12,17 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +error: called `expect` on `x` after checking its variant with `is_some` + --> $DIR/simple_conditionals.rs:40:9 + | +LL | if x.is_some() { + | -------------- help: try: `if let Some(..) = x` +LL | x.unwrap(); // unnecessary +LL | x.expect("an error message"); // unnecessary + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: this call to `unwrap()` will always panic - --> $DIR/simple_conditionals.rs:41:9 + --> $DIR/simple_conditionals.rs:42:9 | LL | if x.is_some() { | ----------- because of this check @@ -27,28 +36,37 @@ note: the lint level is defined here LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)] | ^^^^^^^^^^^^^^^^^^^^^^^^ +error: this call to `expect()` will always panic + --> $DIR/simple_conditionals.rs:43:9 + | +LL | if x.is_some() { + | ----------- because of this check +... +LL | x.expect("an error message"); // will panic + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: this call to `unwrap()` will always panic - --> $DIR/simple_conditionals.rs:44:9 + --> $DIR/simple_conditionals.rs:46:9 | LL | if x.is_none() { | ----------- because of this check LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` - --> $DIR/simple_conditionals.rs:46:9 +error: called `unwrap` on `x` after checking its variant with `is_none` + --> $DIR/simple_conditionals.rs:48:9 | LL | if x.is_none() { - | ----------- the check is happening here + | -------------- help: try: `if let Some(..) = x` ... LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` +error: called `unwrap` on `x` after checking its variant with `is_some` --> $DIR/simple_conditionals.rs:7:13 | LL | if $a.is_some() { - | ------------ the check is happening here + | --------------- help: try: `if let Some(..) = x` LL | $a.unwrap(); // unnecessary | ^^^^^^^^^^^ ... @@ -57,25 +75,34 @@ LL | m!(x); | = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` - --> $DIR/simple_conditionals.rs:54:9 +error: called `unwrap` on `x` after checking its variant with `is_ok` + --> $DIR/simple_conditionals.rs:56:9 | LL | if x.is_ok() { - | --------- the check is happening here + | ------------ help: try: `if let Ok(..) = x` LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ +error: called `expect` on `x` after checking its variant with `is_ok` + --> $DIR/simple_conditionals.rs:57:9 + | +LL | if x.is_ok() { + | ------------ help: try: `if let Ok(..) = x` +LL | x.unwrap(); // unnecessary +LL | x.expect("an error message"); // unnecessary + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: this call to `unwrap_err()` will always panic - --> $DIR/simple_conditionals.rs:55:9 + --> $DIR/simple_conditionals.rs:58:9 | LL | if x.is_ok() { | --------- because of this check -LL | x.unwrap(); // unnecessary +... LL | x.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ error: this call to `unwrap()` will always panic - --> $DIR/simple_conditionals.rs:57:9 + --> $DIR/simple_conditionals.rs:60:9 | LL | if x.is_ok() { | --------- because of this check @@ -83,43 +110,52 @@ LL | if x.is_ok() { LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` - --> $DIR/simple_conditionals.rs:58:9 +error: this call to `expect()` will always panic + --> $DIR/simple_conditionals.rs:61:9 + | +LL | if x.is_ok() { + | --------- because of this check +... +LL | x.expect("an error message"); // will panic + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: called `unwrap_err` on `x` after checking its variant with `is_ok` + --> $DIR/simple_conditionals.rs:62:9 | LL | if x.is_ok() { - | --------- the check is happening here + | ------------ help: try: `if let Err(..) = x` ... LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ error: this call to `unwrap()` will always panic - --> $DIR/simple_conditionals.rs:61:9 + --> $DIR/simple_conditionals.rs:65:9 | LL | if x.is_err() { | ---------- because of this check LL | x.unwrap(); // will panic | ^^^^^^^^^^ -error: you checked before that `unwrap_err()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` - --> $DIR/simple_conditionals.rs:62:9 +error: called `unwrap_err` on `x` after checking its variant with `is_err` + --> $DIR/simple_conditionals.rs:66:9 | LL | if x.is_err() { - | ---------- the check is happening here + | ------------- help: try: `if let Err(..) = x` LL | x.unwrap(); // will panic LL | x.unwrap_err(); // unnecessary | ^^^^^^^^^^^^^^ -error: you checked before that `unwrap()` cannot fail, instead of checking and unwrapping, it's better to use `if let` or `match` - --> $DIR/simple_conditionals.rs:64:9 +error: called `unwrap` on `x` after checking its variant with `is_err` + --> $DIR/simple_conditionals.rs:68:9 | LL | if x.is_err() { - | ---------- the check is happening here + | ------------- help: try: `if let Ok(..) = x` ... LL | x.unwrap(); // unnecessary | ^^^^^^^^^^ error: this call to `unwrap_err()` will always panic - --> $DIR/simple_conditionals.rs:65:9 + --> $DIR/simple_conditionals.rs:69:9 | LL | if x.is_err() { | ---------- because of this check @@ -127,5 +163,5 @@ LL | if x.is_err() { LL | x.unwrap_err(); // will panic | ^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/derivable_impls.rs b/tests/ui/derivable_impls.rs new file mode 100644 index 0000000000000..336a743de726b --- /dev/null +++ b/tests/ui/derivable_impls.rs @@ -0,0 +1,170 @@ +use std::collections::HashMap; + +struct FooDefault<'a> { + a: bool, + b: i32, + c: u64, + d: Vec, + e: FooND1, + f: FooND2, + g: HashMap, + h: (i32, Vec), + i: [Vec; 3], + j: [i32; 5], + k: Option, + l: &'a [i32], +} + +impl std::default::Default for FooDefault<'_> { + fn default() -> Self { + Self { + a: false, + b: 0, + c: 0u64, + d: vec![], + e: Default::default(), + f: FooND2::default(), + g: HashMap::new(), + h: (0, vec![]), + i: [vec![], vec![], vec![]], + j: [0; 5], + k: None, + l: &[], + } + } +} + +struct TupleDefault(bool, i32, u64); + +impl std::default::Default for TupleDefault { + fn default() -> Self { + Self(false, 0, 0u64) + } +} + +struct FooND1 { + a: bool, +} + +impl std::default::Default for FooND1 { + fn default() -> Self { + Self { a: true } + } +} + +struct FooND2 { + a: i32, +} + +impl std::default::Default for FooND2 { + fn default() -> Self { + Self { a: 5 } + } +} + +struct FooNDNew { + a: bool, +} + +impl FooNDNew { + fn new() -> Self { + Self { a: true } + } +} + +impl Default for FooNDNew { + fn default() -> Self { + Self::new() + } +} + +struct FooNDVec(Vec); + +impl Default for FooNDVec { + fn default() -> Self { + Self(vec![5, 12]) + } +} + +struct StrDefault<'a>(&'a str); + +impl Default for StrDefault<'_> { + fn default() -> Self { + Self("") + } +} + +#[derive(Default)] +struct AlreadyDerived(i32, bool); + +macro_rules! mac { + () => { + 0 + }; + ($e:expr) => { + struct X(u32); + impl Default for X { + fn default() -> Self { + Self($e) + } + } + }; +} + +mac!(0); + +struct Y(u32); +impl Default for Y { + fn default() -> Self { + Self(mac!()) + } +} + +struct RustIssue26925 { + a: Option, +} + +// We should watch out for cases where a manual impl is needed because a +// derive adds different type bounds (https://github.com/rust-lang/rust/issues/26925). +// For example, a struct with Option does not require T: Default, but a derive adds +// that type bound anyways. So until #26925 get fixed we should disable lint +// for the following case +impl Default for RustIssue26925 { + fn default() -> Self { + Self { a: None } + } +} + +struct SpecializedImpl { + a: A, + b: B, +} + +impl Default for SpecializedImpl { + fn default() -> Self { + Self { + a: T::default(), + b: T::default(), + } + } +} + +struct WithoutSelfCurly { + a: bool, +} + +impl Default for WithoutSelfCurly { + fn default() -> Self { + WithoutSelfCurly { a: false } + } +} + +struct WithoutSelfParan(bool); + +impl Default for WithoutSelfParan { + fn default() -> Self { + WithoutSelfParan(false) + } +} + +fn main() {} diff --git a/tests/ui/derivable_impls.stderr b/tests/ui/derivable_impls.stderr new file mode 100644 index 0000000000000..4ed64fade026d --- /dev/null +++ b/tests/ui/derivable_impls.stderr @@ -0,0 +1,77 @@ +error: this `impl` can be derived + --> $DIR/derivable_impls.rs:18:1 + | +LL | / impl std::default::Default for FooDefault<'_> { +LL | | fn default() -> Self { +LL | | Self { +LL | | a: false, +... | +LL | | } +LL | | } + | |_^ + | + = note: `-D clippy::derivable-impls` implied by `-D warnings` + = help: try annotating `FooDefault` with `#[derive(Default)]` + +error: this `impl` can be derived + --> $DIR/derivable_impls.rs:39:1 + | +LL | / impl std::default::Default for TupleDefault { +LL | | fn default() -> Self { +LL | | Self(false, 0, 0u64) +LL | | } +LL | | } + | |_^ + | + = help: try annotating `TupleDefault` with `#[derive(Default)]` + +error: this `impl` can be derived + --> $DIR/derivable_impls.rs:91:1 + | +LL | / impl Default for StrDefault<'_> { +LL | | fn default() -> Self { +LL | | Self("") +LL | | } +LL | | } + | |_^ + | + = help: try annotating `StrDefault` with `#[derive(Default)]` + +error: this `impl` can be derived + --> $DIR/derivable_impls.rs:117:1 + | +LL | / impl Default for Y { +LL | | fn default() -> Self { +LL | | Self(mac!()) +LL | | } +LL | | } + | |_^ + | + = help: try annotating `Y` with `#[derive(Default)]` + +error: this `impl` can be derived + --> $DIR/derivable_impls.rs:156:1 + | +LL | / impl Default for WithoutSelfCurly { +LL | | fn default() -> Self { +LL | | WithoutSelfCurly { a: false } +LL | | } +LL | | } + | |_^ + | + = help: try annotating `WithoutSelfCurly` with `#[derive(Default)]` + +error: this `impl` can be derived + --> $DIR/derivable_impls.rs:164:1 + | +LL | / impl Default for WithoutSelfParan { +LL | | fn default() -> Self { +LL | | WithoutSelfParan(false) +LL | | } +LL | | } + | |_^ + | + = help: try annotating `WithoutSelfParan` with `#[derive(Default)]` + +error: aborting due to 6 previous errors + diff --git a/tests/ui/entry.fixed b/tests/ui/entry.fixed index cfad3090ba38d..8a36ec833d76d 100644 --- a/tests/ui/entry.fixed +++ b/tests/ui/entry.fixed @@ -4,7 +4,7 @@ #![warn(clippy::map_entry)] #![feature(asm)] -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use std::hash::Hash; macro_rules! m { @@ -142,14 +142,13 @@ fn hash_map(m: &mut HashMap, m2: &mut HashMa if !m.contains_key(&k) { insert!(m, k, v); } -} -fn btree_map(m: &mut BTreeMap, k: K, v: V, v2: V) { - // insert then do something, use if let - if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) { - e.insert(v); - foo(); - } + // or_insert_with. Partial move of a local declared in the closure is ok. + m.entry(k).or_insert_with(|| { + let x = (String::new(), String::new()); + let _ = x.0; + v + }); } fn main() {} diff --git a/tests/ui/entry.rs b/tests/ui/entry.rs index fa9280b58de11..d972a201ad764 100644 --- a/tests/ui/entry.rs +++ b/tests/ui/entry.rs @@ -4,7 +4,7 @@ #![warn(clippy::map_entry)] #![feature(asm)] -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use std::hash::Hash; macro_rules! m { @@ -146,13 +146,12 @@ fn hash_map(m: &mut HashMap, m2: &mut HashMa if !m.contains_key(&k) { insert!(m, k, v); } -} -fn btree_map(m: &mut BTreeMap, k: K, v: V, v2: V) { - // insert then do something, use if let + // or_insert_with. Partial move of a local declared in the closure is ok. if !m.contains_key(&k) { + let x = (String::new(), String::new()); + let _ = x.0; m.insert(k, v); - foo(); } } diff --git a/tests/ui/entry.stderr b/tests/ui/entry.stderr index 8f2e383d675da..1076500498d32 100644 --- a/tests/ui/entry.stderr +++ b/tests/ui/entry.stderr @@ -165,21 +165,23 @@ LL | | m.insert(m!(k), m!(v)); LL | | } | |_____^ help: try this: `m.entry(m!(k)).or_insert_with(|| m!(v));` -error: usage of `contains_key` followed by `insert` on a `BTreeMap` - --> $DIR/entry.rs:153:5 +error: usage of `contains_key` followed by `insert` on a `HashMap` + --> $DIR/entry.rs:151:5 | LL | / if !m.contains_key(&k) { +LL | | let x = (String::new(), String::new()); +LL | | let _ = x.0; LL | | m.insert(k, v); -LL | | foo(); LL | | } | |_____^ | help: try this | -LL ~ if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) { -LL + e.insert(v); -LL + foo(); -LL + } +LL ~ m.entry(k).or_insert_with(|| { +LL + let x = (String::new(), String::new()); +LL + let _ = x.0; +LL + v +LL + }); | error: aborting due to 10 previous errors diff --git a/tests/ui/entry_btree.fixed b/tests/ui/entry_btree.fixed new file mode 100644 index 0000000000000..94979104556bc --- /dev/null +++ b/tests/ui/entry_btree.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::map_entry)] +#![allow(dead_code)] + +use std::collections::BTreeMap; + +fn foo() {} + +fn btree_map(m: &mut BTreeMap, k: K, v: V) { + // insert then do something, use if let + if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) { + e.insert(v); + foo(); + } +} + +fn main() {} diff --git a/tests/ui/entry_btree.rs b/tests/ui/entry_btree.rs new file mode 100644 index 0000000000000..080c1d959e894 --- /dev/null +++ b/tests/ui/entry_btree.rs @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::map_entry)] +#![allow(dead_code)] + +use std::collections::BTreeMap; + +fn foo() {} + +fn btree_map(m: &mut BTreeMap, k: K, v: V) { + // insert then do something, use if let + if !m.contains_key(&k) { + m.insert(k, v); + foo(); + } +} + +fn main() {} diff --git a/tests/ui/entry_btree.stderr b/tests/ui/entry_btree.stderr new file mode 100644 index 0000000000000..5c6fcdf1a28c0 --- /dev/null +++ b/tests/ui/entry_btree.stderr @@ -0,0 +1,20 @@ +error: usage of `contains_key` followed by `insert` on a `BTreeMap` + --> $DIR/entry_btree.rs:12:5 + | +LL | / if !m.contains_key(&k) { +LL | | m.insert(k, v); +LL | | foo(); +LL | | } + | |_____^ + | + = note: `-D clippy::map-entry` implied by `-D warnings` +help: try this + | +LL ~ if let std::collections::btree_map::Entry::Vacant(e) = m.entry(k) { +LL + e.insert(v); +LL + foo(); +LL + } + | + +error: aborting due to previous error + diff --git a/tests/ui/linkedlist.rs b/tests/ui/linkedlist.rs index 2c3b25cd45e80..690ea810a6214 100644 --- a/tests/ui/linkedlist.rs +++ b/tests/ui/linkedlist.rs @@ -1,6 +1,6 @@ #![feature(associated_type_defaults)] #![warn(clippy::linkedlist)] -#![allow(dead_code, clippy::needless_pass_by_value)] +#![allow(unused, dead_code, clippy::needless_pass_by_value)] extern crate alloc; use alloc::collections::linked_list::LinkedList; @@ -20,24 +20,29 @@ impl Foo for LinkedList { const BAR: Option> = None; } -struct Bar; +pub struct Bar { + priv_linked_list_field: LinkedList, + pub pub_linked_list_field: LinkedList, +} impl Bar { fn foo(_: LinkedList) {} } -pub fn test(my_favourite_linked_list: LinkedList) { - println!("{:?}", my_favourite_linked_list) -} - -pub fn test_ret() -> Option> { - unimplemented!(); +// All of these test should be trigger the lint because they are not +// part of the public api +fn test(my_favorite_linked_list: LinkedList) {} +fn test_ret() -> Option> { + None } - -pub fn test_local_not_linted() { +fn test_local_not_linted() { let _: LinkedList; } -fn main() { - test(LinkedList::new()); - test_local_not_linted(); +// All of these test should be allowed because they are part of the +// public api and `avoid_breaking_exported_api` is `false` by default. +pub fn pub_test(the_most_awesome_linked_list: LinkedList) {} +pub fn pub_test_ret() -> Option> { + None } + +fn main() {} diff --git a/tests/ui/linkedlist.stderr b/tests/ui/linkedlist.stderr index 38ae71714d662..51327df132118 100644 --- a/tests/ui/linkedlist.stderr +++ b/tests/ui/linkedlist.stderr @@ -40,7 +40,15 @@ LL | const BAR: Option>; = help: a `VecDeque` might work error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/linkedlist.rs:25:15 + --> $DIR/linkedlist.rs:24:29 + | +LL | priv_linked_list_field: LinkedList, + | ^^^^^^^^^^^^^^ + | + = help: a `VecDeque` might work + +error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? + --> $DIR/linkedlist.rs:28:15 | LL | fn foo(_: LinkedList) {} | ^^^^^^^^^^^^^^ @@ -48,20 +56,20 @@ LL | fn foo(_: LinkedList) {} = help: a `VecDeque` might work error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/linkedlist.rs:28:39 + --> $DIR/linkedlist.rs:33:34 | -LL | pub fn test(my_favourite_linked_list: LinkedList) { - | ^^^^^^^^^^^^^^ +LL | fn test(my_favorite_linked_list: LinkedList) {} + | ^^^^^^^^^^^^^^ | = help: a `VecDeque` might work error: you seem to be using a `LinkedList`! Perhaps you meant some other data structure? - --> $DIR/linkedlist.rs:32:29 + --> $DIR/linkedlist.rs:34:25 | -LL | pub fn test_ret() -> Option> { - | ^^^^^^^^^^^^^^ +LL | fn test_ret() -> Option> { + | ^^^^^^^^^^^^^^ | = help: a `VecDeque` might work -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/manual_flatten.rs b/tests/ui/manual_flatten.rs index b5bd35a68785a..7db6b730963c9 100644 --- a/tests/ui/manual_flatten.rs +++ b/tests/ui/manual_flatten.rs @@ -91,6 +91,19 @@ fn main() { } } + struct Test { + a: usize, + } + + let mut vec_of_struct = [Some(Test { a: 1 }), None]; + + // Usage of `if let` expression should not trigger lint + for n in vec_of_struct.iter_mut() { + if let Some(z) = n { + *n = None; + } + } + // Using manual flatten should not trigger the lint for n in vec![Some(1), Some(2), Some(3)].iter().flatten() { println!("{}", n); diff --git a/tests/ui/manual_map_option_2.fixed b/tests/ui/manual_map_option_2.fixed new file mode 100644 index 0000000000000..8cc12149403d3 --- /dev/null +++ b/tests/ui/manual_map_option_2.fixed @@ -0,0 +1,50 @@ +// run-rustfix + +#![warn(clippy::manual_map)] +#![allow(clippy::toplevel_ref_arg)] + +fn main() { + // Lint. `y` is declared within the arm, so it isn't captured by the map closure + let _ = Some(0).map(|x| { + let y = (String::new(), String::new()); + (x, y.0) + }); + + // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map + // closure + let s = Some(String::new()); + let _ = match &s { + Some(x) => Some((x.clone(), s)), + None => None, + }; + + // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map + // closure + let s = Some(String::new()); + let _ = match &s { + Some(x) => Some({ + let clone = x.clone(); + let s = || s; + (clone, s()) + }), + None => None, + }; + + // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured as a mutable + // reference by the map closure + let mut s = Some(String::new()); + let _ = match &s { + Some(x) => Some({ + let clone = x.clone(); + let ref mut s = s; + (clone, s) + }), + None => None, + }; + + // Lint. `s` is captured by reference, so no lifetime issues. + let s = Some(String::new()); + let _ = s.as_ref().map(|x| { + if let Some(ref s) = s { (x.clone(), s) } else { panic!() } + }); +} diff --git a/tests/ui/manual_map_option_2.rs b/tests/ui/manual_map_option_2.rs new file mode 100644 index 0000000000000..0862b201ead4b --- /dev/null +++ b/tests/ui/manual_map_option_2.rs @@ -0,0 +1,56 @@ +// run-rustfix + +#![warn(clippy::manual_map)] +#![allow(clippy::toplevel_ref_arg)] + +fn main() { + // Lint. `y` is declared within the arm, so it isn't captured by the map closure + let _ = match Some(0) { + Some(x) => Some({ + let y = (String::new(), String::new()); + (x, y.0) + }), + None => None, + }; + + // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map + // closure + let s = Some(String::new()); + let _ = match &s { + Some(x) => Some((x.clone(), s)), + None => None, + }; + + // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured by the map + // closure + let s = Some(String::new()); + let _ = match &s { + Some(x) => Some({ + let clone = x.clone(); + let s = || s; + (clone, s()) + }), + None => None, + }; + + // Don't lint. `s` is borrowed until partway through the arm, but needs to be captured as a mutable + // reference by the map closure + let mut s = Some(String::new()); + let _ = match &s { + Some(x) => Some({ + let clone = x.clone(); + let ref mut s = s; + (clone, s) + }), + None => None, + }; + + // Lint. `s` is captured by reference, so no lifetime issues. + let s = Some(String::new()); + let _ = match &s { + Some(x) => Some({ + if let Some(ref s) = s { (x.clone(), s) } else { panic!() } + }), + None => None, + }; +} diff --git a/tests/ui/manual_map_option_2.stderr b/tests/ui/manual_map_option_2.stderr new file mode 100644 index 0000000000000..711ff6c4a4b09 --- /dev/null +++ b/tests/ui/manual_map_option_2.stderr @@ -0,0 +1,43 @@ +error: manual implementation of `Option::map` + --> $DIR/manual_map_option_2.rs:8:13 + | +LL | let _ = match Some(0) { + | _____________^ +LL | | Some(x) => Some({ +LL | | let y = (String::new(), String::new()); +LL | | (x, y.0) +LL | | }), +LL | | None => None, +LL | | }; + | |_____^ + | + = note: `-D clippy::manual-map` implied by `-D warnings` +help: try this + | +LL ~ let _ = Some(0).map(|x| { +LL + let y = (String::new(), String::new()); +LL + (x, y.0) +LL ~ }); + | + +error: manual implementation of `Option::map` + --> $DIR/manual_map_option_2.rs:50:13 + | +LL | let _ = match &s { + | _____________^ +LL | | Some(x) => Some({ +LL | | if let Some(ref s) = s { (x.clone(), s) } else { panic!() } +LL | | }), +LL | | None => None, +LL | | }; + | |_____^ + | +help: try this + | +LL ~ let _ = s.as_ref().map(|x| { +LL + if let Some(ref s) = s { (x.clone(), s) } else { panic!() } +LL ~ }); + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/manual_split_once.fixed b/tests/ui/manual_split_once.fixed new file mode 100644 index 0000000000000..3a0332939d409 --- /dev/null +++ b/tests/ui/manual_split_once.fixed @@ -0,0 +1,50 @@ +// run-rustfix + +#![feature(custom_inner_attributes)] +#![warn(clippy::manual_split_once)] +#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)] + +extern crate itertools; + +#[allow(unused_imports)] +use itertools::Itertools; + +fn main() { + let _ = Some("key=value".split_once('=').map_or("key=value", |x| x.0)); + let _ = "key=value".splitn(2, '=').nth(2); + let _ = "key=value".split_once('=').map_or("key=value", |x| x.0); + let _ = "key=value".split_once('=').map_or("key=value", |x| x.0); + let _ = "key=value".split_once('=').unwrap().1; + let _ = "key=value".split_once('=').unwrap().1; + let (_, _) = "key=value".split_once('=').unwrap(); + + let s = String::from("key=value"); + let _ = s.split_once('=').map_or(&*s, |x| x.0); + + let s = Box::::from("key=value"); + let _ = s.split_once('=').map_or(&*s, |x| x.0); + + let s = &"key=value"; + let _ = s.split_once('=').map_or(*s, |x| x.0); + + fn _f(s: &str) -> Option<&str> { + let _ = s.split_once("key=value").map_or(s, |x| x.0); + let _ = s.split_once("key=value")?.1; + let _ = s.split_once("key=value")?.1; + None + } + + // Don't lint, slices don't have `split_once` + let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap(); +} + +fn _msrv_1_51() { + #![clippy::msrv = "1.51"] + // `str::split_once` was stabilized in 1.16. Do not lint this + let _ = "key=value".splitn(2, '=').nth(1).unwrap(); +} + +fn _msrv_1_52() { + #![clippy::msrv = "1.52"] + let _ = "key=value".split_once('=').unwrap().1; +} diff --git a/tests/ui/manual_split_once.rs b/tests/ui/manual_split_once.rs new file mode 100644 index 0000000000000..e6093b63fe8d4 --- /dev/null +++ b/tests/ui/manual_split_once.rs @@ -0,0 +1,50 @@ +// run-rustfix + +#![feature(custom_inner_attributes)] +#![warn(clippy::manual_split_once)] +#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)] + +extern crate itertools; + +#[allow(unused_imports)] +use itertools::Itertools; + +fn main() { + let _ = "key=value".splitn(2, '=').next(); + let _ = "key=value".splitn(2, '=').nth(2); + let _ = "key=value".splitn(2, '=').next().unwrap(); + let _ = "key=value".splitn(2, '=').nth(0).unwrap(); + let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); + let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); + + let s = String::from("key=value"); + let _ = s.splitn(2, '=').next().unwrap(); + + let s = Box::::from("key=value"); + let _ = s.splitn(2, '=').nth(0).unwrap(); + + let s = &"key=value"; + let _ = s.splitn(2, '=').skip(0).next().unwrap(); + + fn _f(s: &str) -> Option<&str> { + let _ = s.splitn(2, "key=value").next()?; + let _ = s.splitn(2, "key=value").nth(1)?; + let _ = s.splitn(2, "key=value").skip(1).next()?; + None + } + + // Don't lint, slices don't have `split_once` + let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap(); +} + +fn _msrv_1_51() { + #![clippy::msrv = "1.51"] + // `str::split_once` was stabilized in 1.16. Do not lint this + let _ = "key=value".splitn(2, '=').nth(1).unwrap(); +} + +fn _msrv_1_52() { + #![clippy::msrv = "1.52"] + let _ = "key=value".splitn(2, '=').nth(1).unwrap(); +} diff --git a/tests/ui/manual_split_once.stderr b/tests/ui/manual_split_once.stderr new file mode 100644 index 0000000000000..4f15196b469e1 --- /dev/null +++ b/tests/ui/manual_split_once.stderr @@ -0,0 +1,82 @@ +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:13:13 + | +LL | let _ = "key=value".splitn(2, '=').next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some("key=value".split_once('=').map_or("key=value", |x| x.0))` + | + = note: `-D clippy::manual-split-once` implied by `-D warnings` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:15:13 + | +LL | let _ = "key=value".splitn(2, '=').next().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:16:13 + | +LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:17:13 + | +LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:18:13 + | +LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:19:18 + | +LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:22:13 + | +LL | let _ = s.splitn(2, '=').next().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:25:13 + | +LL | let _ = s.splitn(2, '=').nth(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:28:13 + | +LL | let _ = s.splitn(2, '=').skip(0).next().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(*s, |x| x.0)` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:31:17 + | +LL | let _ = s.splitn(2, "key=value").next()?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value").map_or(s, |x| x.0)` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:32:17 + | +LL | let _ = s.splitn(2, "key=value").nth(1)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:33:17 + | +LL | let _ = s.splitn(2, "key=value").skip(1).next()?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1` + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:49:13 + | +LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` + +error: aborting due to 13 previous errors + diff --git a/tests/ui/mem_replace.fixed b/tests/ui/mem_replace.fixed index 3b6224254a0a7..b609ba659467f 100644 --- a/tests/ui/mem_replace.fixed +++ b/tests/ui/mem_replace.fixed @@ -51,9 +51,29 @@ fn replace_with_default() { let mut binary_heap: BinaryHeap = BinaryHeap::new(); let _ = std::mem::take(&mut binary_heap); + + let mut tuple = (vec![1, 2], BinaryHeap::::new()); + let _ = std::mem::take(&mut tuple); + + let mut refstr = "hello"; + let _ = std::mem::take(&mut refstr); + + let mut slice: &[i32] = &[1, 2, 3]; + let _ = std::mem::take(&mut slice); +} + +// lint is disabled for primitives because in this case `take` +// has no clear benefit over `replace` and sometimes is harder to read +fn dont_lint_primitive() { + let mut pbool = true; + let _ = std::mem::replace(&mut pbool, false); + + let mut pint = 5; + let _ = std::mem::replace(&mut pint, 0); } fn main() { replace_option_with_none(); replace_with_default(); + dont_lint_primitive(); } diff --git a/tests/ui/mem_replace.rs b/tests/ui/mem_replace.rs index 0a36db9e92159..93f6dcdec83b9 100644 --- a/tests/ui/mem_replace.rs +++ b/tests/ui/mem_replace.rs @@ -51,9 +51,29 @@ fn replace_with_default() { let mut binary_heap: BinaryHeap = BinaryHeap::new(); let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); + + let mut tuple = (vec![1, 2], BinaryHeap::::new()); + let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new())); + + let mut refstr = "hello"; + let _ = std::mem::replace(&mut refstr, ""); + + let mut slice: &[i32] = &[1, 2, 3]; + let _ = std::mem::replace(&mut slice, &[]); +} + +// lint is disabled for primitives because in this case `take` +// has no clear benefit over `replace` and sometimes is harder to read +fn dont_lint_primitive() { + let mut pbool = true; + let _ = std::mem::replace(&mut pbool, false); + + let mut pint = 5; + let _ = std::mem::replace(&mut pint, 0); } fn main() { replace_option_with_none(); replace_with_default(); + dont_lint_primitive(); } diff --git a/tests/ui/mem_replace.stderr b/tests/ui/mem_replace.stderr index f8aa1538bffa8..90dc6c95f8582 100644 --- a/tests/ui/mem_replace.stderr +++ b/tests/ui/mem_replace.stderr @@ -98,5 +98,23 @@ error: replacing a value of type `T` with `T::default()` is better expressed usi LL | let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)` -error: aborting due to 16 previous errors +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:56:13 + | +LL | let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut tuple)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:59:13 + | +LL | let _ = std::mem::replace(&mut refstr, ""); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut refstr)` + +error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take` + --> $DIR/mem_replace.rs:62:13 + | +LL | let _ = std::mem::replace(&mut slice, &[]); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut slice)` + +error: aborting due to 19 previous errors diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index 513d930e05687..c441b35b99203 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -32,7 +32,7 @@ use std::ops::Mul; use std::rc::{self, Rc}; use std::sync::{self, Arc}; -use option_helpers::IteratorFalsePositives; +use option_helpers::{IteratorFalsePositives, IteratorMethodFalsePositives}; struct Lt<'a> { foo: &'a u32, @@ -131,6 +131,9 @@ fn filter_next() { // Check that we don't lint if the caller is not an `Iterator`. let foo = IteratorFalsePositives { foo: 0 }; let _ = foo.filter().next(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next(); } fn main() { diff --git a/tests/ui/min_rust_version_attr.rs b/tests/ui/min_rust_version_attr.rs index 7f9f7ddc53575..8d9fc5a864d75 100644 --- a/tests/ui/min_rust_version_attr.rs +++ b/tests/ui/min_rust_version_attr.rs @@ -4,6 +4,11 @@ use std::ops::{Deref, RangeFrom}; +fn approx_const() { + let log2_10 = 3.321928094887362; + let log10_2 = 0.301029995663981; +} + fn cloned_instead_of_copied() { let _ = [1].iter().cloned(); } diff --git a/tests/ui/min_rust_version_attr.stderr b/tests/ui/min_rust_version_attr.stderr index a2e4e86ed6b8c..360dcfb230c65 100644 --- a/tests/ui/min_rust_version_attr.stderr +++ b/tests/ui/min_rust_version_attr.stderr @@ -1,12 +1,12 @@ error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:160:24 + --> $DIR/min_rust_version_attr.rs:165:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::manual-strip` implied by `-D warnings` note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:159:9 + --> $DIR/min_rust_version_attr.rs:164:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -17,13 +17,13 @@ LL ~ assert_eq!(.to_uppercase(), "WORLD!"); | error: stripping a prefix manually - --> $DIR/min_rust_version_attr.rs:172:24 + --> $DIR/min_rust_version_attr.rs:177:24 | LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!"); | ^^^^^^^^^^^^^^^^^^^^ | note: the prefix was tested here - --> $DIR/min_rust_version_attr.rs:171:9 + --> $DIR/min_rust_version_attr.rs:176:9 | LL | if s.starts_with("hello, ") { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/missing-doc-crate.stderr b/tests/ui/missing-doc-crate.stderr deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/tests/ui/missing_const_for_fn/cant_be_const.stderr b/tests/ui/missing_const_for_fn/cant_be_const.stderr deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/tests/ui/mut_range_bound.rs b/tests/ui/mut_range_bound.rs index 1348dd2a3d8bb..e1ae1ef928223 100644 --- a/tests/ui/mut_range_bound.rs +++ b/tests/ui/mut_range_bound.rs @@ -1,14 +1,6 @@ #![allow(unused)] -fn main() { - mut_range_bound_upper(); - mut_range_bound_lower(); - mut_range_bound_both(); - mut_range_bound_no_mutation(); - immut_range_bound(); - mut_borrow_range_bound(); - immut_borrow_range_bound(); -} +fn main() {} fn mut_range_bound_upper() { let mut m = 4; @@ -61,3 +53,32 @@ fn immut_range_bound() { continue; } // no warning } + +fn mut_range_bound_break() { + let mut m = 4; + for i in 0..m { + if m == 4 { + m = 5; // no warning because of immediate break + break; + } + } +} + +fn mut_range_bound_no_immediate_break() { + let mut m = 4; + for i in 0..m { + m = 2; // warning because it is not immediately followed by break + if m == 4 { + break; + } + } + + let mut n = 3; + for i in n..10 { + if n == 4 { + n = 1; // FIXME: warning because is is not immediately followed by break + let _ = 2; + break; + } + } +} diff --git a/tests/ui/mut_range_bound.stderr b/tests/ui/mut_range_bound.stderr index 0eeb76e0ec5fd..4b5a3fc1e418c 100644 --- a/tests/ui/mut_range_bound.stderr +++ b/tests/ui/mut_range_bound.stderr @@ -1,34 +1,59 @@ -error: attempt to mutate range bound within loop; note that the range of the loop is unchanged - --> $DIR/mut_range_bound.rs:16:9 +error: attempt to mutate range bound within loop + --> $DIR/mut_range_bound.rs:8:9 | LL | m = 5; | ^ | = note: `-D clippy::mut-range-bound` implied by `-D warnings` + = note: the range of the loop is unchanged -error: attempt to mutate range bound within loop; note that the range of the loop is unchanged - --> $DIR/mut_range_bound.rs:23:9 +error: attempt to mutate range bound within loop + --> $DIR/mut_range_bound.rs:15:9 | LL | m *= 2; | ^ + | + = note: the range of the loop is unchanged -error: attempt to mutate range bound within loop; note that the range of the loop is unchanged - --> $DIR/mut_range_bound.rs:31:9 +error: attempt to mutate range bound within loop + --> $DIR/mut_range_bound.rs:23:9 | LL | m = 5; | ^ + | + = note: the range of the loop is unchanged -error: attempt to mutate range bound within loop; note that the range of the loop is unchanged - --> $DIR/mut_range_bound.rs:32:9 +error: attempt to mutate range bound within loop + --> $DIR/mut_range_bound.rs:24:9 | LL | n = 7; | ^ + | + = note: the range of the loop is unchanged -error: attempt to mutate range bound within loop; note that the range of the loop is unchanged - --> $DIR/mut_range_bound.rs:46:22 +error: attempt to mutate range bound within loop + --> $DIR/mut_range_bound.rs:38:22 | LL | let n = &mut m; // warning | ^ + | + = note: the range of the loop is unchanged + +error: attempt to mutate range bound within loop + --> $DIR/mut_range_bound.rs:70:9 + | +LL | m = 2; // warning because it is not immediately followed by break + | ^ + | + = note: the range of the loop is unchanged + +error: attempt to mutate range bound within loop + --> $DIR/mut_range_bound.rs:79:13 + | +LL | n = 1; // FIXME: warning because is is not immediately followed by break + | ^ + | + = note: the range of the loop is unchanged -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/needless_option_as_deref.fixed b/tests/ui/needless_option_as_deref.fixed new file mode 100644 index 0000000000000..d721452ae8880 --- /dev/null +++ b/tests/ui/needless_option_as_deref.fixed @@ -0,0 +1,13 @@ +// run-rustfix + +#[warn(clippy::needless_option_as_deref)] + +fn main() { + // should lint + let _: Option<&usize> = Some(&1); + let _: Option<&mut usize> = Some(&mut 1); + + // should not lint + let _ = Some(Box::new(1)).as_deref(); + let _ = Some(Box::new(1)).as_deref_mut(); +} diff --git a/tests/ui/needless_option_as_deref.rs b/tests/ui/needless_option_as_deref.rs new file mode 100644 index 0000000000000..bb15512adf6aa --- /dev/null +++ b/tests/ui/needless_option_as_deref.rs @@ -0,0 +1,13 @@ +// run-rustfix + +#[warn(clippy::needless_option_as_deref)] + +fn main() { + // should lint + let _: Option<&usize> = Some(&1).as_deref(); + let _: Option<&mut usize> = Some(&mut 1).as_deref_mut(); + + // should not lint + let _ = Some(Box::new(1)).as_deref(); + let _ = Some(Box::new(1)).as_deref_mut(); +} diff --git a/tests/ui/needless_option_as_deref.stderr b/tests/ui/needless_option_as_deref.stderr new file mode 100644 index 0000000000000..5dd507b4a71e8 --- /dev/null +++ b/tests/ui/needless_option_as_deref.stderr @@ -0,0 +1,16 @@ +error: derefed type is same as origin + --> $DIR/needless_option_as_deref.rs:7:29 + | +LL | let _: Option<&usize> = Some(&1).as_deref(); + | ^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&1)` + | + = note: `-D clippy::needless-option-as-deref` implied by `-D warnings` + +error: derefed type is same as origin + --> $DIR/needless_option_as_deref.rs:8:33 + | +LL | let _: Option<&mut usize> = Some(&mut 1).as_deref_mut(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&mut 1)` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 769ccc14bc1e4..d1815d0aec331 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -1,3 +1,4 @@ +// edition:2018 // run-rustfix #![warn(clippy::option_if_let_else)] #![allow(clippy::redundant_closure)] @@ -86,4 +87,65 @@ fn main() { test_map_or_else(None); let _ = negative_tests(None); let _ = impure_else(None); + + let _ = Some(0).map_or(0, |x| loop { + if x == 0 { + break x; + } + }); + + // #7576 + const fn _f(x: Option) -> u32 { + // Don't lint, `map_or` isn't const + if let Some(x) = x { x } else { 10 } + } + + // #5822 + let s = String::new(); + // Don't lint, `Some` branch consumes `s`, but else branch uses `s` + let _ = if let Some(x) = Some(0) { + let s = s; + s.len() + x + } else { + s.len() + }; + + let s = String::new(); + // Lint, both branches immutably borrow `s`. + let _ = Some(0).map_or_else(|| s.len(), |x| s.len() + x); + + let s = String::new(); + // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`. + let _ = Some(0).map_or(1, |x| { + let s = s; + s.len() + x + }); + + let s = Some(String::new()); + // Don't lint, `Some` branch borrows `s`, but else branch consumes `s` + let _ = if let Some(x) = &s { + x.len() + } else { + let _s = s; + 10 + }; + + let mut s = Some(String::new()); + // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows `s` + let _ = if let Some(x) = &mut s { + x.push_str("test"); + x.len() + } else { + let _s = &s; + 10 + }; + + async fn _f1(x: u32) -> u32 { + x + } + + async fn _f2() { + // Don't lint. `await` can't be moved into a closure. + let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 }; + } } diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index e2f8dec3b930c..a15627338cb4a 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -1,3 +1,4 @@ +// edition:2018 // run-rustfix #![warn(clippy::option_if_let_else)] #![allow(clippy::redundant_closure)] @@ -105,4 +106,71 @@ fn main() { test_map_or_else(None); let _ = negative_tests(None); let _ = impure_else(None); + + let _ = if let Some(x) = Some(0) { + loop { + if x == 0 { + break x; + } + } + } else { + 0 + }; + + // #7576 + const fn _f(x: Option) -> u32 { + // Don't lint, `map_or` isn't const + if let Some(x) = x { x } else { 10 } + } + + // #5822 + let s = String::new(); + // Don't lint, `Some` branch consumes `s`, but else branch uses `s` + let _ = if let Some(x) = Some(0) { + let s = s; + s.len() + x + } else { + s.len() + }; + + let s = String::new(); + // Lint, both branches immutably borrow `s`. + let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; + + let s = String::new(); + // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`. + let _ = if let Some(x) = Some(0) { + let s = s; + s.len() + x + } else { + 1 + }; + + let s = Some(String::new()); + // Don't lint, `Some` branch borrows `s`, but else branch consumes `s` + let _ = if let Some(x) = &s { + x.len() + } else { + let _s = s; + 10 + }; + + let mut s = Some(String::new()); + // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows `s` + let _ = if let Some(x) = &mut s { + x.push_str("test"); + x.len() + } else { + let _s = &s; + 10 + }; + + async fn _f1(x: u32) -> u32 { + x + } + + async fn _f2() { + // Don't lint. `await` can't be moved into a closure. + let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 }; + } } diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 099e49ef8e3dc..ed748ee8b39e4 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -1,5 +1,5 @@ error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:7:5 + --> $DIR/option_if_let_else.rs:8:5 | LL | / if let Some(x) = string { LL | | (true, x) @@ -11,19 +11,19 @@ LL | | } = note: `-D clippy::option-if-let-else` implied by `-D warnings` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:25:13 + --> $DIR/option_if_let_else.rs:26:13 | LL | let _ = if let Some(s) = *string { s.len() } else { 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:26:13 + --> $DIR/option_if_let_else.rs:27:13 | LL | let _ = if let Some(s) = &num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:27:13 + --> $DIR/option_if_let_else.rs:28:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -43,13 +43,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:33:13 + --> $DIR/option_if_let_else.rs:34:13 | LL | let _ = if let Some(ref s) = num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:34:13 + --> $DIR/option_if_let_else.rs:35:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -69,7 +69,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:40:13 + --> $DIR/option_if_let_else.rs:41:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -89,7 +89,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:49:5 + --> $DIR/option_if_let_else.rs:50:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -108,7 +108,7 @@ LL + }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:62:13 + --> $DIR/option_if_let_else.rs:63:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -120,7 +120,7 @@ LL | | }; | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)` error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:71:13 + --> $DIR/option_if_let_else.rs:72:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -143,10 +143,58 @@ LL ~ }, |x| x * x * x * x); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:100:13 + --> $DIR/option_if_let_else.rs:101:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` -error: aborting due to 11 previous errors +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:110:13 + | +LL | let _ = if let Some(x) = Some(0) { + | _____________^ +LL | | loop { +LL | | if x == 0 { +LL | | break x; +... | +LL | | 0 +LL | | }; + | |_____^ + | +help: try + | +LL ~ let _ = Some(0).map_or(0, |x| loop { +LL + if x == 0 { +LL + break x; +LL + } +LL ~ }); + | + +error: use Option::map_or_else instead of an if let/else + --> $DIR/option_if_let_else.rs:138:13 + | +LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or_else(|| s.len(), |x| s.len() + x)` + +error: use Option::map_or instead of an if let/else + --> $DIR/option_if_let_else.rs:142:13 + | +LL | let _ = if let Some(x) = Some(0) { + | _____________^ +LL | | let s = s; +LL | | s.len() + x +LL | | } else { +LL | | 1 +LL | | }; + | |_____^ + | +help: try + | +LL ~ let _ = Some(0).map_or(1, |x| { +LL + let s = s; +LL + s.len() + x +LL ~ }); + | + +error: aborting due to 14 previous errors diff --git a/tests/ui/proc_macro.stderr b/tests/ui/proc_macro.stderr index 872cbc66af622..48fd58c9a4933 100644 --- a/tests/ui/proc_macro.stderr +++ b/tests/ui/proc_macro.stderr @@ -1,10 +1,11 @@ -error: approximate value of `f{32, 64}::consts::PI` found. Consider using it directly +error: approximate value of `f{32, 64}::consts::PI` found --> $DIR/proc_macro.rs:10:14 | LL | let _x = 3.14; | ^^^^ | = note: `#[deny(clippy::approx_constant)]` on by default + = help: consider using the constant directly error: aborting due to previous error diff --git a/tests/ui/rc_buffer_redefined_string.stderr b/tests/ui/rc_buffer_redefined_string.stderr deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/tests/ui/rc_mutex.rs b/tests/ui/rc_mutex.rs index 657a3ecf6a006..18e8a2e01e022 100644 --- a/tests/ui/rc_mutex.rs +++ b/tests/ui/rc_mutex.rs @@ -1,13 +1,17 @@ #![warn(clippy::rc_mutex)] -#![allow(clippy::blacklisted_name)] +#![allow(unused, clippy::blacklisted_name)] use std::rc::Rc; use std::sync::Mutex; -pub struct MyStruct { +pub struct MyStructWithPrivItem { foo: Rc>, } +pub struct MyStructWithPubItem { + pub foo: Rc>, +} + pub struct SubT { foo: T, } @@ -17,18 +21,16 @@ pub enum MyEnum { Two, } -pub fn test1(foo: Rc>) {} - -pub fn test2(foo: Rc>) {} +// All of these test should be trigger the lint because they are not +// part of the public api +fn test1(foo: Rc>) {} +fn test2(foo: Rc>) {} +fn test3(foo: Rc>>) {} -pub fn test3(foo: Rc>>) {} +// All of these test should be allowed because they are part of the +// public api and `avoid_breaking_exported_api` is `false` by default. +pub fn pub_test1(foo: Rc>) {} +pub fn pub_test2(foo: Rc>) {} +pub fn pub_test3(foo: Rc>>) {} -fn main() { - test1(Rc::new(Mutex::new(1))); - test2(Rc::new(Mutex::new(MyEnum::One))); - test3(Rc::new(Mutex::new(SubT { foo: 1 }))); - - let _my_struct = MyStruct { - foo: Rc::new(Mutex::new(1)), - }; -} +fn main() {} diff --git a/tests/ui/rc_mutex.stderr b/tests/ui/rc_mutex.stderr index 8e58e2bc2d0bf..fe84361d78162 100644 --- a/tests/ui/rc_mutex.stderr +++ b/tests/ui/rc_mutex.stderr @@ -1,28 +1,35 @@ -error: found `Rc>`. Consider using `Rc>` or `Arc>` instead +error: usage of `Rc>` --> $DIR/rc_mutex.rs:8:10 | LL | foo: Rc>, | ^^^^^^^^^^^^^^ | = note: `-D clippy::rc-mutex` implied by `-D warnings` + = help: consider using `Rc>` or `Arc>` instead -error: found `Rc>`. Consider using `Rc>` or `Arc>` instead - --> $DIR/rc_mutex.rs:20:22 +error: usage of `Rc>` + --> $DIR/rc_mutex.rs:26:18 | -LL | pub fn test1(foo: Rc>) {} - | ^^^^^^^^^^^^ +LL | fn test1(foo: Rc>) {} + | ^^^^^^^^^^^^ + | + = help: consider using `Rc>` or `Arc>` instead -error: found `Rc>`. Consider using `Rc>` or `Arc>` instead - --> $DIR/rc_mutex.rs:22:19 +error: usage of `Rc>` + --> $DIR/rc_mutex.rs:27:15 + | +LL | fn test2(foo: Rc>) {} + | ^^^^^^^^^^^^^^^^^ | -LL | pub fn test2(foo: Rc>) {} - | ^^^^^^^^^^^^^^^^^ + = help: consider using `Rc>` or `Arc>` instead -error: found `Rc>`. Consider using `Rc>` or `Arc>` instead - --> $DIR/rc_mutex.rs:24:19 +error: usage of `Rc>` + --> $DIR/rc_mutex.rs:28:15 + | +LL | fn test3(foo: Rc>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^ | -LL | pub fn test3(foo: Rc>>) {} - | ^^^^^^^^^^^^^^^^^^^^^^ + = help: consider using `Rc>` or `Arc>` instead error: aborting due to 4 previous errors diff --git a/tests/ui/redundant_allocation.rs b/tests/ui/redundant_allocation.rs index 1b4f2a66c705e..52fbc91e32555 100644 --- a/tests/ui/redundant_allocation.rs +++ b/tests/ui/redundant_allocation.rs @@ -77,4 +77,24 @@ mod outer_arc { } } +// https://github.com/rust-lang/rust-clippy/issues/7487 +mod box_dyn { + use std::boxed::Box; + use std::rc::Rc; + use std::sync::Arc; + + pub trait T {} + + struct S { + a: Box>, + b: Rc>, + c: Arc>, + } + + pub fn test_box(_: Box>) {} + pub fn test_rc(_: Rc>) {} + pub fn test_arc(_: Arc>) {} + pub fn test_rc_box(_: Rc>>) {} +} + fn main() {} diff --git a/tests/ui/redundant_allocation.stderr b/tests/ui/redundant_allocation.stderr index fdab74eb538e3..c3b10e5f5e679 100644 --- a/tests/ui/redundant_allocation.stderr +++ b/tests/ui/redundant_allocation.stderr @@ -134,5 +134,14 @@ LL | pub fn arc_test9(foo: Arc>) -> Arc>> { = note: `Rc>` is already on the heap, `Arc>>` makes an extra allocation = help: consider using just `Arc>` or `Rc>` -error: aborting due to 15 previous errors +error: usage of `Rc>>` + --> $DIR/redundant_allocation.rs:97:27 + | +LL | pub fn test_rc_box(_: Rc>>) {} + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `Box>` is already on the heap, `Rc>>` makes an extra allocation + = help: consider using just `Rc>` or `Box>` + +error: aborting due to 16 previous errors diff --git a/tests/ui/unnecessary_operation.fixed b/tests/ui/unnecessary_operation.fixed index 2fca96c4cd556..bf0ec8deb3458 100644 --- a/tests/ui/unnecessary_operation.fixed +++ b/tests/ui/unnecessary_operation.fixed @@ -62,10 +62,10 @@ fn main() { get_number(); 5;get_number(); 42;get_number(); - [42, 55];get_usize(); + assert!([42, 55].len() > get_usize()); 42;get_number(); get_number(); - [42; 55];get_usize(); + assert!([42; 55].len() > get_usize()); get_number(); String::from("blah"); diff --git a/tests/ui/unnecessary_operation.stderr b/tests/ui/unnecessary_operation.stderr index f88c9f9908bea..f66d08ecb8281 100644 --- a/tests/ui/unnecessary_operation.stderr +++ b/tests/ui/unnecessary_operation.stderr @@ -1,128 +1,128 @@ -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:51:5 | LL | Tuple(get_number()); - | ^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` | = note: `-D clippy::unnecessary-operation` implied by `-D warnings` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:52:5 | LL | Struct { field: get_number() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:53:5 | LL | Struct { ..get_struct() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_struct();` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_struct();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:54:5 | LL | Enum::Tuple(get_number()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:55:5 | LL | Enum::Struct { field: get_number() }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:56:5 | LL | 5 + get_number(); - | ^^^^^^^^^^^^^^^^^ help: replace it with: `5;get_number();` + | ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:57:5 | LL | *&get_number(); - | ^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:58:5 | LL | &get_number(); - | ^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:59:5 | LL | (5, 6, get_number()); - | ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `5;6;get_number();` + | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;6;get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:60:5 | LL | box get_number(); - | ^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:61:5 | LL | get_number()..; - | ^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:62:5 | LL | ..get_number(); - | ^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:63:5 | LL | 5..get_number(); - | ^^^^^^^^^^^^^^^^ help: replace it with: `5;get_number();` + | ^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:64:5 | LL | [42, get_number()]; - | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `42;get_number();` + | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:65:5 | LL | [42, 55][get_usize()]; - | ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `[42, 55];get_usize();` + | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:66:5 | LL | (42, get_number()).1; - | ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `42;get_number();` + | ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:67:5 | LL | [get_number(); 55]; - | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `get_number();` + | ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:68:5 | LL | [42; 55][get_usize()]; - | ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `[42; 55];get_usize();` + | ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42; 55].len() > get_usize());` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:69:5 | LL | / { LL | | get_number() LL | | }; - | |______^ help: replace it with: `get_number();` + | |______^ help: statement can be reduced to: `get_number();` -error: statement can be reduced +error: unnecessary operation --> $DIR/unnecessary_operation.rs:72:5 | LL | / FooString { LL | | s: String::from("blah"), LL | | }; - | |______^ help: replace it with: `String::from("blah");` + | |______^ help: statement can be reduced to: `String::from("blah");` error: aborting due to 20 previous errors diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs index 1eaec4a50a6a6..77102b8cac0c9 100644 --- a/tests/versioncheck.rs +++ b/tests/versioncheck.rs @@ -1,4 +1,7 @@ +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![warn(rust_2018_idioms, unused_lifetimes)] #![allow(clippy::single_match_else)] + use rustc_tools_util::VersionInfo; #[test] From 7736e0afb86a54ca68c36d96262e902761f02f80 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Wed, 8 Sep 2021 18:54:48 +0200 Subject: [PATCH 084/621] Avoid unnecessary duplicate metadata encoding --- src/driver/aot.rs | 2 +- src/metadata.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/driver/aot.rs b/src/driver/aot.rs index 8968dc0b253e9..40cbc5e1a7ee4 100644 --- a/src/driver/aot.rs +++ b/src/driver/aot.rs @@ -277,7 +277,7 @@ pub(crate) fn run_aot( let tmp_file = tcx.output_filenames(()).temp_path(OutputType::Metadata, Some(&metadata_cgu_name)); - let obj = crate::metadata::new_metadata_object(tcx, &metadata_cgu_name); + let obj = crate::metadata::new_metadata_object(tcx, &metadata_cgu_name, &metadata); if let Err(err) = std::fs::write(&tmp_file, obj) { tcx.sess.fatal(&format!("error writing metadata object file: {}", err)); diff --git a/src/metadata.rs b/src/metadata.rs index 47d1032964201..9afa999a87d8d 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -3,14 +3,14 @@ use object::write::{Object, StandardSegment, Symbol, SymbolSection}; use object::{SectionKind, SymbolFlags, SymbolKind, SymbolScope}; +use rustc_middle::middle::cstore::EncodedMetadata; use rustc_middle::ty::TyCtxt; // Adapted from https://github.com/rust-lang/rust/blob/da573206f87b5510de4b0ee1a9c044127e409bd3/src/librustc_codegen_llvm/base.rs#L47-L112 -pub(crate) fn new_metadata_object(tcx: TyCtxt<'_>, cgu_name: &str) -> Vec { +pub(crate) fn new_metadata_object(tcx: TyCtxt<'_>, cgu_name: &str, metadata: &EncodedMetadata) -> Vec { use snap::write::FrameEncoder; use std::io::Write; - let metadata = tcx.encode_metadata(); let mut compressed = rustc_metadata::METADATA_HEADER.to_vec(); FrameEncoder::new(&mut compressed).write_all(&metadata.raw_data).unwrap(); From 7f1c2c0e53898c987942388bb0868cb8ccb46cdf Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 8 Sep 2021 14:05:49 -0500 Subject: [PATCH 085/621] Add target to cargo config --- .cargo/config | 1 + 1 file changed, 1 insertion(+) diff --git a/.cargo/config b/.cargo/config index 84ae36a46d71d..7a57644be00b3 100644 --- a/.cargo/config +++ b/.cargo/config @@ -7,3 +7,4 @@ collect-metadata = "test --test dogfood --features metadata-collector-lint -- ru [build] # -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests rustflags = ["-Zunstable-options", "-Zbinary-dep-depinfo"] +target-dir = "target" From cea46dd9fe268d733156ed620bee1309fbcc2396 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 8 Sep 2021 10:41:09 -0500 Subject: [PATCH 086/621] Use relative deps path --- tests/compile-test.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/compile-test.rs b/tests/compile-test.rs index c63c47690d52a..ddf5b394ae1f4 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -120,6 +120,8 @@ fn default_config() -> compiletest::Config { config.run_lib_path = path.clone(); config.compile_lib_path = path; } + let current_exe_path = std::env::current_exe().unwrap(); + let deps_path = current_exe_path.parent().unwrap(); // Using `-L dependency={}` enforces that external dependencies are added with `--extern`. // This is valuable because a) it allows us to monitor what external dependencies are used @@ -127,7 +129,7 @@ fn default_config() -> compiletest::Config { config.target_rustcflags = Some(format!( "--emit=metadata -L dependency={} -L dependency={} -Dwarnings -Zui-testing {}", host_lib().join("deps").display(), - cargo::TARGET_LIB.join("deps").display(), + deps_path.display(), extern_flags(), )); From 2e15c8054d7f26bf438a84a7e6a7fd34f540444f Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 8 Sep 2021 10:45:44 -0500 Subject: [PATCH 087/621] Make host libs -L flag optional --- tests/compile-test.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/compile-test.rs b/tests/compile-test.rs index ddf5b394ae1f4..27a1627622b40 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -104,7 +104,7 @@ fn extern_flags() -> String { } crates .into_iter() - .map(|(name, path)| format!("--extern {}={} ", name, path)) + .map(|(name, path)| format!(" --extern {}={}", name, path)) .collect() } @@ -126,10 +126,13 @@ fn default_config() -> compiletest::Config { // Using `-L dependency={}` enforces that external dependencies are added with `--extern`. // This is valuable because a) it allows us to monitor what external dependencies are used // and b) it ensures that conflicting rlibs are resolved properly. + let host_libs = option_env!("HOST_LIBS") + .map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display())) + .unwrap_or_default(); config.target_rustcflags = Some(format!( - "--emit=metadata -L dependency={} -L dependency={} -Dwarnings -Zui-testing {}", - host_lib().join("deps").display(), + "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{}{}", deps_path.display(), + host_libs, extern_flags(), )); From e3b171dcf099fbb7e7d3bd2cb28b11f7d3fb2773 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 8 Sep 2021 10:55:08 -0500 Subject: [PATCH 088/621] Use relative exe paths --- clippy_dev/src/bless.rs | 13 ++----------- tests/cargo/mod.rs | 13 ------------- tests/compile-test.rs | 11 ++++++----- tests/dogfood.rs | 7 ++++++- 4 files changed, 14 insertions(+), 30 deletions(-) diff --git a/clippy_dev/src/bless.rs b/clippy_dev/src/bless.rs index c4fa0a9aca70b..19153aa74d0b8 100644 --- a/clippy_dev/src/bless.rs +++ b/clippy_dev/src/bless.rs @@ -1,7 +1,6 @@ //! `bless` updates the reference files in the repo with changed output files //! from the last test run. -use std::env; use std::ffi::OsStr; use std::fs; use std::lazy::SyncLazy; @@ -10,17 +9,9 @@ use walkdir::WalkDir; use crate::clippy_project_root; -// NOTE: this is duplicated with tests/cargo/mod.rs What to do? -pub static CARGO_TARGET_DIR: SyncLazy = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") { - Some(v) => v.into(), - None => env::current_dir().unwrap().join("target"), -}); - static CLIPPY_BUILD_TIME: SyncLazy> = SyncLazy::new(|| { - let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string()); - let mut path = PathBuf::from(&**CARGO_TARGET_DIR); - path.push(profile); - path.push("cargo-clippy"); + let mut path = std::env::current_exe().unwrap(); + path.set_file_name("cargo-clippy"); fs::metadata(path).ok()?.modified().ok() }); diff --git a/tests/cargo/mod.rs b/tests/cargo/mod.rs index a8f3e3145f64d..1f3ce557eb45a 100644 --- a/tests/cargo/mod.rs +++ b/tests/cargo/mod.rs @@ -7,19 +7,6 @@ pub static CARGO_TARGET_DIR: SyncLazy = SyncLazy::new(|| match env::var None => env::current_dir().unwrap().join("target"), }); -pub static TARGET_LIB: SyncLazy = SyncLazy::new(|| { - if let Some(path) = option_env!("TARGET_LIBS") { - path.into() - } else { - let mut dir = CARGO_TARGET_DIR.clone(); - if let Some(target) = env::var_os("CARGO_BUILD_TARGET") { - dir.push(target); - } - dir.push(env!("PROFILE")); - dir - } -}); - #[must_use] pub fn is_rustc_test_suite() -> bool { option_env!("RUSTC_TEST_SUITE").is_some() diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 27a1627622b40..2641fb8123486 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -50,10 +50,6 @@ fn host_lib() -> PathBuf { option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from) } -fn clippy_driver_path() -> PathBuf { - option_env!("CLIPPY_DRIVER_PATH").map_or(cargo::TARGET_LIB.join("clippy-driver"), PathBuf::from) -} - /// Produces a string with an `--extern` flag for all UI test crate /// dependencies. /// @@ -122,6 +118,7 @@ fn default_config() -> compiletest::Config { } let current_exe_path = std::env::current_exe().unwrap(); let deps_path = current_exe_path.parent().unwrap(); + let profile_path = deps_path.parent().unwrap(); // Using `-L dependency={}` enforces that external dependencies are added with `--extern`. // This is valuable because a) it allows us to monitor what external dependencies are used @@ -137,7 +134,11 @@ fn default_config() -> compiletest::Config { )); config.build_base = host_lib().join("test_build_base"); - config.rustc_path = clippy_driver_path(); + config.rustc_path = profile_path.join(if cfg!(windows) { + "clippy-driver.exe" + } else { + "clippy-driver" + }); config } diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 54f452172deb4..4190dfcfe6df8 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -15,7 +15,12 @@ use std::process::Command; mod cargo; -static CLIPPY_PATH: SyncLazy = SyncLazy::new(|| cargo::TARGET_LIB.join("cargo-clippy")); +static CLIPPY_PATH: SyncLazy = SyncLazy::new(|| { + let mut path = std::env::current_exe().unwrap(); + assert!(path.pop()); // deps + path.set_file_name("cargo-clippy"); + path +}); #[test] fn dogfood_clippy() { From ecaf7acd4f9074e255cafaac9aa4b1c601dc311a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 8 Sep 2021 10:58:18 -0500 Subject: [PATCH 089/621] Use relative path for test builds --- clippy_dev/src/bless.rs | 7 ++----- tests/cargo/mod.rs | 9 --------- tests/compile-test.rs | 7 +------ 3 files changed, 3 insertions(+), 20 deletions(-) diff --git a/clippy_dev/src/bless.rs b/clippy_dev/src/bless.rs index 19153aa74d0b8..3d97fa8a8926b 100644 --- a/clippy_dev/src/bless.rs +++ b/clippy_dev/src/bless.rs @@ -85,10 +85,7 @@ fn updated_since_clippy_build(path: &Path) -> Option { } fn build_dir() -> PathBuf { - let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string()); - let mut path = PathBuf::new(); - path.push(CARGO_TARGET_DIR.clone()); - path.push(profile); - path.push("test_build_base"); + let mut path = std::env::current_exe().unwrap(); + path.set_file_name("test_build_base"); path } diff --git a/tests/cargo/mod.rs b/tests/cargo/mod.rs index 1f3ce557eb45a..4dbe71e4b6ad6 100644 --- a/tests/cargo/mod.rs +++ b/tests/cargo/mod.rs @@ -1,12 +1,3 @@ -use std::env; -use std::lazy::SyncLazy; -use std::path::PathBuf; - -pub static CARGO_TARGET_DIR: SyncLazy = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") { - Some(v) => v.into(), - None => env::current_dir().unwrap().join("target"), -}); - #[must_use] pub fn is_rustc_test_suite() -> bool { option_env!("RUSTC_TEST_SUITE").is_some() diff --git a/tests/compile-test.rs b/tests/compile-test.rs index 2641fb8123486..d113c4d34371a 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -1,5 +1,4 @@ #![feature(test)] // compiletest_rs requires this attribute -#![feature(once_cell)] #![cfg_attr(feature = "deny-warnings", deny(warnings))] #![warn(rust_2018_idioms, unused_lifetimes)] @@ -46,10 +45,6 @@ extern crate quote; #[allow(unused_extern_crates)] extern crate syn; -fn host_lib() -> PathBuf { - option_env!("HOST_LIBS").map_or(cargo::CARGO_TARGET_DIR.join(env!("PROFILE")), PathBuf::from) -} - /// Produces a string with an `--extern` flag for all UI test crate /// dependencies. /// @@ -133,7 +128,7 @@ fn default_config() -> compiletest::Config { extern_flags(), )); - config.build_base = host_lib().join("test_build_base"); + config.build_base = profile_path.join("test_build_base"); config.rustc_path = profile_path.join(if cfg!(windows) { "clippy-driver.exe" } else { From 1074bad06d421feabd6d4c75d0cb110c677d812d Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 8 Sep 2021 10:59:05 -0500 Subject: [PATCH 090/621] Rename test_build_base to test --- clippy_dev/src/bless.rs | 2 +- tests/compile-test.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_dev/src/bless.rs b/clippy_dev/src/bless.rs index 3d97fa8a8926b..daf0fcc993bad 100644 --- a/clippy_dev/src/bless.rs +++ b/clippy_dev/src/bless.rs @@ -86,6 +86,6 @@ fn updated_since_clippy_build(path: &Path) -> Option { fn build_dir() -> PathBuf { let mut path = std::env::current_exe().unwrap(); - path.set_file_name("test_build_base"); + path.set_file_name("test"); path } diff --git a/tests/compile-test.rs b/tests/compile-test.rs index d113c4d34371a..c9d1011159589 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -128,7 +128,7 @@ fn default_config() -> compiletest::Config { extern_flags(), )); - config.build_base = profile_path.join("test_build_base"); + config.build_base = profile_path.join("test"); config.rustc_path = profile_path.join(if cfg!(windows) { "clippy-driver.exe" } else { From 5250744abc89944213f19789154045d709568c7b Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 8 Sep 2021 12:50:57 -0500 Subject: [PATCH 091/621] Remove target dir from aliases --- .cargo/config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.cargo/config b/.cargo/config index 7a57644be00b3..688473f2f9bfc 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,7 +1,7 @@ [alias] uitest = "test --test compile-test" -dev = "run --target-dir clippy_dev/target --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" -lintcheck = "run --target-dir lintcheck/target --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- " +dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" +lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- " collect-metadata = "test --test dogfood --features metadata-collector-lint -- run_metadata_collection_lint --ignored" [build] From 9e08e7f631319a13d763ffe2df5b427b6cef8b0a Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Wed, 8 Sep 2021 13:34:00 -0500 Subject: [PATCH 092/621] Remove special dogfood target --- src/main.rs | 20 +------------------- tests/dogfood.rs | 5 ----- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/src/main.rs b/src/main.rs index 7589f42a7b4d3..7ebdd947893e9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,6 @@ use rustc_tools_util::VersionInfo; use std::env; -use std::ffi::OsString; use std::path::PathBuf; use std::process::{self, Command}; @@ -14,7 +13,7 @@ Usage: cargo clippy [options] [--] [...] Common options: - --no-deps Run Clippy only on the given crate, without linting the dependencies + --no-deps Run Clippy only on the given crate, without linting the dependencies --fix Automatically apply lint suggestions. This flag implies `--no-deps` -h, --help Print this message -V, --version Print version info and exit @@ -116,22 +115,6 @@ impl ClippyCmd { path } - fn target_dir() -> Option<(&'static str, OsString)> { - env::var_os("CLIPPY_DOGFOOD") - .map(|_| { - env::var_os("CARGO_MANIFEST_DIR").map_or_else( - || std::ffi::OsString::from("clippy_dogfood"), - |d| { - std::path::PathBuf::from(d) - .join("target") - .join("dogfood") - .into_os_string() - }, - ) - }) - .map(|p| ("CARGO_TARGET_DIR", p)) - } - fn into_std_cmd(self) -> Command { let mut cmd = Command::new("cargo"); let clippy_args: String = self @@ -141,7 +124,6 @@ impl ClippyCmd { .collect(); cmd.env("RUSTC_WORKSPACE_WRAPPER", Self::path()) - .envs(ClippyCmd::target_dir()) .env("CLIPPY_ARGS", clippy_args) .arg(self.cargo_subcommand) .args(&self.args); diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 4190dfcfe6df8..a37cdfed126f6 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -33,7 +33,6 @@ fn dogfood_clippy() { let mut command = Command::new(&*CLIPPY_PATH); command .current_dir(root_dir) - .env("CLIPPY_DOGFOOD", "1") .env("CARGO_INCREMENTAL", "0") .arg("clippy") .arg("--all-targets") @@ -79,7 +78,6 @@ fn test_no_deps_ignores_path_deps_in_workspaces() { // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`. let output = Command::new(&*CLIPPY_PATH) .current_dir(&cwd) - .env("CLIPPY_DOGFOOD", "1") .env("CARGO_INCREMENTAL", "0") .arg("clippy") .args(&["-p", "subcrate"]) @@ -99,7 +97,6 @@ fn test_no_deps_ignores_path_deps_in_workspaces() { // Test that without the `--no-deps` argument, `path_dep` is linted. let output = Command::new(&*CLIPPY_PATH) .current_dir(&cwd) - .env("CLIPPY_DOGFOOD", "1") .env("CARGO_INCREMENTAL", "0") .arg("clippy") .args(&["-p", "subcrate"]) @@ -126,7 +123,6 @@ fn test_no_deps_ignores_path_deps_in_workspaces() { let successful_build = || { let output = Command::new(&*CLIPPY_PATH) .current_dir(&cwd) - .env("CLIPPY_DOGFOOD", "1") .env("CARGO_INCREMENTAL", "0") .arg("clippy") .args(&["-p", "subcrate"]) @@ -228,7 +224,6 @@ fn run_clippy_for_project(project: &str) { command .current_dir(root_dir.join(project)) - .env("CLIPPY_DOGFOOD", "1") .env("CARGO_INCREMENTAL", "0") .arg("clippy") .arg("--all-targets") From 293db0d33c06f1642af51df0afcb6f5e0fd0eb10 Mon Sep 17 00:00:00 2001 From: Ariel Davis Date: Wed, 8 Sep 2021 21:12:02 -0400 Subject: [PATCH 093/621] Allow giving reasons for disallowed_methods --- clippy_lints/src/disallowed_method.rs | 84 +++++++++++-------- clippy_lints/src/lib.rs | 4 +- clippy_lints/src/utils/conf.rs | 10 ++- .../toml_disallowed_method/clippy.toml | 7 +- .../conf_disallowed_method.stderr | 8 +- 5 files changed, 69 insertions(+), 44 deletions(-) diff --git a/clippy_lints/src/disallowed_method.rs b/clippy_lints/src/disallowed_method.rs index 7069cb4198ca9..1167b26c8f153 100644 --- a/clippy_lints/src/disallowed_method.rs +++ b/clippy_lints/src/disallowed_method.rs @@ -1,33 +1,44 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::fn_def_id; -use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{def::Res, def_id::DefId, Crate, Expr}; +use rustc_hir::{def::Res, def_id::DefIdMap, Crate, Expr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Symbol; + +use crate::utils::conf; declare_clippy_lint! { /// ### What it does /// Denies the configured methods and functions in clippy.toml /// /// ### Why is this bad? - /// Some methods are undesirable in certain contexts, - /// and it's beneficial to lint for them as needed. + /// Some methods are undesirable in certain contexts, and it's beneficial to + /// lint for them as needed. /// /// ### Example /// An example clippy.toml configuration: /// ```toml /// # clippy.toml - /// disallowed-methods = ["std::vec::Vec::leak", "std::time::Instant::now"] + /// disallowed-methods = [ + /// # Can use a string as the path of the disallowed method. + /// "std::boxed::Box::new", + /// # Can also use an inline table with a `path` key. + /// { path = "std::time::Instant::now" }, + /// # When using an inline table, can add a `reason` for why the method + /// # is disallowed. + /// { path = "std::vec::Vec::leak", reason = "no leaking memory" }, + /// ] /// ``` /// /// ```rust,ignore /// // Example code where clippy issues a warning /// let xs = vec![1, 2, 3, 4]; /// xs.leak(); // Vec::leak is disallowed in the config. + /// // The diagnostic contains the message "no leaking memory". /// /// let _now = Instant::now(); // Instant::now is disallowed in the config. + /// + /// let _box = Box::new(3); // Box::new is disallowed in the config. /// ``` /// /// Use instead: @@ -43,18 +54,15 @@ declare_clippy_lint! { #[derive(Clone, Debug)] pub struct DisallowedMethod { - disallowed: FxHashSet>, - def_ids: FxHashSet<(DefId, Vec)>, + conf_disallowed: Vec, + disallowed: DefIdMap>, } impl DisallowedMethod { - pub fn new(disallowed: &FxHashSet) -> Self { + pub fn new(conf_disallowed: Vec) -> Self { Self { - disallowed: disallowed - .iter() - .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::>()) - .collect(), - def_ids: FxHashSet::default(), + conf_disallowed, + disallowed: DefIdMap::default(), } } } @@ -63,32 +71,36 @@ impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]); impl<'tcx> LateLintPass<'tcx> for DisallowedMethod { fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) { - for path in &self.disallowed { - let segs = path.iter().map(ToString::to_string).collect::>(); - if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::>()) - { - self.def_ids.insert((id, path.clone())); + for conf in &self.conf_disallowed { + let (path, reason) = match conf { + conf::DisallowedMethod::Simple(path) => (path, None), + conf::DisallowedMethod::WithReason { path, reason } => ( + path, + reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)), + ), + }; + let segs: Vec<_> = path.split("::").collect(); + if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs) { + self.disallowed.insert(id, reason); } } } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some(def_id) = fn_def_id(cx, expr) { - if self.def_ids.iter().any(|(id, _)| def_id == *id) { - let func_path = cx.get_def_path(def_id); - let func_path_string = func_path - .into_iter() - .map(Symbol::to_ident_string) - .collect::>() - .join("::"); - - span_lint( - cx, - DISALLOWED_METHOD, - expr.span, - &format!("use of a disallowed method `{}`", func_path_string), - ); + let def_id = match fn_def_id(cx, expr) { + Some(def_id) => def_id, + None => return, + }; + let reason = match self.disallowed.get(&def_id) { + Some(reason) => reason, + None => return, + }; + let func_path = cx.tcx.def_path_str(def_id); + let msg = format!("use of a disallowed method `{}`", func_path); + span_lint_and_then(cx, DISALLOWED_METHOD, expr.span, &msg, |diag| { + if let Some(reason) = reason { + diag.note(reason); } - } + }); } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 92a13be81f411..9544309ae4f45 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -2105,8 +2105,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(float_equality_without_abs::FloatEqualityWithoutAbs)); store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)); store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync)); - let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::>(); - store.register_late_pass(move || Box::new(disallowed_method::DisallowedMethod::new(&disallowed_methods))); + let disallowed_methods = conf.disallowed_methods.clone(); + store.register_late_pass(move || Box::new(disallowed_method::DisallowedMethod::new(disallowed_methods.clone()))); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)); store.register_late_pass(|| Box::new(undropped_manually_drops::UndroppedManuallyDrops)); diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 8651fa6fcf9ea..8b087507b1189 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -15,6 +15,14 @@ pub struct Rename { pub rename: String, } +/// A single disallowed method, used by the `DISALLOWED_METHOD` lint. +#[derive(Clone, Debug, Deserialize)] +#[serde(untagged)] +pub enum DisallowedMethod { + Simple(String), + WithReason { path: String, reason: Option }, +} + /// Conf with parse errors #[derive(Default)] pub struct TryConf { @@ -243,7 +251,7 @@ define_Conf! { /// Lint: DISALLOWED_METHOD. /// /// The list of disallowed methods, written as fully qualified paths. - (disallowed_methods: Vec = Vec::new()), + (disallowed_methods: Vec = Vec::new()), /// Lint: DISALLOWED_TYPE. /// /// The list of disallowed types, written as fully qualified paths. diff --git a/tests/ui-toml/toml_disallowed_method/clippy.toml b/tests/ui-toml/toml_disallowed_method/clippy.toml index a3245da68250c..f1d4a4619c5dc 100644 --- a/tests/ui-toml/toml_disallowed_method/clippy.toml +++ b/tests/ui-toml/toml_disallowed_method/clippy.toml @@ -1,5 +1,8 @@ disallowed-methods = [ + # just a string is shorthand for path only "std::iter::Iterator::sum", - "regex::Regex::is_match", - "regex::Regex::new" + # can give path and reason with an inline table + { path = "regex::Regex::is_match", reason = "no matching allowed" }, + # can use an inline table but omit reason + { path = "regex::Regex::new" }, ] diff --git a/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr index 2b628c67fa751..38123220a4320 100644 --- a/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr +++ b/tests/ui-toml/toml_disallowed_method/conf_disallowed_method.stderr @@ -1,4 +1,4 @@ -error: use of a disallowed method `regex::re_unicode::Regex::new` +error: use of a disallowed method `regex::Regex::new` --> $DIR/conf_disallowed_method.rs:7:14 | LL | let re = Regex::new(r"ab.*c").unwrap(); @@ -6,13 +6,15 @@ LL | let re = Regex::new(r"ab.*c").unwrap(); | = note: `-D clippy::disallowed-method` implied by `-D warnings` -error: use of a disallowed method `regex::re_unicode::Regex::is_match` +error: use of a disallowed method `regex::Regex::is_match` --> $DIR/conf_disallowed_method.rs:8:5 | LL | re.is_match("abc"); | ^^^^^^^^^^^^^^^^^^ + | + = note: no matching allowed (from clippy.toml) -error: use of a disallowed method `core::iter::traits::iterator::Iterator::sum` +error: use of a disallowed method `std::iter::Iterator::sum` --> $DIR/conf_disallowed_method.rs:11:5 | LL | a.iter().sum::(); From 8f88acdbfa6073eefbfcd773619d460fdfec122a Mon Sep 17 00:00:00 2001 From: Labelray Date: Tue, 7 Sep 2021 09:59:21 +0800 Subject: [PATCH 094/621] add new lint `iter_not_returning_iterator` --- CHANGELOG.md | 1 + .../src/iter_not_returning_iterator.rs | 64 +++++++++++++++++++ clippy_lints/src/lib.rs | 4 ++ tests/ui/iter_not_returning_iterator.rs | 47 ++++++++++++++ tests/ui/iter_not_returning_iterator.stderr | 16 +++++ 5 files changed, 132 insertions(+) create mode 100644 clippy_lints/src/iter_not_returning_iterator.rs create mode 100644 tests/ui/iter_not_returning_iterator.rs create mode 100644 tests/ui/iter_not_returning_iterator.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index f5ac2f7c9f8c9..71383e8116b04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2722,6 +2722,7 @@ Released 2018-09-13 [`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count [`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop [`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice +[`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator [`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth [`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero [`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs new file mode 100644 index 0000000000000..6c779348ca28a --- /dev/null +++ b/clippy_lints/src/iter_not_returning_iterator.rs @@ -0,0 +1,64 @@ +use clippy_utils::{diagnostics::span_lint, return_ty, ty::implements_trait}; +use rustc_hir::{ImplItem, ImplItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::kw; +use rustc_span::symbol::sym; + +declare_clippy_lint! { + /// ### What it does + /// Detects methods named `iter` or `iter_mut` that do not have a return type that implements `Iterator`. + /// + /// ### Why is this bad? + /// Methods named `iter` or `iter_mut` conventionally return an `Iterator`. + /// + /// ### Example + /// ```rust + /// // `String` does not implement `Iterator` + /// struct Data {} + /// impl Data { + /// fn iter(&self) -> String { + /// todo!() + /// } + /// } + /// ``` + /// Use instead: + /// ```rust + /// use std::str::Chars; + /// struct Data {} + /// impl Data { + /// fn iter(&self) -> Chars<'static> { + /// todo!() + /// } + /// } + /// ``` + pub ITER_NOT_RETURNING_ITERATOR, + pedantic, + "methods named `iter` or `iter_mut` that do not return an `Iterator`" +} + +declare_lint_pass!(IterNotReturningIterator => [ITER_NOT_RETURNING_ITERATOR]); + +impl LateLintPass<'_> for IterNotReturningIterator { + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'tcx>) { + let name: &str = &impl_item.ident.name.as_str(); + if_chain! { + if let ImplItemKind::Fn(fn_sig, _) = &impl_item.kind; + let ret_ty = return_ty(cx, impl_item.hir_id()); + if matches!(name, "iter" | "iter_mut"); + if let [param] = cx.tcx.fn_arg_names(impl_item.def_id); + if param.name == kw::SelfLower; + if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); + if !implements_trait(cx, ret_ty, iter_trait_id, &[]); + + then { + span_lint( + cx, + ITER_NOT_RETURNING_ITERATOR, + fn_sig.span, + &format!("this method is named `{}` but its return type does not implement `Iterator`", name), + ); + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 92a13be81f411..63deb46dc736f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -241,6 +241,7 @@ mod int_plus_one; mod integer_division; mod invalid_upcast_comparisons; mod items_after_statements; +mod iter_not_returning_iterator; mod large_const_arrays; mod large_enum_variant; mod large_stack_arrays; @@ -674,6 +675,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: integer_division::INTEGER_DIVISION, invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS, items_after_statements::ITEMS_AFTER_STATEMENTS, + iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR, large_const_arrays::LARGE_CONST_ARRAYS, large_enum_variant::LARGE_ENUM_VARIANT, large_stack_arrays::LARGE_STACK_ARRAYS, @@ -1104,6 +1106,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(infinite_iter::MAYBE_INFINITE_ITER), LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS), + LintId::of(iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR), LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS), LintId::of(let_underscore::LET_UNDERSCORE_DROP), LintId::of(literal_representation::LARGE_DIGIT_GROUPS), @@ -2131,6 +2134,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(strlen_on_c_strings::StrlenOnCStrings)); store.register_late_pass(move || Box::new(self_named_constructors::SelfNamedConstructors)); store.register_late_pass(move || Box::new(feature_name::FeatureName)); + store.register_late_pass(move || Box::new(iter_not_returning_iterator::IterNotReturningIterator)); } #[rustfmt::skip] diff --git a/tests/ui/iter_not_returning_iterator.rs b/tests/ui/iter_not_returning_iterator.rs new file mode 100644 index 0000000000000..377f760b3c4b2 --- /dev/null +++ b/tests/ui/iter_not_returning_iterator.rs @@ -0,0 +1,47 @@ +#![warn(clippy::iter_not_returning_iterator)] + +struct Data { + begin: u32, +} + +struct Counter { + count: u32, +} + +impl Data { + fn iter(&self) -> Counter { + todo!() + } + + fn iter_mut(&self) -> Counter { + todo!() + } +} + +struct Data2 { + begin: u32, +} + +struct Counter2 { + count: u32, +} + +impl Data2 { + fn iter(&self) -> Counter2 { + todo!() + } + + fn iter_mut(&self) -> Counter2 { + todo!() + } +} + +impl Iterator for Counter { + type Item = u32; + + fn next(&mut self) -> Option { + todo!() + } +} + +fn main() {} diff --git a/tests/ui/iter_not_returning_iterator.stderr b/tests/ui/iter_not_returning_iterator.stderr new file mode 100644 index 0000000000000..2273cd0be66ff --- /dev/null +++ b/tests/ui/iter_not_returning_iterator.stderr @@ -0,0 +1,16 @@ +error: this method is named `iter` but its return type does not implement `Iterator` + --> $DIR/iter_not_returning_iterator.rs:30:5 + | +LL | fn iter(&self) -> Counter2 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::iter-not-returning-iterator` implied by `-D warnings` + +error: this method is named `iter_mut` but its return type does not implement `Iterator` + --> $DIR/iter_not_returning_iterator.rs:34:5 + | +LL | fn iter_mut(&self) -> Counter2 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + From 6ff5b471ef7806c40e57b9e83107ce4d62012d1b Mon Sep 17 00:00:00 2001 From: Robert Xiao Date: Fri, 13 Aug 2021 02:03:31 -0600 Subject: [PATCH 095/621] Fix issue #72649: avoid spurious "previous iteration of loop" errors. Only follow backwards edges during get_moved_indexes if the move path is definitely initialized at loop entry. Otherwise, the error occurred prior to the loop, so we ignore the backwards edges to avoid generating misleading "value moved here, in previous iteration of loop" errors. This patch also slightly improves the analysis of inits, including NonPanicPathOnly initializations (which are ignored by drop_flag_effects::for_location_inits). This is required for the definite initialization analysis, but may also help find certain skipped reinits in rare cases. Patch passes all non-ignored src/test/ui testcases. --- .../src/diagnostics/conflict_errors.rs | 108 +++++++++++++----- .../ui/moves/issue-72649-uninit-in-loop.rs | 74 ++++++++++++ .../moves/issue-72649-uninit-in-loop.stderr | 58 ++++++++++ 3 files changed, 212 insertions(+), 28 deletions(-) create mode 100644 src/test/ui/moves/issue-72649-uninit-in-loop.rs create mode 100644 src/test/ui/moves/issue-72649-uninit-in-loop.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index ce1e7c14b1ff6..b69df438b61c5 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -10,8 +10,7 @@ use rustc_middle::mir::{ ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm, }; use rustc_middle::ty::{self, suggest_constraining_type_param, Ty}; -use rustc_mir_dataflow::drop_flag_effects; -use rustc_mir_dataflow::move_paths::{MoveOutIndex, MovePathIndex}; +use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; use rustc_span::source_map::DesugaringKind; use rustc_span::symbol::sym; use rustc_span::{BytePos, MultiSpan, Span, DUMMY_SP}; @@ -1516,25 +1515,45 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } + let mut mpis = vec![mpi]; + let move_paths = &self.move_data.move_paths; + mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi)); + let mut stack = Vec::new(); - stack.extend(predecessor_locations(self.body, location).map(|predecessor| { - let is_back_edge = location.dominates(predecessor, &self.dominators); - (predecessor, is_back_edge) - })); + let mut back_edge_stack = Vec::new(); + + predecessor_locations(self.body, location).for_each(|predecessor| { + if location.dominates(predecessor, &self.dominators) { + back_edge_stack.push(predecessor) + } else { + stack.push(predecessor); + } + }); + + let mut reached_start = false; + + /* Check if the mpi is initialized as an argument */ + let mut is_argument = false; + for arg in self.body.args_iter() { + let path = self.move_data.rev_lookup.find_local(arg); + if mpis.contains(&path) { + is_argument = true; + } + } let mut visited = FxHashSet::default(); let mut move_locations = FxHashSet::default(); let mut reinits = vec![]; let mut result = vec![]; - 'dfs: while let Some((location, is_back_edge)) = stack.pop() { + let mut dfs_iter = |result: &mut Vec, location: Location, is_back_edge: bool| { debug!( "report_use_of_moved_or_uninitialized: (current_location={:?}, back_edge={})", location, is_back_edge ); if !visited.insert(location) { - continue; + return true; } // check for moves @@ -1553,10 +1572,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // worry about the other case: that is, if there is a move of a.b.c, it is already // marked as a move of a.b and a as well, so we will generate the correct errors // there. - let mut mpis = vec![mpi]; - let move_paths = &self.move_data.move_paths; - mpis.extend(move_paths[mpi].parents(move_paths).map(|(mpi, _)| mpi)); - for moi in &self.move_data.loc_map[location] { debug!("report_use_of_moved_or_uninitialized: moi={:?}", moi); let path = self.move_data.moves[*moi].path; @@ -1584,33 +1599,70 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // Because we stop the DFS here, we only highlight `let c = a`, // and not `let b = a`. We will of course also report an error at // `let c = a` which highlights `let b = a` as the move. - continue 'dfs; + return true; } } } // check for inits let mut any_match = false; - drop_flag_effects::for_location_inits( - self.infcx.tcx, - &self.body, - self.move_data, - location, - |m| { - if m == mpi { - any_match = true; + for ii in &self.move_data.init_loc_map[location] { + let init = self.move_data.inits[*ii]; + match init.kind { + InitKind::Deep | InitKind::NonPanicPathOnly => { + if mpis.contains(&init.path) { + any_match = true; + } } - }, - ); + InitKind::Shallow => { + if mpi == init.path { + any_match = true; + } + } + } + } if any_match { reinits.push(location); - continue 'dfs; + return true; } + return false; + }; - stack.extend(predecessor_locations(self.body, location).map(|predecessor| { - let back_edge = location.dominates(predecessor, &self.dominators); - (predecessor, is_back_edge || back_edge) - })); + while let Some(location) = stack.pop() { + if dfs_iter(&mut result, location, false) { + continue; + } + + let mut has_predecessor = false; + predecessor_locations(self.body, location).for_each(|predecessor| { + if location.dominates(predecessor, &self.dominators) { + back_edge_stack.push(predecessor) + } else { + stack.push(predecessor); + } + has_predecessor = true; + }); + + if !has_predecessor { + reached_start = true; + } + } + if (is_argument || !reached_start) && result.is_empty() { + /* Process back edges (moves in future loop iterations) only if + the move path is definitely initialized upon loop entry, + to avoid spurious "in previous iteration" errors. + During DFS, if there's a path from the error back to the start + of the function with no intervening init or move, then the + move path may be uninitialized at loop entry. + */ + while let Some(location) = back_edge_stack.pop() { + if dfs_iter(&mut result, location, true) { + continue; + } + + predecessor_locations(self.body, location) + .for_each(|predecessor| back_edge_stack.push(predecessor)); + } } // Check if we can reach these reinits from a move location. diff --git a/src/test/ui/moves/issue-72649-uninit-in-loop.rs b/src/test/ui/moves/issue-72649-uninit-in-loop.rs new file mode 100644 index 0000000000000..e6bc4e22ec22c --- /dev/null +++ b/src/test/ui/moves/issue-72649-uninit-in-loop.rs @@ -0,0 +1,74 @@ +// Regression test for issue #72649 +// Tests that we don't emit spurious +// 'value moved in previous iteration of loop' message + +struct NonCopy; + +fn good() { + loop { + let value = NonCopy{}; + let _used = value; + } +} + +fn moved_here_1() { + loop { + let value = NonCopy{}; + //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait + let _used = value; + //~^ NOTE value moved here + let _used2 = value; //~ ERROR use of moved value: `value` + //~^ NOTE value used here after move + } +} + +fn moved_here_2() { + let value = NonCopy{}; + //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait + loop { + let _used = value; + //~^ NOTE value moved here + loop { + let _used2 = value; //~ ERROR use of moved value: `value` + //~^ NOTE value used here after move + } + } +} + +fn moved_loop_1() { + let value = NonCopy{}; + //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait + loop { + let _used = value; //~ ERROR use of moved value: `value` + //~^ NOTE value moved here, in previous iteration of loop + } +} + +fn moved_loop_2() { + let mut value = NonCopy{}; + //~^ NOTE move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait + let _used = value; + value = NonCopy{}; + loop { + let _used2 = value; //~ ERROR use of moved value: `value` + //~^ NOTE value moved here, in previous iteration of loop + } +} + +fn uninit_1() { + loop { + let value: NonCopy; + let _used = value; //~ ERROR use of possibly-uninitialized variable: `value` + //~^ NOTE use of possibly-uninitialized `value` + } +} + +fn uninit_2() { + let mut value: NonCopy; + loop { + let _used = value; //~ ERROR use of possibly-uninitialized variable: `value` + //~^ NOTE use of possibly-uninitialized `value` + } +} + +fn main() {} diff --git a/src/test/ui/moves/issue-72649-uninit-in-loop.stderr b/src/test/ui/moves/issue-72649-uninit-in-loop.stderr new file mode 100644 index 0000000000000..076d3dff14086 --- /dev/null +++ b/src/test/ui/moves/issue-72649-uninit-in-loop.stderr @@ -0,0 +1,58 @@ +error[E0382]: use of moved value: `value` + --> $DIR/issue-72649-uninit-in-loop.rs:20:22 + | +LL | let value = NonCopy{}; + | ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait +LL | +LL | let _used = value; + | ----- value moved here +LL | +LL | let _used2 = value; + | ^^^^^ value used here after move + +error[E0382]: use of moved value: `value` + --> $DIR/issue-72649-uninit-in-loop.rs:32:26 + | +LL | let value = NonCopy{}; + | ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait +... +LL | let _used = value; + | ----- value moved here +... +LL | let _used2 = value; + | ^^^^^ value used here after move + +error[E0382]: use of moved value: `value` + --> $DIR/issue-72649-uninit-in-loop.rs:42:21 + | +LL | let value = NonCopy{}; + | ----- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait +... +LL | let _used = value; + | ^^^^^ value moved here, in previous iteration of loop + +error[E0382]: use of moved value: `value` + --> $DIR/issue-72649-uninit-in-loop.rs:53:22 + | +LL | let mut value = NonCopy{}; + | --------- move occurs because `value` has type `NonCopy`, which does not implement the `Copy` trait +... +LL | let _used2 = value; + | ^^^^^ value moved here, in previous iteration of loop + +error[E0381]: use of possibly-uninitialized variable: `value` + --> $DIR/issue-72649-uninit-in-loop.rs:61:21 + | +LL | let _used = value; + | ^^^^^ use of possibly-uninitialized `value` + +error[E0381]: use of possibly-uninitialized variable: `value` + --> $DIR/issue-72649-uninit-in-loop.rs:69:21 + | +LL | let _used = value; + | ^^^^^ use of possibly-uninitialized `value` + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0381, E0382. +For more information about an error, try `rustc --explain E0381`. From a81217c12d70ec6707b4956e1f70c9f58ce992ac Mon Sep 17 00:00:00 2001 From: Andreas Liljeqvist Date: Sun, 29 Aug 2021 11:06:55 +0200 Subject: [PATCH 096/621] Make `abi::Abi` `Copy` and remove a *lot* of refs fix fix Remove more refs and clones fix more fix --- src/abi/pass_mode.rs | 20 ++++++++++---------- src/intrinsics/mod.rs | 4 ++-- src/value_and_place.rs | 24 +++++++++--------------- 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/src/abi/pass_mode.rs b/src/abi/pass_mode.rs index 44eae706ea8f6..2144e7ed67acb 100644 --- a/src/abi/pass_mode.rs +++ b/src/abi/pass_mode.rs @@ -92,9 +92,9 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { fn get_abi_param(&self, tcx: TyCtxt<'tcx>) -> SmallVec<[AbiParam; 2]> { match self.mode { PassMode::Ignore => smallvec![], - PassMode::Direct(attrs) => match &self.layout.abi { + PassMode::Direct(attrs) => match self.layout.abi { Abi::Scalar(scalar) => smallvec![apply_arg_attrs_to_abi_param( - AbiParam::new(scalar_to_clif_type(tcx, scalar.clone())), + AbiParam::new(scalar_to_clif_type(tcx, scalar)), attrs )], Abi::Vector { .. } => { @@ -103,10 +103,10 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { } _ => unreachable!("{:?}", self.layout.abi), }, - PassMode::Pair(attrs_a, attrs_b) => match &self.layout.abi { + PassMode::Pair(attrs_a, attrs_b) => match self.layout.abi { Abi::ScalarPair(a, b) => { - let a = scalar_to_clif_type(tcx, a.clone()); - let b = scalar_to_clif_type(tcx, b.clone()); + let a = scalar_to_clif_type(tcx, a); + let b = scalar_to_clif_type(tcx, b); smallvec![ apply_arg_attrs_to_abi_param(AbiParam::new(a), attrs_a), apply_arg_attrs_to_abi_param(AbiParam::new(b), attrs_b), @@ -139,9 +139,9 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { fn get_abi_return(&self, tcx: TyCtxt<'tcx>) -> (Option, Vec) { match self.mode { PassMode::Ignore => (None, vec![]), - PassMode::Direct(_) => match &self.layout.abi { + PassMode::Direct(_) => match self.layout.abi { Abi::Scalar(scalar) => { - (None, vec![AbiParam::new(scalar_to_clif_type(tcx, scalar.clone()))]) + (None, vec![AbiParam::new(scalar_to_clif_type(tcx, scalar))]) } Abi::Vector { .. } => { let vector_ty = crate::intrinsics::clif_vector_type(tcx, self.layout).unwrap(); @@ -149,10 +149,10 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { } _ => unreachable!("{:?}", self.layout.abi), }, - PassMode::Pair(_, _) => match &self.layout.abi { + PassMode::Pair(_, _) => match self.layout.abi { Abi::ScalarPair(a, b) => { - let a = scalar_to_clif_type(tcx, a.clone()); - let b = scalar_to_clif_type(tcx, b.clone()); + let a = scalar_to_clif_type(tcx, a); + let b = scalar_to_clif_type(tcx, b); (None, vec![AbiParam::new(a), AbiParam::new(b)]) } _ => unreachable!("{:?}", self.layout.abi), diff --git a/src/intrinsics/mod.rs b/src/intrinsics/mod.rs index 12f61e0c564aa..8b62b44df8ab3 100644 --- a/src/intrinsics/mod.rs +++ b/src/intrinsics/mod.rs @@ -143,8 +143,8 @@ macro validate_simd_type($fx:ident, $intrinsic:ident, $span:ident, $ty:expr) { } pub(crate) fn clif_vector_type<'tcx>(tcx: TyCtxt<'tcx>, layout: TyAndLayout<'tcx>) -> Option { - let (element, count) = match &layout.abi { - Abi::Vector { element, count } => (element.clone(), *count), + let (element, count) = match layout.abi { + Abi::Vector { element, count } => (element, count), _ => unreachable!(), }; diff --git a/src/value_and_place.rs b/src/value_and_place.rs index 364b3da92b888..30d5340935f14 100644 --- a/src/value_and_place.rs +++ b/src/value_and_place.rs @@ -49,11 +49,7 @@ fn codegen_field<'tcx>( } } -fn scalar_pair_calculate_b_offset( - tcx: TyCtxt<'_>, - a_scalar: &Scalar, - b_scalar: &Scalar, -) -> Offset32 { +fn scalar_pair_calculate_b_offset(tcx: TyCtxt<'_>, a_scalar: Scalar, b_scalar: Scalar) -> Offset32 { let b_offset = a_scalar.value.size(&tcx).align_to(b_scalar.value.align(&tcx).abi); Offset32::new(b_offset.bytes().try_into().unwrap()) } @@ -124,12 +120,10 @@ impl<'tcx> CValue<'tcx> { match self.0 { CValueInner::ByRef(ptr, None) => { let clif_ty = match layout.abi { - Abi::Scalar(ref scalar) => scalar_to_clif_type(fx.tcx, scalar.clone()), - Abi::Vector { ref element, count } => { - scalar_to_clif_type(fx.tcx, element.clone()) - .by(u16::try_from(count).unwrap()) - .unwrap() - } + Abi::Scalar(scalar) => scalar_to_clif_type(fx.tcx, scalar), + Abi::Vector { element, count } => scalar_to_clif_type(fx.tcx, element) + .by(u16::try_from(count).unwrap()) + .unwrap(), _ => unreachable!("{:?}", layout.ty), }; let mut flags = MemFlags::new(); @@ -147,13 +141,13 @@ impl<'tcx> CValue<'tcx> { let layout = self.1; match self.0 { CValueInner::ByRef(ptr, None) => { - let (a_scalar, b_scalar) = match &layout.abi { + let (a_scalar, b_scalar) = match layout.abi { Abi::ScalarPair(a, b) => (a, b), _ => unreachable!("load_scalar_pair({:?})", self), }; let b_offset = scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar); - let clif_ty1 = scalar_to_clif_type(fx.tcx, a_scalar.clone()); - let clif_ty2 = scalar_to_clif_type(fx.tcx, b_scalar.clone()); + let clif_ty1 = scalar_to_clif_type(fx.tcx, a_scalar); + let clif_ty2 = scalar_to_clif_type(fx.tcx, b_scalar); let mut flags = MemFlags::new(); flags.set_notrap(); let val1 = ptr.load(fx, clif_ty1, flags); @@ -564,7 +558,7 @@ impl<'tcx> CPlace<'tcx> { to_ptr.store(fx, val, flags); return; } - Abi::ScalarPair(ref a_scalar, ref b_scalar) => { + Abi::ScalarPair(a_scalar, b_scalar) => { let (value, extra) = from.load_scalar_pair(fx); let b_offset = scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar); to_ptr.store(fx, value, flags); From ccc087ed629db0489f4cecfeaaa69f6c0f9f6fe5 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Wed, 1 Sep 2021 18:31:42 -0400 Subject: [PATCH 097/621] Prep for upgrade to cargo_metadata 0.14.0 --- clippy_lints/src/cargo_common_metadata.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/cargo_common_metadata.rs b/clippy_lints/src/cargo_common_metadata.rs index bd5426ba707a8..162911b77d615 100644 --- a/clippy_lints/src/cargo_common_metadata.rs +++ b/clippy_lints/src/cargo_common_metadata.rs @@ -1,7 +1,5 @@ //! lint on missing cargo common metadata -use std::path::PathBuf; - use clippy_utils::{diagnostics::span_lint, is_lint_allowed}; use rustc_hir::{hir_id::CRATE_HIR_ID, Crate}; use rustc_lint::{LateContext, LateLintPass}; @@ -69,12 +67,8 @@ fn missing_warning(cx: &LateContext<'_>, package: &cargo_metadata::Package, fiel span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, &message); } -fn is_empty_str(value: &Option) -> bool { - value.as_ref().map_or(true, String::is_empty) -} - -fn is_empty_path(value: &Option) -> bool { - value.as_ref().and_then(|x| x.to_str()).map_or(true, str::is_empty) +fn is_empty_str>(value: &Option) -> bool { + value.as_ref().map_or(true, |s| s.as_ref().is_empty()) } fn is_empty_vec(value: &[String]) -> bool { @@ -98,7 +92,7 @@ impl LateLintPass<'_> for CargoCommonMetadata { missing_warning(cx, &package, "package.description"); } - if is_empty_str(&package.license) && is_empty_path(&package.license_file) { + if is_empty_str(&package.license) && is_empty_str(&package.license_file) { missing_warning(cx, &package, "either package.license or package.license_file"); } @@ -106,7 +100,7 @@ impl LateLintPass<'_> for CargoCommonMetadata { missing_warning(cx, &package, "package.repository"); } - if is_empty_path(&package.readme) { + if is_empty_str(&package.readme) { missing_warning(cx, &package, "package.readme"); } From 43ed2065cc9916f9d27140c825ab8e9321d580f0 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Thu, 9 Sep 2021 05:19:03 -0400 Subject: [PATCH 098/621] Update dependencies --- Cargo.toml | 22 ++++++++++------------ clippy_dev/Cargo.toml | 6 +++--- clippy_dummy/Cargo.toml | 2 +- clippy_lints/Cargo.toml | 14 +++++++------- clippy_utils/Cargo.toml | 4 ++-- lintcheck/Cargo.toml | 18 +++++++++--------- 6 files changed, 32 insertions(+), 34 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2310370fb9fbe..40aaa5924df5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,37 +21,35 @@ name = "clippy-driver" path = "src/driver.rs" [dependencies] -# begin automatic update -clippy_lints = { version = "0.1.50", path = "clippy_lints" } -# end automatic update +clippy_lints = { version = "0.1", path = "clippy_lints" } semver = "0.11" -rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" } -tempfile = { version = "3.1.0", optional = true } +rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } +tempfile = { version = "3.2", optional = true } [dev-dependencies] cargo_metadata = "0.12" -compiletest_rs = { version = "0.6.0", features = ["tmp"] } +compiletest_rs = { version = "0.7", features = ["tmp"] } tester = "0.9" -regex = "1.4" +regex = "1.5" # This is used by the `collect-metadata` alias. filetime = "0.2" # A noop dependency that changes in the Rust repository, it's a bit of a hack. # See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust` # for more information. -rustc-workspace-hack = "1.0.0" +rustc-workspace-hack = "1.0" # UI test dependencies clippy_utils = { path = "clippy_utils" } derive-new = "0.5" if_chain = "1.0" -itertools = "0.10.1" -quote = "1" +itertools = "0.10" +quote = "1.0" serde = { version = "1.0", features = ["derive"] } -syn = { version = "1", features = ["full"] } +syn = { version = "1.0", features = ["full"] } [build-dependencies] -rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util" } +rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } [features] deny-warnings = ["clippy_lints/deny-warnings"] diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index 0fae8c7b9afcf..d7d2655026b4b 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -6,11 +6,11 @@ edition = "2018" [dependencies] bytecount = "0.6" clap = "2.33" -itertools = "0.9" +itertools = "0.10" opener = "0.5" -regex = "1" +regex = "1.5" shell-escape = "0.1" -walkdir = "2" +walkdir = "2.3" [features] deny-warnings = [] diff --git a/clippy_dummy/Cargo.toml b/clippy_dummy/Cargo.toml index a1707bad5c268..c206a1eb07b50 100644 --- a/clippy_dummy/Cargo.toml +++ b/clippy_dummy/Cargo.toml @@ -13,4 +13,4 @@ keywords = ["clippy", "lint", "plugin"] categories = ["development-tools", "development-tools::cargo-plugins"] [build-dependencies] -term = "0.6" +term = "0.7" diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml index 3c28024bf926a..e59175a55e184 100644 --- a/clippy_lints/Cargo.toml +++ b/clippy_lints/Cargo.toml @@ -11,21 +11,21 @@ edition = "2018" [dependencies] cargo_metadata = "0.12" clippy_utils = { path = "../clippy_utils" } -if_chain = "1.0.0" -itertools = "0.9" +if_chain = "1.0" +itertools = "0.10" pulldown-cmark = { version = "0.8", default-features = false } -quine-mc_cluskey = "0.2.2" +quine-mc_cluskey = "0.2" regex-syntax = "0.6" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", optional = true } -toml = "0.5.3" +toml = "0.5" unicode-normalization = "0.1" -unicode-script = { version = "0.5.3", default-features = false } +unicode-script = { version = "0.5", default-features = false } semver = "0.11" -rustc-semver = "1.1.0" +rustc-semver = "1.1" # NOTE: cargo requires serde feat in its url dep # see -url = { version = "2.1.0", features = ["serde"] } +url = { version = "2.2", features = ["serde"] } [features] deny-warnings = ["clippy_utils/deny-warnings"] diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml index 4c038a997952a..7c24e830e71dc 100644 --- a/clippy_utils/Cargo.toml +++ b/clippy_utils/Cargo.toml @@ -5,8 +5,8 @@ edition = "2018" publish = false [dependencies] -if_chain = "1.0.0" -rustc-semver="1.1.0" +if_chain = "1.0" +rustc-semver = "1.1" [features] deny-warnings = [] diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index ada033de6e3ab..f33f1b65eabdb 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -11,15 +11,15 @@ publish = false [dependencies] clap = "2.33" -flate2 = {version = "1.0.19"} -fs_extra = {version = "1.2.0"} -rayon = {version = "1.5.0"} -serde = {version = "1.0", features = ["derive"]} -serde_json = {version = "1.0"} -tar = {version = "0.4.30"} -toml = {version = "0.5"} -ureq = {version = "2.0.0-rc3"} -walkdir = {version = "2.3.2"} +flate2 = "1.0" +fs_extra = "1.2" +rayon = "1.5" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +tar = "0.4" +toml = "0.5" +ureq = "2.2" +walkdir = "2.3" [features] deny-warnings = [] From 4f5563d0275118cbebbc2a3d3d60be1a7600adaa Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Thu, 9 Sep 2021 16:58:41 +0200 Subject: [PATCH 099/621] Added abs_diff for integer types. --- library/core/src/num/int_macros.rs | 40 +++++++++++++++++++++++++++++ library/core/src/num/uint_macros.rs | 21 +++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 2a02545041dcf..187be5d47573a 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2227,6 +2227,46 @@ macro_rules! int_impl { } } + /// Computes the absolute difference between `self` and `other`. + /// + /// This function always returns the correct answer without overflow or + /// panics by returning an unsigned integer. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(int_abs_diff)] + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".abs_diff(80), 20", stringify!($UnsignedT), ");")] + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".abs_diff(110), 10", stringify!($UnsignedT), ");")] + #[doc = concat!("assert_eq!((-100", stringify!($SelfT), ").abs_diff(80), 180", stringify!($UnsignedT), ");")] + #[doc = concat!("assert_eq!((-100", stringify!($SelfT), ").abs_diff(-120), 20", stringify!($UnsignedT), ");")] + #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.abs_diff(", stringify!($SelfT), "::MAX), ", stringify!($UnsignedT), "::MAX);")] + /// ``` + #[unstable(feature = "int_abs_diff", issue = "none")] + #[inline] + pub const fn abs_diff(self, other: Self) -> $UnsignedT { + if self < other { + // Converting a non-negative x from signed to unsigned by using + // `x as U` is left unchanged, but a negative x is converted + // to value x + 2^N. Thus if `s` and `o` are binary variables + // respectively indicating whether `self` and `other` are + // negative, we are computing the mathematical value: + // + // (other + o*2^N) - (self + s*2^N) mod 2^N + // other - self + (o-s)*2^N mod 2^N + // other - self mod 2^N + // + // Finally, taking the mod 2^N of the mathematical value of + // `other - self` does not change it as it already is + // in the range [0, 2^N). + (other as $UnsignedT).wrapping_sub(self as $UnsignedT) + } else { + (self as $UnsignedT).wrapping_sub(other as $UnsignedT) + } + } + /// Returns a number representing sign of `self`. /// /// - `0` if the number is zero diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 7523d8ec976dd..1bba4179f9955 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1490,6 +1490,27 @@ macro_rules! uint_impl { (c, b | d) } + /// Computes the absolute difference between `self` and `other`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(int_abs_diff)] + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".abs_diff(80), 20", stringify!($SelfT), ");")] + #[doc = concat!("assert_eq!(100", stringify!($SelfT), ".abs_diff(110), 10", stringify!($SelfT), ");")] + /// ``` + #[unstable(feature = "int_abs_diff", issue = "none")] + #[inline] + pub const fn abs_diff(self, other: Self) -> Self { + if self < other { + other - self + } else { + self - other + } + } + /// Calculates the multiplication of `self` and `rhs`. /// /// Returns a tuple of the multiplication along with a boolean From 81ce2fb1678aa6e5508bd1700d2d6f3e436340bb Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Fri, 21 May 2021 19:35:49 +0200 Subject: [PATCH 100/621] Ignore automatically derived impls of `Clone` and `Debug` in dead code analysis --- clippy_lints/src/macro_use.rs | 29 +++++++---------------------- clippy_lints/src/regex.rs | 8 ++------ tests/ui/default_trait_access.fixed | 2 +- tests/ui/default_trait_access.rs | 2 +- 4 files changed, 11 insertions(+), 30 deletions(-) diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 7627e0fb28956..41e6ad12d0589 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -29,36 +29,21 @@ declare_clippy_lint! { "#[macro_use] is no longer needed" } -const BRACKETS: &[char] = &['<', '>']; - #[derive(Clone, Debug, PartialEq, Eq)] struct PathAndSpan { path: String, span: Span, } -/// `MacroRefData` includes the name of the macro -/// and the path from `SourceMap::span_to_filename`. +/// `MacroRefData` includes the name of the macro. #[derive(Debug, Clone)] pub struct MacroRefData { name: String, - path: String, } impl MacroRefData { - pub fn new(name: String, callee: Span, cx: &LateContext<'_>) -> Self { - let sm = cx.sess().source_map(); - let mut path = sm.filename_for_diagnostics(&sm.span_to_filename(callee)).to_string(); - - // std lib paths are <::std::module::file type> - // so remove brackets, space and type. - if path.contains('<') { - path = path.replace(BRACKETS, ""); - } - if path.contains(' ') { - path = path.split(' ').next().unwrap().to_string(); - } - Self { name, path } + pub fn new(name: String) -> Self { + Self { name } } } @@ -78,7 +63,7 @@ impl MacroUseImports { fn push_unique_macro(&mut self, cx: &LateContext<'_>, span: Span) { let call_site = span.source_callsite(); let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); - if let Some(callee) = span.source_callee() { + if let Some(_callee) = span.source_callee() { if !self.collected.contains(&call_site) { let name = if name.contains("::") { name.split("::").last().unwrap().to_string() @@ -86,7 +71,7 @@ impl MacroUseImports { name.to_string() }; - self.mac_refs.push(MacroRefData::new(name, callee.def_site, cx)); + self.mac_refs.push(MacroRefData::new(name)); self.collected.insert(call_site); } } @@ -95,10 +80,10 @@ impl MacroUseImports { fn push_unique_macro_pat_ty(&mut self, cx: &LateContext<'_>, span: Span) { let call_site = span.source_callsite(); let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); - if let Some(callee) = span.source_callee() { + if let Some(_callee) = span.source_callee() { if !self.collected.contains(&call_site) { self.mac_refs - .push(MacroRefData::new(name.to_string(), callee.def_site, cx)); + .push(MacroRefData::new(name.to_string())); self.collected.insert(call_site); } } diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index eab097337306b..89be7bf844f3a 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -3,8 +3,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::{LitKind, StrStyle}; -use rustc_data_structures::fx::FxHashSet; -use rustc_hir::{BorrowKind, Expr, ExprKind, HirId}; +use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{BytePos, Span}; @@ -53,10 +52,7 @@ declare_clippy_lint! { } #[derive(Clone, Default)] -pub struct Regex { - spans: FxHashSet, - last: Option, -} +pub struct Regex {} impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX]); diff --git a/tests/ui/default_trait_access.fixed b/tests/ui/default_trait_access.fixed index 4c80cabc72305..f1f9c123dc842 100644 --- a/tests/ui/default_trait_access.fixed +++ b/tests/ui/default_trait_access.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_imports)] +#![allow(unused_imports,dead_code)] #![deny(clippy::default_trait_access)] use std::default; diff --git a/tests/ui/default_trait_access.rs b/tests/ui/default_trait_access.rs index a68b6455c0416..7f3dfc7f01366 100644 --- a/tests/ui/default_trait_access.rs +++ b/tests/ui/default_trait_access.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_imports)] +#![allow(unused_imports,dead_code)] #![deny(clippy::default_trait_access)] use std::default; From 77e7f8ae88910d64ebeba5689e90d7c1a01f2bf3 Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Thu, 9 Sep 2021 23:01:28 +0200 Subject: [PATCH 101/621] Added psadbw support for u8::abs_diff. --- library/core/src/num/uint_macros.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 1bba4179f9955..a5f12447fdc32 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -1504,10 +1504,16 @@ macro_rules! uint_impl { #[unstable(feature = "int_abs_diff", issue = "none")] #[inline] pub const fn abs_diff(self, other: Self) -> Self { - if self < other { - other - self + if mem::size_of::() == 1 { + // Trick LLVM into generating the psadbw instruction when SSE2 + // is available and this function is autovectorized for u8's. + (self as i32).wrapping_sub(other as i32).abs() as Self } else { - self - other + if self < other { + other - self + } else { + self - other + } } } From 5782dc0eb992a4975eca370345e1ba455d12faa4 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 9 Sep 2021 15:35:26 -0500 Subject: [PATCH 102/621] Fix redundant closure bugs --- clippy_lints/src/eta_reduction.rs | 240 +++++++----------- tests/ui/eta.fixed | 63 +++-- tests/ui/eta.rs | 55 ++-- tests/ui/eta.stderr | 66 +++-- tests/ui/map_flatten.fixed | 1 + tests/ui/map_flatten.rs | 1 + tests/ui/map_flatten.stderr | 14 +- tests/ui/semicolon_if_nothing_returned.rs | 5 +- tests/ui/semicolon_if_nothing_returned.stderr | 12 +- 9 files changed, 227 insertions(+), 230 deletions(-) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index f6a64a8ca6031..9df92cc5b6406 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -1,16 +1,16 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; -use clippy_utils::ty::{implements_trait, type_is_unsafe_function}; use clippy_utils::usage::UsedAfterExprVisitor; -use clippy_utils::{get_enclosing_loop_or_closure, higher}; -use clippy_utils::{is_adjusted, iter_input_pats}; +use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{def_id, Expr, ExprKind, Param, PatKind, QPath}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, ClosureKind, Ty}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Expr, ExprKind, Param, PatKind, Unsafety}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; +use rustc_middle::ty::subst::Subst; +use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { @@ -52,12 +52,6 @@ declare_clippy_lint! { /// ### Why is this bad? /// It's unnecessary to create the closure. /// - /// ### Known problems - /// [#3071](https://github.com/rust-lang/rust-clippy/issues/3071), - /// [#3942](https://github.com/rust-lang/rust-clippy/issues/3942), - /// [#4002](https://github.com/rust-lang/rust-clippy/issues/4002) - /// - /// /// ### Example /// ```rust,ignore /// Some('a').map(|s| s.to_uppercase()); @@ -75,32 +69,16 @@ declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_MET impl<'tcx> LateLintPass<'tcx> for EtaReduction { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if in_external_macro(cx.sess(), expr.span) { + if expr.span.from_expansion() { return; } - - match expr.kind { - ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => { - for arg in args { - // skip `foo(macro!())` - if arg.span.ctxt() == expr.span.ctxt() { - check_closure(cx, arg); - } - } - }, - _ => (), - } - } -} - -fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let ExprKind::Closure(_, decl, eid, _, _) = expr.kind { - let body = cx.tcx.hir().body(eid); - let ex = &body.value; - - if ex.span.ctxt() != expr.span.ctxt() { - if decl.inputs.is_empty() { - if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, ex) { + let body = match expr.kind { + ExprKind::Closure(_, _, id, _, _) => cx.tcx.hir().body(id), + _ => return, + }; + if body.value.span.from_expansion() { + if body.params.is_empty() { + if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, &body.value) { // replace `|| vec![]` with `Vec::new` span_lint_and_sugg( cx, @@ -117,33 +95,30 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { return; } - if_chain!( - if let ExprKind::Call(caller, args) = ex.kind; - - if let ExprKind::Path(_) = caller.kind; - - // Not the same number of arguments, there is no way the closure is the same as the function return; - if args.len() == decl.inputs.len(); - - // Are the expression or the arguments type-adjusted? Then we need the closure - if !(is_adjusted(cx, ex) || args.iter().any(|arg| is_adjusted(cx, arg))); - - let fn_ty = cx.typeck_results().expr_ty(caller); - - if matches!(fn_ty.kind(), ty::FnDef(_, _) | ty::FnPtr(_) | ty::Closure(_, _)); - - if !type_is_unsafe_function(cx, fn_ty); - - if compare_inputs(&mut iter_input_pats(decl, body), &mut args.iter()); + let closure_ty = cx.typeck_results().expr_ty(expr); + if_chain!( + if let ExprKind::Call(callee, args) = body.value.kind; + if let ExprKind::Path(_) = callee.kind; + if check_inputs(cx, body.params, args); + let callee_ty = cx.typeck_results().expr_ty_adjusted(callee); + let call_ty = cx.typeck_results().type_dependent_def_id(body.value.hir_id) + .map_or(callee_ty, |id| cx.tcx.type_of(id)); + if check_sig(cx, closure_ty, call_ty); + let substs = cx.typeck_results().node_substs(callee.hir_id); + // This fixes some false positives that I don't entirely understand + if substs.is_empty() || !cx.typeck_results().expr_ty(expr).has_late_bound_regions(); + // A type param function ref like `T::f` is not 'static, however + // it is if cast like `T::f as fn()`. This seems like a rustc bug. + if !substs.types().any(|t| matches!(t.kind(), ty::Param(_))); then { span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { - if let Some(mut snippet) = snippet_opt(cx, caller.span) { + if let Some(mut snippet) = snippet_opt(cx, callee.span) { if_chain! { - if let ty::Closure(_, substs) = fn_ty.kind(); + if let ty::Closure(_, substs) = callee_ty.peel_refs().kind(); if let ClosureKind::FnMut = substs.as_closure().kind(); - if UsedAfterExprVisitor::is_found(cx, caller) - || get_enclosing_loop_or_closure(cx.tcx, expr).is_some(); + if get_enclosing_loop_or_closure(cx.tcx, expr).is_some() + || UsedAfterExprVisitor::is_found(cx, callee); then { // Mutable closure is used after current expr; we cannot consume it. @@ -162,110 +137,79 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { ); if_chain!( - if let ExprKind::MethodCall(path, _, args, _) = ex.kind; - - // Not the same number of arguments, there is no way the closure is the same as the function return; - if args.len() == decl.inputs.len(); - - // Are the expression or the arguments type-adjusted? Then we need the closure - if !(is_adjusted(cx, ex) || args.iter().skip(1).any(|arg| is_adjusted(cx, arg))); - - let method_def_id = cx.typeck_results().type_dependent_def_id(ex.hir_id).unwrap(); - if !type_is_unsafe_function(cx, cx.tcx.type_of(method_def_id)); - - if compare_inputs(&mut iter_input_pats(decl, body), &mut args.iter()); - - if let Some(name) = get_ufcs_type_name(cx, method_def_id, &args[0]); - + if let ExprKind::MethodCall(path, _, args, _) = body.value.kind; + if check_inputs(cx, body.params, args); + let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap(); + let substs = cx.typeck_results().node_substs(body.value.hir_id); + let call_ty = cx.tcx.type_of(method_def_id).subst(cx.tcx, substs); + if check_sig(cx, closure_ty, call_ty); then { - span_lint_and_sugg( - cx, - REDUNDANT_CLOSURE_FOR_METHOD_CALLS, - expr.span, - "redundant closure", - "replace the closure with the method itself", - format!("{}::{}", name, path.ident.name), - Applicability::MachineApplicable, - ); + span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| { + let name = get_ufcs_type_name(cx, method_def_id); + diag.span_suggestion( + expr.span, + "replace the closure with the method itself", + format!("{}::{}", name, path.ident.name), + Applicability::MachineApplicable, + ); + }) } ); } } -/// Tries to determine the type for universal function call to be used instead of the closure -fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: def_id::DefId, self_arg: &Expr<'_>) -> Option { - let expected_type_of_self = &cx.tcx.fn_sig(method_def_id).inputs_and_output().skip_binder()[0]; - let actual_type_of_self = &cx.typeck_results().node_type(self_arg.hir_id); - - if let Some(trait_id) = cx.tcx.trait_of_item(method_def_id) { - if match_borrow_depth(expected_type_of_self, actual_type_of_self) - && implements_trait(cx, actual_type_of_self, trait_id, &[]) - { - return Some(cx.tcx.def_path_str(trait_id)); - } +fn check_inputs(cx: &LateContext<'_>, params: &[Param<'_>], call_args: &[Expr<'_>]) -> bool { + if params.len() != call_args.len() { + return false; } - - cx.tcx.impl_of_method(method_def_id).and_then(|_| { - //a type may implicitly implement other type's methods (e.g. Deref) - if match_types(expected_type_of_self, actual_type_of_self) { - Some(get_type_name(cx, actual_type_of_self)) - } else { - None + std::iter::zip(params, call_args).all(|(param, arg)| { + match param.pat.kind { + PatKind::Binding(_, id, ..) if path_to_local_id(arg, id) => {}, + _ => return false, + } + match *cx.typeck_results().expr_adjustments(arg) { + [] => true, + [Adjustment { + kind: Adjust::Deref(None), + .. + }, Adjustment { + kind: Adjust::Borrow(AutoBorrow::Ref(_, mu2)), + .. + }] => { + // re-borrow with the same mutability is allowed + let ty = cx.typeck_results().expr_ty(arg); + matches!(*ty.kind(), ty::Ref(.., mu1) if mu1 == mu2.into()) + }, + _ => false, } }) } -fn match_borrow_depth(lhs: Ty<'_>, rhs: Ty<'_>) -> bool { - match (&lhs.kind(), &rhs.kind()) { - (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_borrow_depth(t1, t2), - (l, r) => !matches!((l, r), (ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _))), - } -} - -fn match_types(lhs: Ty<'_>, rhs: Ty<'_>) -> bool { - match (&lhs.kind(), &rhs.kind()) { - (ty::Bool, ty::Bool) - | (ty::Char, ty::Char) - | (ty::Int(_), ty::Int(_)) - | (ty::Uint(_), ty::Uint(_)) - | (ty::Str, ty::Str) => true, - (ty::Ref(_, t1, mut1), ty::Ref(_, t2, mut2)) => mut1 == mut2 && match_types(t1, t2), - (ty::Array(t1, _), ty::Array(t2, _)) | (ty::Slice(t1), ty::Slice(t2)) => match_types(t1, t2), - (ty::Adt(def1, _), ty::Adt(def2, _)) => def1 == def2, - (_, _) => false, +fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tcx>) -> bool { + let call_sig = call_ty.fn_sig(cx.tcx); + if call_sig.unsafety() == Unsafety::Unsafe { + return false; } -} - -fn get_type_name(cx: &LateContext<'_>, ty: Ty<'_>) -> String { - match ty.kind() { - ty::Adt(t, _) => cx.tcx.def_path_str(t.did), - ty::Ref(_, r, _) => get_type_name(cx, r), - _ => ty.to_string(), + if !closure_ty.has_late_bound_regions() { + return true; } + let substs = match closure_ty.kind() { + ty::Closure(_, substs) => substs, + _ => return false, + }; + let closure_sig = cx.tcx.signature_unclosure(substs.as_closure().sig(), Unsafety::Normal); + cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig) } -fn compare_inputs( - closure_inputs: &mut dyn Iterator>, - call_args: &mut dyn Iterator>, -) -> bool { - for (closure_input, function_arg) in closure_inputs.zip(call_args) { - if let PatKind::Binding(_, _, ident, _) = closure_input.pat.kind { - // XXXManishearth Should I be checking the binding mode here? - if let ExprKind::Path(QPath::Resolved(None, p)) = function_arg.kind { - if p.segments.len() != 1 { - // If it's a proper path, it can't be a local variable - return false; - } - if p.segments[0].ident.name != ident.name { - // The two idents should be the same - return false; - } - } else { - return false; +fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String { + match cx.tcx.associated_item(method_def_id).container { + ty::TraitContainer(def_id) => cx.tcx.def_path_str(def_id), + ty::ImplContainer(def_id) => { + let ty = cx.tcx.type_of(def_id); + match ty.kind() { + ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did), + _ => ty.to_string(), } - } else { - return false; - } + }, } - true } diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 91b837f9a8588..9d9a1a3e5003f 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -14,7 +14,7 @@ clippy::needless_borrow )] -use std::path::PathBuf; +use std::path::{Path, PathBuf}; macro_rules! mac { () => { @@ -30,19 +30,18 @@ macro_rules! closure_mac { fn main() { let a = Some(1u8).map(foo); - meta(foo); let c = Some(1u8).map(|a| {1+2; foo}(a)); true.then(|| mac!()); // don't lint function in macro expansion Some(1).map(closure_mac!()); // don't lint closure in macro expansion let _: Option> = true.then(std::vec::Vec::new); // special case vec! - let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted? - all(&[1, 2, 3], &2, |x, y| below(x, y)); //is adjusted + let d = Some(1u8).map(|a| foo(foo2(a))); //is adjusted? + all(&[1, 2, 3], &2, below); //is adjusted unsafe { Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn } // See #815 - let e = Some(1u8).map(|a| divergent(a)); + let e = Some(1u8).map(divergent); let e = Some(1u8).map(generic); let e = Some(1u8).map(generic); // See #515 @@ -90,24 +89,17 @@ impl<'a> std::ops::Deref for TestStruct<'a> { fn test_redundant_closures_containing_method_calls() { let i = 10; let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo); - let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo); let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo); let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref()); - let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo); - let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear); let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear); unsafe { let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe()); } let e = Some("str").map(std::string::ToString::to_string); - let e = Some("str").map(str::to_string); - let e = Some('a').map(char::to_uppercase); let e = Some('a').map(char::to_uppercase); let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect(); let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect(); - let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect(); - let p = Some(PathBuf::new()); - let e = p.as_ref().and_then(|s| s.to_str()); + let e = Some(PathBuf::new()).as_ref().and_then(|s| s.to_str()); let c = Some(TestStruct { some_ref: &i }) .as_ref() .map(|c| c.to_ascii_uppercase()); @@ -119,10 +111,6 @@ fn test_redundant_closures_containing_method_calls() { t.iter().filter(|x| x.trait_foo_ref()); t.iter().map(|x| x.trait_foo_ref()); } - - let mut some = Some(|x| x * x); - let arr = [Ok(1), Err(2)]; - let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect(); } struct Thunk(Box T>); @@ -145,13 +133,6 @@ fn foobar() { thunk.unwrap() } -fn meta(f: F) -where - F: Fn(u8), -{ - f(1u8) -} - fn foo(_: u8) {} fn foo2(_: u8) -> u8 { @@ -180,7 +161,7 @@ fn generic(_: T) -> u8 { } fn passes_fn_mut(mut x: Box) { - requires_fn_once(|| x()); + requires_fn_once(x); } fn requires_fn_once(_: T) {} @@ -236,3 +217,35 @@ fn mutable_closure_in_loop() { Some(1).map(&mut closure); } } + +fn late_bound_lifetimes() { + fn take_asref_path>(path: P) {} + + fn map_str(thunk: F) + where + F: FnOnce(&str), + { + } + + fn map_str_to_path(thunk: F) + where + F: FnOnce(&str) -> &Path, + { + } + map_str(|s| take_asref_path(s)); + map_str_to_path(std::convert::AsRef::as_ref); +} + +mod type_param_bound { + trait Trait { + fn fun(); + } + + fn take(_: T) {} + + fn test() { + // don't lint, but it's questionable that rust requires a cast + take(|| X::fun()); + take(X::fun as fn()); + } +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index 1b53700289db3..3b53b9b28eb14 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -14,7 +14,7 @@ clippy::needless_borrow )] -use std::path::PathBuf; +use std::path::{Path, PathBuf}; macro_rules! mac { () => { @@ -30,7 +30,6 @@ macro_rules! closure_mac { fn main() { let a = Some(1u8).map(|a| foo(a)); - meta(|a| foo(a)); let c = Some(1u8).map(|a| {1+2; foo}(a)); true.then(|| mac!()); // don't lint function in macro expansion Some(1).map(closure_mac!()); // don't lint closure in macro expansion @@ -90,24 +89,17 @@ impl<'a> std::ops::Deref for TestStruct<'a> { fn test_redundant_closures_containing_method_calls() { let i = 10; let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); - let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo); let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref()); - let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo); let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); - let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear); unsafe { let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe()); } let e = Some("str").map(|s| s.to_string()); - let e = Some("str").map(str::to_string); let e = Some('a').map(|s| s.to_uppercase()); - let e = Some('a').map(char::to_uppercase); let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect(); let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); - let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect(); - let p = Some(PathBuf::new()); - let e = p.as_ref().and_then(|s| s.to_str()); + let e = Some(PathBuf::new()).as_ref().and_then(|s| s.to_str()); let c = Some(TestStruct { some_ref: &i }) .as_ref() .map(|c| c.to_ascii_uppercase()); @@ -119,10 +111,6 @@ fn test_redundant_closures_containing_method_calls() { t.iter().filter(|x| x.trait_foo_ref()); t.iter().map(|x| x.trait_foo_ref()); } - - let mut some = Some(|x| x * x); - let arr = [Ok(1), Err(2)]; - let _: Vec<_> = arr.iter().map(|x| x.map_err(|e| some.take().unwrap()(e))).collect(); } struct Thunk(Box T>); @@ -145,13 +133,6 @@ fn foobar() { thunk.unwrap() } -fn meta(f: F) -where - F: Fn(u8), -{ - f(1u8) -} - fn foo(_: u8) {} fn foo2(_: u8) -> u8 { @@ -236,3 +217,35 @@ fn mutable_closure_in_loop() { Some(1).map(|n| closure(n)); } } + +fn late_bound_lifetimes() { + fn take_asref_path>(path: P) {} + + fn map_str(thunk: F) + where + F: FnOnce(&str), + { + } + + fn map_str_to_path(thunk: F) + where + F: FnOnce(&str) -> &Path, + { + } + map_str(|s| take_asref_path(s)); + map_str_to_path(|s| s.as_ref()); +} + +mod type_param_bound { + trait Trait { + fn fun(); + } + + fn take(_: T) {} + + fn test() { + // don't lint, but it's questionable that rust requires a cast + take(|| X::fun()); + take(X::fun as fn()); + } +} diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index 28da894134619..48d7e9e9c9626 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -7,19 +7,19 @@ LL | let a = Some(1u8).map(|a| foo(a)); = note: `-D clippy::redundant-closure` implied by `-D warnings` error: redundant closure - --> $DIR/eta.rs:33:10 + --> $DIR/eta.rs:36:40 | -LL | meta(|a| foo(a)); - | ^^^^^^^^^^ help: replace the closure with the function itself: `foo` +LL | let _: Option> = true.then(|| vec![]); // special case vec! + | ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new` error: redundant closure - --> $DIR/eta.rs:37:40 + --> $DIR/eta.rs:37:35 | -LL | let _: Option> = true.then(|| vec![]); // special case vec! - | ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new` +LL | let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted? + | ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2` error: this expression borrows a reference (`&u8`) that is immediately dereferenced by the compiler - --> $DIR/eta.rs:39:21 + --> $DIR/eta.rs:38:21 | LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted | ^^^ help: change this to: `&2` @@ -27,13 +27,25 @@ LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted = note: `-D clippy::needless-borrow` implied by `-D warnings` error: redundant closure - --> $DIR/eta.rs:46:27 + --> $DIR/eta.rs:38:26 + | +LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted + | ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below` + +error: redundant closure + --> $DIR/eta.rs:44:27 + | +LL | let e = Some(1u8).map(|a| divergent(a)); + | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `divergent` + +error: redundant closure + --> $DIR/eta.rs:45:27 | LL | let e = Some(1u8).map(|a| generic(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic` error: redundant closure - --> $DIR/eta.rs:92:51 + --> $DIR/eta.rs:91:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo` @@ -41,70 +53,82 @@ LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); = note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings` error: redundant closure - --> $DIR/eta.rs:94:51 + --> $DIR/eta.rs:92:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo` error: redundant closure - --> $DIR/eta.rs:97:42 + --> $DIR/eta.rs:94:42 | LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); | ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear` error: redundant closure - --> $DIR/eta.rs:102:29 + --> $DIR/eta.rs:98:29 | LL | let e = Some("str").map(|s| s.to_string()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string` error: redundant closure - --> $DIR/eta.rs:104:27 + --> $DIR/eta.rs:99:27 | LL | let e = Some('a').map(|s| s.to_uppercase()); | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase` error: redundant closure - --> $DIR/eta.rs:107:65 + --> $DIR/eta.rs:101:65 | LL | let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase` error: redundant closure - --> $DIR/eta.rs:190:27 + --> $DIR/eta.rs:164:22 + | +LL | requires_fn_once(|| x()); + | ^^^^^^ help: replace the closure with the function itself: `x` + +error: redundant closure + --> $DIR/eta.rs:171:27 | LL | let a = Some(1u8).map(|a| foo_ptr(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr` error: redundant closure - --> $DIR/eta.rs:195:27 + --> $DIR/eta.rs:176:27 | LL | let a = Some(1u8).map(|a| closure(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure` error: redundant closure - --> $DIR/eta.rs:227:28 + --> $DIR/eta.rs:208:28 | LL | x.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> $DIR/eta.rs:228:28 + --> $DIR/eta.rs:209:28 | LL | y.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> $DIR/eta.rs:229:28 + --> $DIR/eta.rs:210:28 | LL | z.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res` error: redundant closure - --> $DIR/eta.rs:236:21 + --> $DIR/eta.rs:217:21 | LL | Some(1).map(|n| closure(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure` -error: aborting due to 17 previous errors +error: redundant closure + --> $DIR/eta.rs:236:21 + | +LL | map_str_to_path(|s| s.as_ref()); + | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::convert::AsRef::as_ref` + +error: aborting due to 21 previous errors diff --git a/tests/ui/map_flatten.fixed b/tests/ui/map_flatten.fixed index 18846c898da05..fec3a95edd62d 100644 --- a/tests/ui/map_flatten.fixed +++ b/tests/ui/map_flatten.fixed @@ -4,6 +4,7 @@ #![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] +#![allow(clippy::redundant_closure)] #![allow(clippy::unnecessary_wraps)] #![feature(result_flattening)] diff --git a/tests/ui/map_flatten.rs b/tests/ui/map_flatten.rs index 01db27876da70..aa1f76e335af0 100644 --- a/tests/ui/map_flatten.rs +++ b/tests/ui/map_flatten.rs @@ -4,6 +4,7 @@ #![allow(clippy::let_underscore_drop)] #![allow(clippy::missing_docs_in_private_items)] #![allow(clippy::map_identity)] +#![allow(clippy::redundant_closure)] #![allow(clippy::unnecessary_wraps)] #![feature(result_flattening)] diff --git a/tests/ui/map_flatten.stderr b/tests/ui/map_flatten.stderr index 38457c8ea4dd4..bcd2047e6faa3 100644 --- a/tests/ui/map_flatten.stderr +++ b/tests/ui/map_flatten.stderr @@ -1,5 +1,5 @@ error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:17:46 + --> $DIR/map_flatten.rs:18:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id)` @@ -7,37 +7,37 @@ LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().coll = note: `-D clippy::map-flatten` implied by `-D warnings` error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:18:46 + --> $DIR/map_flatten.rs:19:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_ref)` error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:19:46 + --> $DIR/map_flatten.rs:20:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(option_id_closure)` error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:20:46 + --> $DIR/map_flatten.rs:21:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `filter_map` instead: `.filter_map(|x| x.checked_add(1))` error: called `map(..).flatten()` on an `Iterator` - --> $DIR/map_flatten.rs:23:46 + --> $DIR/map_flatten.rs:24:46 | LL | let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `flat_map` instead: `.flat_map(|x| 0..x)` error: called `map(..).flatten()` on an `Option` - --> $DIR/map_flatten.rs:26:39 + --> $DIR/map_flatten.rs:27:39 | LL | let _: Option<_> = (Some(Some(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)` error: called `map(..).flatten()` on an `Result` - --> $DIR/map_flatten.rs:29:41 + --> $DIR/map_flatten.rs:30:41 | LL | let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten(); | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `.and_then(|x| x)` diff --git a/tests/ui/semicolon_if_nothing_returned.rs b/tests/ui/semicolon_if_nothing_returned.rs index 79ba7402f1f49..9644a23296831 100644 --- a/tests/ui/semicolon_if_nothing_returned.rs +++ b/tests/ui/semicolon_if_nothing_returned.rs @@ -1,4 +1,5 @@ #![warn(clippy::semicolon_if_nothing_returned)] +#![allow(clippy::redundant_closure)] #![feature(label_break_value)] fn get_unit() {} @@ -30,8 +31,8 @@ fn unsafe_checks_error() { use std::ptr; let mut s = MaybeUninit::::uninit(); - let _d = || unsafe { - ptr::drop_in_place(s.as_mut_ptr()) + let _d = || unsafe { + ptr::drop_in_place(s.as_mut_ptr()) }; } diff --git a/tests/ui/semicolon_if_nothing_returned.stderr b/tests/ui/semicolon_if_nothing_returned.stderr index e88ebe2ad35f0..78813e7cc1c39 100644 --- a/tests/ui/semicolon_if_nothing_returned.stderr +++ b/tests/ui/semicolon_if_nothing_returned.stderr @@ -1,5 +1,5 @@ error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:8:5 + --> $DIR/semicolon_if_nothing_returned.rs:9:5 | LL | println!("Hello") | ^^^^^^^^^^^^^^^^^ help: add a `;` here: `println!("Hello");` @@ -7,27 +7,27 @@ LL | println!("Hello") = note: `-D clippy::semicolon-if-nothing-returned` implied by `-D warnings` error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:12:5 + --> $DIR/semicolon_if_nothing_returned.rs:13:5 | LL | get_unit() | ^^^^^^^^^^ help: add a `;` here: `get_unit();` error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:17:5 + --> $DIR/semicolon_if_nothing_returned.rs:18:5 | LL | y = x + 1 | ^^^^^^^^^ help: add a `;` here: `y = x + 1;` error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:23:9 + --> $DIR/semicolon_if_nothing_returned.rs:24:9 | LL | hello() | ^^^^^^^ help: add a `;` here: `hello();` error: consider adding a `;` to the last statement for consistent formatting - --> $DIR/semicolon_if_nothing_returned.rs:34:9 + --> $DIR/semicolon_if_nothing_returned.rs:35:9 | -LL | ptr::drop_in_place(s.as_mut_ptr()) +LL | ptr::drop_in_place(s.as_mut_ptr()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `ptr::drop_in_place(s.as_mut_ptr());` error: aborting due to 5 previous errors From ae2a95fadc098b326f2114fc5ee6f55ee0db81a8 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 9 Sep 2021 15:36:06 -0500 Subject: [PATCH 103/621] Eat dogfood --- clippy_lints/src/disallowed_type.rs | 2 +- clippy_lints/src/format.rs | 4 +- clippy_lints/src/loops/while_let_loop.rs | 2 +- clippy_lints/src/ptr.rs | 2 +- clippy_utils/src/ast_utils.rs | 85 +++++++++++------------- clippy_utils/src/eager_or_lazy.rs | 7 +- 6 files changed, 45 insertions(+), 57 deletions(-) diff --git a/clippy_lints/src/disallowed_type.rs b/clippy_lints/src/disallowed_type.rs index e627168b93275..6c861fb33a978 100644 --- a/clippy_lints/src/disallowed_type.rs +++ b/clippy_lints/src/disallowed_type.rs @@ -48,7 +48,7 @@ impl DisallowedType { Self { disallowed: disallowed .iter() - .map(|s| s.split("::").map(|seg| Symbol::intern(seg)).collect::>()) + .map(|s| s.split("::").map(Symbol::intern).collect::>()) .collect(), def_ids: FxHashSet::default(), prim_tys: FxHashSet::default(), diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 863c606f5a92c..508cac33848f7 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -69,8 +69,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { ty::Str => true, _ => false, }; - if format_args.args.iter().all(|e| is_display_arg(e)); - if format_args.fmt_expr.map_or(true, |e| check_unformatted(e)); + if format_args.args.iter().all(is_display_arg); + if format_args.fmt_expr.map_or(true, check_unformatted); then { let is_new_string = match value.kind { ExprKind::Binary(..) => true, diff --git a/clippy_lints/src/loops/while_let_loop.rs b/clippy_lints/src/loops/while_let_loop.rs index 1848f5b5de2f2..4dcd5c87722ee 100644 --- a/clippy_lints/src/loops/while_let_loop.rs +++ b/clippy_lints/src/loops/while_let_loop.rs @@ -65,7 +65,7 @@ fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> { fn is_simple_break_expr(expr: &Expr<'_>) -> bool { match expr.kind { ExprKind::Break(dest, ref passed_expr) if dest.label.is_none() && passed_expr.is_none() => true, - ExprKind::Block(b, _) => extract_first_expr(b).map_or(false, |subexpr| is_simple_break_expr(subexpr)), + ExprKind::Block(b, _) => extract_first_expr(b).map_or(false, is_simple_break_expr), _ => false, } } diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index c0d1f1eb6e65e..d696e17d656d4 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -372,7 +372,7 @@ fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: for (_, ref mutbl, ref argspan) in decl .inputs .iter() - .filter_map(|ty| get_rptr_lm(ty)) + .filter_map(get_rptr_lm) .filter(|&(lt, _, _)| lt.name == out.name) { if *mutbl == Mutability::Mut { diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 133f6c29f7d21..2fa98831c7740 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -46,15 +46,12 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool { | (Ref(l, Mutability::Not), Ref(r, Mutability::Not)) | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r), (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)), - (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp), + (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp), (TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => { eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r)) }, (Struct(lqself, lp, lfs, lr), Struct(rqself, rp, rfs, rr)) => { - lr == rr - && eq_maybe_qself(lqself, rqself) - && eq_path(lp, rp) - && unordered_over(lfs, rfs, |lf, rf| eq_field_pat(lf, rf)) + lr == rr && eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && unordered_over(lfs, rfs, eq_field_pat) }, (Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)), (MacCall(l), MacCall(r)) => eq_mac_call(l, r), @@ -76,7 +73,7 @@ pub fn eq_field_pat(l: &PatField, r: &PatField) -> bool { l.is_placeholder == r.is_placeholder && eq_id(l.ident, r.ident) && eq_pat(&l.pat, &r.pat) - && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) + && over(&l.attrs, &r.attrs, eq_attr) } pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool { @@ -92,7 +89,7 @@ pub fn eq_maybe_qself(l: &Option, r: &Option) -> bool { } pub fn eq_path(l: &Path, r: &Path) -> bool { - over(&l.segments, &r.segments, |l, r| eq_path_seg(l, r)) + over(&l.segments, &r.segments, eq_path_seg) } pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool { @@ -101,9 +98,7 @@ pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool { pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool { match (l, r) { - (GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => { - over(&l.args, &r.args, |l, r| eq_angle_arg(l, r)) - }, + (GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => over(&l.args, &r.args, eq_angle_arg), (GenericArgs::Parenthesized(l), GenericArgs::Parenthesized(r)) => { over(&l.inputs, &r.inputs, |l, r| eq_ty(l, r)) && eq_fn_ret_ty(&l.output, &r.output) }, @@ -142,7 +137,7 @@ pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool { pub fn eq_expr(l: &Expr, r: &Expr) -> bool { use ExprKind::*; - if !over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) { + if !over(&l.attrs, &r.attrs, eq_attr) { return false; } match (&l.kind, &r.kind) { @@ -173,20 +168,20 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2), Index(r1, r2)) => eq_expr(l1, r1) && eq_expr(l2, r2), (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv), (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp), - (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, |l, r| eq_arm(l, r)), + (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm), (Closure(lc, la, lm, lf, lb, _), Closure(rc, ra, rm, rf, rb, _)) => { lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(lb, rb) }, (Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb), (Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt), (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re), - (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp), + (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp), (MacCall(l), MacCall(r)) => eq_mac_call(l, r), (Struct(lse), Struct(rse)) => { eq_maybe_qself(&lse.qself, &rse.qself) && eq_path(&lse.path, &rse.path) && eq_struct_rest(&lse.rest, &rse.rest) - && unordered_over(&lse.fields, &rse.fields, |l, r| eq_field(l, r)) + && unordered_over(&lse.fields, &rse.fields, eq_field) }, _ => false, } @@ -196,7 +191,7 @@ pub fn eq_field(l: &ExprField, r: &ExprField) -> bool { l.is_placeholder == r.is_placeholder && eq_id(l.ident, r.ident) && eq_expr(&l.expr, &r.expr) - && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) + && over(&l.attrs, &r.attrs, eq_attr) } pub fn eq_arm(l: &Arm, r: &Arm) -> bool { @@ -204,7 +199,7 @@ pub fn eq_arm(l: &Arm, r: &Arm) -> bool { && eq_pat(&l.pat, &r.pat) && eq_expr(&l.body, &r.body) && eq_expr_opt(&l.guard, &r.guard) - && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) + && over(&l.attrs, &r.attrs, eq_attr) } pub fn eq_label(l: &Option