From 656a38830b5228d5a47222c8f3da2a8346ccab0b Mon Sep 17 00:00:00 2001 From: zetanumbers Date: Thu, 18 Jan 2024 14:28:39 +0300 Subject: [PATCH 0001/1468] Add `A: 'static` bound for `Arc/Rc::pin_in` --- library/alloc/src/rc.rs | 5 ++++- library/alloc/src/sync.rs | 10 ++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 263b1449de156..a32a0271adfe7 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -883,7 +883,10 @@ impl Rc { #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] #[inline] - pub fn pin_in(value: T, alloc: A) -> Pin { + pub fn pin_in(value: T, alloc: A) -> Pin + where + A: 'static, + { unsafe { Pin::new_unchecked(Rc::new_in(value, alloc)) } } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 5273b3cb2dafa..c389066e6b62e 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -801,7 +801,10 @@ impl Arc { #[cfg(not(no_global_oom_handling))] #[unstable(feature = "allocator_api", issue = "32838")] #[inline] - pub fn pin_in(data: T, alloc: A) -> Pin> { + pub fn pin_in(data: T, alloc: A) -> Pin> + where + A: 'static, + { unsafe { Pin::new_unchecked(Arc::new_in(data, alloc)) } } @@ -809,7 +812,10 @@ impl Arc { /// fails. #[inline] #[unstable(feature = "allocator_api", issue = "32838")] - pub fn try_pin_in(data: T, alloc: A) -> Result>, AllocError> { + pub fn try_pin_in(data: T, alloc: A) -> Result>, AllocError> + where + A: 'static, + { unsafe { Ok(Pin::new_unchecked(Arc::try_new_in(data, alloc)?)) } } From 67c03579bc45a280548499d37ba2db7ef2e9f6a3 Mon Sep 17 00:00:00 2001 From: Daniel Sedlak Date: Fri, 2 Feb 2024 10:13:22 +0100 Subject: [PATCH 0002/1468] Stabilize slice_split_at_unchecked --- library/core/src/lib.rs | 1 - library/core/src/slice/mod.rs | 8 ++------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index b54680a61b4d8..ee41056eaac74 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -186,7 +186,6 @@ #![feature(ptr_metadata)] #![feature(set_ptr_value)] #![feature(slice_ptr_get)] -#![feature(slice_split_at_unchecked)] #![feature(split_at_checked)] #![feature(str_internals)] #![feature(str_split_inclusive_remainder)] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 73e92ed1dad63..e21c6bd797a6b 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -1928,8 +1928,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_split_at_unchecked)] - /// /// let v = [1, 2, 3, 4, 5, 6]; /// /// unsafe { @@ -1950,7 +1948,7 @@ impl [T] { /// assert_eq!(right, []); /// } /// ``` - #[unstable(feature = "slice_split_at_unchecked", reason = "new API", issue = "76014")] + #[stable(feature = "slice_split_at_unchecked", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_stable(feature = "const_slice_split_at_unchecked", since = "1.77.0")] #[inline] #[must_use] @@ -1991,8 +1989,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_split_at_unchecked)] - /// /// let mut v = [1, 0, 3, 0, 5, 6]; /// // scoped to restrict the lifetime of the borrows /// unsafe { @@ -2004,7 +2000,7 @@ impl [T] { /// } /// assert_eq!(v, [1, 2, 3, 4, 5, 6]); /// ``` - #[unstable(feature = "slice_split_at_unchecked", reason = "new API", issue = "76014")] + #[stable(feature = "slice_split_at_unchecked", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_slice_split_at_mut", issue = "101804")] #[inline] #[must_use] From 679b90c2954102099d4876d59e94219e544964ce Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 20 Feb 2024 14:41:55 +0100 Subject: [PATCH 0003/1468] Propagate temporary lifetime extension into if and match. --- compiler/rustc_hir_analysis/src/check/region.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 0f5fd7e99b776..e16d61bed2e9d 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -689,6 +689,8 @@ fn resolve_local<'tcx>( /// | [ ..., E&, ... ] /// | ( ..., E&, ... ) /// | {...; E&} + /// | if _ { ...; E& } else { ...; E& } + /// | match _ { ..., _ => E&, ... } /// | box E& /// | E& as ... /// | ( E& ) @@ -727,6 +729,17 @@ fn resolve_local<'tcx>( record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id); } } + hir::ExprKind::If(_, then_block, else_block) => { + record_rvalue_scope_if_borrow_expr(visitor, then_block, blk_id); + if let Some(else_block) = else_block { + record_rvalue_scope_if_borrow_expr(visitor, else_block, blk_id); + } + } + hir::ExprKind::Match(_, arms, _) => { + for arm in arms { + record_rvalue_scope_if_borrow_expr(visitor, arm.body, blk_id); + } + } hir::ExprKind::Call(..) | hir::ExprKind::MethodCall(..) => { // FIXME(@dingxiangfei2009): choose call arguments here // for candidacy for extended parameter rule application From 476faa219641878df1ed95db6ed138b49cf6f348 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 20 Feb 2024 14:58:17 +0100 Subject: [PATCH 0004/1468] Update test. --- tests/ui/borrowck/let_underscore_temporary.rs | 30 ++-- .../borrowck/let_underscore_temporary.stderr | 158 ++++++------------ 2 files changed, 73 insertions(+), 115 deletions(-) diff --git a/tests/ui/borrowck/let_underscore_temporary.rs b/tests/ui/borrowck/let_underscore_temporary.rs index 0a24df08925f8..d1bdabd4ecac4 100644 --- a/tests/ui/borrowck/let_underscore_temporary.rs +++ b/tests/ui/borrowck/let_underscore_temporary.rs @@ -7,8 +7,9 @@ fn let_underscore(string: &Option<&str>, mut num: Option) { *s += 1; s } else { - &mut 0 - //~^ ERROR temporary value dropped while borrowed + let a = 0; + &a + //~^ ERROR does not live long enough }; let _ = if let Some(ref s) = num { s } else { &0 }; let _ = if let Some(mut s) = num { @@ -21,8 +22,9 @@ fn let_underscore(string: &Option<&str>, mut num: Option) { *s += 1; s } else { - &mut 0 - //~^ ERROR temporary value dropped while borrowed + let a = 0; + &a + //~^ ERROR does not live long enough }; } @@ -33,8 +35,9 @@ fn let_ascribe(string: &Option<&str>, mut num: Option) { *s += 1; s } else { - &mut 0 - //~^ ERROR temporary value dropped while borrowed + let a = 0; + &a + //~^ ERROR does not live long enough }; let _: _ = if let Some(ref s) = num { s } else { &0 }; let _: _ = if let Some(mut s) = num { @@ -47,8 +50,9 @@ fn let_ascribe(string: &Option<&str>, mut num: Option) { *s += 1; s } else { - &mut 0 - //~^ ERROR temporary value dropped while borrowed + let a = 0; + &a + //~^ ERROR does not live long enough }; } @@ -63,8 +67,9 @@ fn matched(string: &Option<&str>, mut num: Option) { *s += 1; s } else { - &mut 0 - //~^ ERROR temporary value dropped while borrowed + let a = 0; + &a + //~^ ERROR does not live long enough } { _ => {} }; @@ -83,8 +88,9 @@ fn matched(string: &Option<&str>, mut num: Option) { *s += 1; s } else { - &mut 0 - //~^ ERROR temporary value dropped while borrowed + let a = 0; + &a + //~^ ERROR does not live long enough } { _ => {} }; diff --git a/tests/ui/borrowck/let_underscore_temporary.stderr b/tests/ui/borrowck/let_underscore_temporary.stderr index 6bccf329181f0..90b3462ebf8c3 100644 --- a/tests/ui/borrowck/let_underscore_temporary.stderr +++ b/tests/ui/borrowck/let_underscore_temporary.stderr @@ -1,117 +1,69 @@ -error[E0716]: temporary value dropped while borrowed - --> $DIR/let_underscore_temporary.rs:10:14 +error[E0597]: `a` does not live long enough + --> $DIR/let_underscore_temporary.rs:11:9 | -LL | let _ = if let Some(s) = &mut num { - | _____________- -LL | | *s += 1; -LL | | s -LL | | } else { -LL | | &mut 0 - | | ^ creates a temporary value which is freed while still in use -LL | | -LL | | }; - | | - - | | | - | |_____temporary value is freed at the end of this statement - | borrow later used here - | - = note: consider using a `let` binding to create a longer lived value +LL | let a = 0; + | - binding `a` declared here +LL | &a + | ^^ borrowed value does not live long enough +LL | +LL | }; + | - `a` dropped here while still borrowed -error[E0716]: temporary value dropped while borrowed - --> $DIR/let_underscore_temporary.rs:24:14 - | -LL | let _ = if let Some(ref mut s) = num { - | _____________- -LL | | *s += 1; -LL | | s -LL | | } else { -LL | | &mut 0 - | | ^ creates a temporary value which is freed while still in use -LL | | -LL | | }; - | | - - | | | - | |_____temporary value is freed at the end of this statement - | borrow later used here +error[E0597]: `a` does not live long enough + --> $DIR/let_underscore_temporary.rs:26:9 | - = note: consider using a `let` binding to create a longer lived value +LL | let a = 0; + | - binding `a` declared here +LL | &a + | ^^ borrowed value does not live long enough +LL | +LL | }; + | - `a` dropped here while still borrowed -error[E0716]: temporary value dropped while borrowed - --> $DIR/let_underscore_temporary.rs:36:14 - | -LL | let _: _ = if let Some(s) = &mut num { - | ________________- -LL | | *s += 1; -LL | | s -LL | | } else { -LL | | &mut 0 - | | ^ creates a temporary value which is freed while still in use -LL | | -LL | | }; - | | - - | | | - | |_____temporary value is freed at the end of this statement - | borrow later used here +error[E0597]: `a` does not live long enough + --> $DIR/let_underscore_temporary.rs:39:9 | - = note: consider using a `let` binding to create a longer lived value +LL | let a = 0; + | - binding `a` declared here +LL | &a + | ^^ borrowed value does not live long enough +LL | +LL | }; + | - `a` dropped here while still borrowed -error[E0716]: temporary value dropped while borrowed - --> $DIR/let_underscore_temporary.rs:50:14 +error[E0597]: `a` does not live long enough + --> $DIR/let_underscore_temporary.rs:54:9 | -LL | let _: _ = if let Some(ref mut s) = num { - | ________________- -LL | | *s += 1; -LL | | s -LL | | } else { -LL | | &mut 0 - | | ^ creates a temporary value which is freed while still in use -LL | | -LL | | }; - | | - - | | | - | |_____temporary value is freed at the end of this statement - | borrow later used here - | - = note: consider using a `let` binding to create a longer lived value +LL | let a = 0; + | - binding `a` declared here +LL | &a + | ^^ borrowed value does not live long enough +LL | +LL | }; + | - `a` dropped here while still borrowed -error[E0716]: temporary value dropped while borrowed - --> $DIR/let_underscore_temporary.rs:66:14 - | -LL | match if let Some(s) = &mut num { - | ___________- -LL | | *s += 1; -LL | | s -LL | | } else { -LL | | &mut 0 - | | ^ creates a temporary value which is freed while still in use -LL | | -LL | | } { - | | - - | | | - | |_____temporary value is freed at the end of this statement - | borrow later used here +error[E0597]: `a` does not live long enough + --> $DIR/let_underscore_temporary.rs:71:9 | - = note: consider using a `let` binding to create a longer lived value +LL | let a = 0; + | - binding `a` declared here +LL | &a + | ^^ borrowed value does not live long enough +LL | +LL | } { + | - `a` dropped here while still borrowed -error[E0716]: temporary value dropped while borrowed - --> $DIR/let_underscore_temporary.rs:86:14 - | -LL | match if let Some(ref mut s) = num { - | ___________- -LL | | *s += 1; -LL | | s -LL | | } else { -LL | | &mut 0 - | | ^ creates a temporary value which is freed while still in use -LL | | -LL | | } { - | | - - | | | - | |_____temporary value is freed at the end of this statement - | borrow later used here +error[E0597]: `a` does not live long enough + --> $DIR/let_underscore_temporary.rs:92:9 | - = note: consider using a `let` binding to create a longer lived value +LL | let a = 0; + | - binding `a` declared here +LL | &a + | ^^ borrowed value does not live long enough +LL | +LL | } { + | - `a` dropped here while still borrowed error: aborting due to 6 previous errors -For more information about this error, try `rustc --explain E0716`. +For more information about this error, try `rustc --explain E0597`. From bec765e5079c09fe9f32119d4f8b7a3ff818f4f2 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 20 Feb 2024 15:19:16 +0100 Subject: [PATCH 0005/1468] Add test. --- .../lifetimes/temporary-lifetime-extension.rs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/ui/lifetimes/temporary-lifetime-extension.rs diff --git a/tests/ui/lifetimes/temporary-lifetime-extension.rs b/tests/ui/lifetimes/temporary-lifetime-extension.rs new file mode 100644 index 0000000000000..477801f327344 --- /dev/null +++ b/tests/ui/lifetimes/temporary-lifetime-extension.rs @@ -0,0 +1,29 @@ +//@ check-pass + +fn temp() -> (String, i32) { + (String::from("Hello"), 1) +} + +fn main() { + let a = &temp(); + let b = [(&temp(),)]; + let c = &temp().0; + let d = &temp().0[..]; + let e = { + let _ = 123; + &(*temp().0)[..] + }; + let f = if true { + &temp() + } else { + &temp() + }; + let g = match true { + true => &temp(), + false => { + let _ = 123; + &temp() + } + }; + println!("{a:?} {b:?} {c:?} {d:?} {e:?} {f:?} {g:?}"); +} From e6d3e0cf99dcde0dd660cea52998fa11c79f36d1 Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 20 Feb 2024 16:22:04 +0100 Subject: [PATCH 0006/1468] Extend temporary lifetime extension test. Co-authored-by: Daniel Henry-Mantilla --- tests/ui/lifetimes/temporary-lifetime-extension.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/ui/lifetimes/temporary-lifetime-extension.rs b/tests/ui/lifetimes/temporary-lifetime-extension.rs index 477801f327344..193ca0b4d0733 100644 --- a/tests/ui/lifetimes/temporary-lifetime-extension.rs +++ b/tests/ui/lifetimes/temporary-lifetime-extension.rs @@ -25,5 +25,8 @@ fn main() { &temp() } }; - println!("{a:?} {b:?} {c:?} {d:?} {e:?} {f:?} {g:?}"); + let h = match temp() { + owned_non_temporary => &{ owned_non_temporary }, + }; + println!("{a:?} {b:?} {c:?} {d:?} {e:?} {f:?} {g:?} {h:?}"); } From f4caa832dad4482ec1fa85bdbc3156c83cc948de Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Tue, 20 Feb 2024 16:23:05 +0100 Subject: [PATCH 0007/1468] Add clarifying comment to test. --- tests/ui/lifetimes/temporary-lifetime-extension.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ui/lifetimes/temporary-lifetime-extension.rs b/tests/ui/lifetimes/temporary-lifetime-extension.rs index 193ca0b4d0733..1ecef2f3d04df 100644 --- a/tests/ui/lifetimes/temporary-lifetime-extension.rs +++ b/tests/ui/lifetimes/temporary-lifetime-extension.rs @@ -26,6 +26,7 @@ fn main() { } }; let h = match temp() { + // The {} moves the value, making a new temporary. owned_non_temporary => &{ owned_non_temporary }, }; println!("{a:?} {b:?} {c:?} {d:?} {e:?} {f:?} {g:?} {h:?}"); From f1548ec94dcc32634a607389765f76f51cd94eee Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Thu, 22 Feb 2024 02:24:35 +0100 Subject: [PATCH 0008/1468] Remove redundant `-Wl,-syslibroot` Clang already passes this when invoking the linker: https://github.com/llvm/llvm-project/blob/llvmorg-17.0.6/clang/lib/Driver/ToolChains/Darwin.cpp#L439-L442 --- compiler/rustc_codegen_ssa/src/back/link.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 435b517e602b1..a35899bca52e7 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2939,7 +2939,13 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { match flavor { LinkerFlavor::Darwin(Cc::Yes, _) => { - cmd.args(&["-isysroot", &sdk_root, "-Wl,-syslibroot", &sdk_root]); + // Use `-isysroot` instead of `--sysroot`, as only the former + // makes Clang treat it as a platform SDK. + // + // This is admittedly a bit strange, as on most targets + // `-isysroot` only applies to include header files, but on Apple + // targets this also applies to libraries and frameworks. + cmd.args(&["-isysroot", &sdk_root]); } LinkerFlavor::Darwin(Cc::No, _) => { cmd.args(&["-syslibroot", &sdk_root]); From bb4bba5fcf9fce9208e6c2c1de008cec78a9519c Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 22 Feb 2024 17:09:20 +0900 Subject: [PATCH 0009/1468] Remove redundant imports --- crates/core_simd/src/vector.rs | 1 - crates/core_simd/tests/swizzle_dyn.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 9e97a3161bb2b..46b1acf25dd78 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -3,7 +3,6 @@ use crate::simd::{ ptr::{SimdConstPtr, SimdMutPtr}, LaneCount, Mask, MaskElement, SupportedLaneCount, Swizzle, }; -use core::convert::{TryFrom, TryInto}; /// A SIMD vector with the shape of `[T; N]` but the operations of `T`. /// diff --git a/crates/core_simd/tests/swizzle_dyn.rs b/crates/core_simd/tests/swizzle_dyn.rs index f21a937f01c44..19ffe1417c8c4 100644 --- a/crates/core_simd/tests/swizzle_dyn.rs +++ b/crates/core_simd/tests/swizzle_dyn.rs @@ -1,6 +1,6 @@ #![feature(portable_simd)] use core::{fmt, ops::RangeInclusive}; -use test_helpers::{self, biteq, make_runner, prop_assert_biteq}; +use test_helpers::{biteq, make_runner, prop_assert_biteq}; fn swizzle_dyn_scalar_ver(values: [u8; N], idxs: [u8; N]) -> [u8; N] { let mut array = [0; N]; From 6ce3ab72a07e4aadb6bf9b62427d5c31c4639e59 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 22 Feb 2024 16:57:30 +0900 Subject: [PATCH 0010/1468] Fix build error on big endian aarch64 --- crates/core_simd/src/vendor/arm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core_simd/src/vendor/arm.rs b/crates/core_simd/src/vendor/arm.rs index ee5c642137367..233dc0807289f 100644 --- a/crates/core_simd/src/vendor/arm.rs +++ b/crates/core_simd/src/vendor/arm.rs @@ -69,7 +69,7 @@ mod simd32 { from_transmute! { unsafe Simd => int8x4_t } } -#[cfg(target_arch = "aarch64")] +#[cfg(all(target_arch = "aarch64", target_endian = "little"))] mod aarch64 { use super::neon::*; use super::*; From 18de239ecf5054ce4f284a221c394ceace0cbab5 Mon Sep 17 00:00:00 2001 From: AquaEBM Date: Fri, 23 Feb 2024 12:26:52 +0100 Subject: [PATCH 0011/1468] add stdarch_x86_avx512 feature flag for AVX-512-supporting architectures --- crates/core_simd/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index a25723e11cef9..ecadb56bd126b 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -33,6 +33,10 @@ any(target_arch = "powerpc", target_arch = "powerpc64"), feature(stdarch_powerpc) )] +#![cfg_attr( + all(target_arch = "x86_64", target_feature = "avx512f"), + feature(stdarch_x86_avx512) +)] #![warn(missing_docs, clippy::missing_inline_in_public_items)] // basically all items, really #![deny(unsafe_op_in_unsafe_fn, clippy::undocumented_unsafe_blocks)] #![allow(internal_features)] From a4b413d4fd8244e8a54ada9ef6b99160d8f7f01c Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Mon, 25 Dec 2023 20:53:01 +0000 Subject: [PATCH 0012/1468] Add asm label support to AST and HIR --- clippy_lints/src/loops/never_loop.rs | 3 +++ clippy_utils/src/hir_utils.rs | 1 + 2 files changed, 4 insertions(+) diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 245a903f99826..65d922f03df3a 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -255,6 +255,9 @@ fn never_loop_expr<'tcx>( InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => { NeverLoopResult::Normal }, + InlineAsmOperand::Label { block } => { + never_loop_block(cx, block, local_labels, main_loop_id) + } })), ExprKind::OffsetOf(_, _) | ExprKind::Yield(_, _) diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index d50332e82da5b..643852c1c54fb 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -835,6 +835,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_body(anon_const.body); }, InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path), + InlineAsmOperand::Label { block } => self.hash_block(block), } } }, From dbfbd0e77fd12a46ac81d0ec6fca4012aad0ea2d Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 19 Feb 2024 23:30:17 +0100 Subject: [PATCH 0013/1468] feat: add `const_is_empty` lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/methods/is_empty.rs | 40 +++++++++ clippy_lints/src/methods/mod.rs | 35 +++++++- tests/ui/const_is_empty.rs | 52 +++++++++++ tests/ui/const_is_empty.stderr | 124 +++++++++++++++++++++++++++ 6 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/methods/is_empty.rs create mode 100644 tests/ui/const_is_empty.rs create mode 100644 tests/ui/const_is_empty.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 26dce1f926572..9182e61f6d66d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5109,6 +5109,7 @@ Released 2018-09-13 [`collection_is_never_read`]: https://rust-lang.github.io/rust-clippy/master/index.html#collection_is_never_read [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty +[`const_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_is_empty [`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator [`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index b6a35bb3e0328..fe9bc77ce556e 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -350,6 +350,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::methods::CLONE_ON_COPY_INFO, crate::methods::CLONE_ON_REF_PTR_INFO, crate::methods::COLLAPSIBLE_STR_REPLACE_INFO, + crate::methods::CONST_IS_EMPTY_INFO, crate::methods::DRAIN_COLLECT_INFO, crate::methods::ERR_EXPECT_INFO, crate::methods::EXPECT_FUN_CALL_INFO, diff --git a/clippy_lints/src/methods/is_empty.rs b/clippy_lints/src/methods/is_empty.rs new file mode 100644 index 0000000000000..4713c99d33b59 --- /dev/null +++ b/clippy_lints/src/methods/is_empty.rs @@ -0,0 +1,40 @@ +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::expr_or_init; +use rustc_ast::LitKind; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_span::source_map::Spanned; + +use super::CONST_IS_EMPTY; + +pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_>) { + if in_external_macro(cx.sess(), expr.span) || !receiver.span.eq_ctxt(expr.span) { + return; + } + let init_expr = expr_or_init(cx, receiver); + if let Some(init_is_empty) = is_empty(init_expr) + && init_expr.span.eq_ctxt(receiver.span) + { + span_lint_and_note( + cx, + CONST_IS_EMPTY, + expr.span, + &format!("this expression always evaluates to {init_is_empty:?}"), + Some(init_expr.span), + "because its initialization value is constant", + ); + } +} + +fn is_empty(expr: &'_ rustc_hir::Expr<'_>) -> Option { + if let ExprKind::Lit(Spanned { node, .. }) = expr.kind { + match node { + LitKind::Str(sym, _) => Some(sym.is_empty()), + LitKind::ByteStr(value, _) | LitKind::CStr(value, _) => Some(value.is_empty()), + _ => None, + } + } else { + None + } +} diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 820f4c8589065..b6894971acc3e 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -36,6 +36,7 @@ mod inefficient_to_string; mod inspect_for_each; mod into_iter_on_ref; mod is_digit_ascii_radix; +mod is_empty; mod iter_cloned_collect; mod iter_count; mod iter_filter; @@ -4041,6 +4042,31 @@ declare_clippy_lint! { "calling `.get().is_some()` or `.get().is_none()` instead of `.contains()` or `.contains_key()`" } +declare_clippy_lint! { + /// ### What it does + /// It identifies calls to `.is_empty()` on constant values. + /// + /// ### Why is this bad? + /// String literals and constant values are known at compile time. Checking if they + /// are empty will always return the same value. This might not be the intention of + /// the expression. + /// + /// ### Example + /// ```no_run + /// let value = ""; + /// if value.is_empty() { + /// println!("the string is empty"); + /// } + /// ``` + /// Use instead: + /// ```no_run + /// println!("the string is empty"); + /// ``` + #[clippy::version = "1.78.0"] + pub CONST_IS_EMPTY, + suspicious, + "is_empty() called on strings known at compile time" +} pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4089,6 +4115,7 @@ impl_lint_pass!(Methods => [ CLONE_ON_COPY, CLONE_ON_REF_PTR, COLLAPSIBLE_STR_REPLACE, + CONST_IS_EMPTY, ITER_OVEREAGER_CLONED, CLONED_INSTEAD_OF_COPIED, FLAT_MAP_OPTION, @@ -4442,7 +4469,7 @@ impl Methods { ("as_deref" | "as_deref_mut", []) => { needless_option_as_deref::check(cx, expr, recv, name); }, - ("as_bytes" | "is_empty", []) => { + ("as_bytes", []) => { if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { redundant_as_str::check(cx, expr, recv, as_str_span, span); } @@ -4616,6 +4643,12 @@ impl Methods { ("hash", [arg]) => { unit_hash::check(cx, expr, recv, arg); }, + ("is_empty", []) => { + if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { + redundant_as_str::check(cx, expr, recv, as_str_span, span); + } + is_empty::check(cx, expr, recv); + }, ("is_file", []) => filetype_is_file::check(cx, expr, recv), ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, &self.msrv), ("is_none", []) => check_is_some_is_none(cx, expr, recv, call_span, false), diff --git a/tests/ui/const_is_empty.rs b/tests/ui/const_is_empty.rs new file mode 100644 index 0000000000000..d61cdbb042496 --- /dev/null +++ b/tests/ui/const_is_empty.rs @@ -0,0 +1,52 @@ +#![warn(clippy::const_is_empty)] + +fn test_literal() { + if "".is_empty() { + //~^ERROR: this expression always evaluates to true + } + if "foobar".is_empty() { + //~^ERROR: this expression always evaluates to false + } +} + +fn test_byte_literal() { + if b"".is_empty() { + //~^ERROR: this expression always evaluates to true + } + if b"foobar".is_empty() { + //~^ERROR: this expression always evaluates to false + } +} + +fn test_no_mut() { + let mut empty = ""; + if empty.is_empty() { + // No lint because it is mutable + } +} + +fn test_propagated() { + let empty = ""; + let non_empty = "foobar"; + let empty2 = empty; + let non_empty2 = non_empty; + if empty2.is_empty() { + //~^ERROR: this expression always evaluates to true + } + if non_empty2.is_empty() { + //~^ERROR: this expression always evaluates to false + } +} + +fn main() { + let value = "foobar"; + let _ = value.is_empty(); + //~^ ERROR: this expression always evaluates to false + let x = value; + let _ = x.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = "".is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = b"".is_empty(); + //~^ ERROR: this expression always evaluates to true +} diff --git a/tests/ui/const_is_empty.stderr b/tests/ui/const_is_empty.stderr new file mode 100644 index 0000000000000..5a89e686242d8 --- /dev/null +++ b/tests/ui/const_is_empty.stderr @@ -0,0 +1,124 @@ +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:4:8 + | +LL | if "".is_empty() { + | ^^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:4:8 + | +LL | if "".is_empty() { + | ^^ + = note: `-D clippy::const-is-empty` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::const_is_empty)]` + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:7:8 + | +LL | if "foobar".is_empty() { + | ^^^^^^^^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:7:8 + | +LL | if "foobar".is_empty() { + | ^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:13:8 + | +LL | if b"".is_empty() { + | ^^^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:13:8 + | +LL | if b"".is_empty() { + | ^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:16:8 + | +LL | if b"foobar".is_empty() { + | ^^^^^^^^^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:16:8 + | +LL | if b"foobar".is_empty() { + | ^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:33:8 + | +LL | if empty2.is_empty() { + | ^^^^^^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:29:17 + | +LL | let empty = ""; + | ^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:36:8 + | +LL | if non_empty2.is_empty() { + | ^^^^^^^^^^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:30:21 + | +LL | let non_empty = "foobar"; + | ^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:43:13 + | +LL | let _ = value.is_empty(); + | ^^^^^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:42:17 + | +LL | let value = "foobar"; + | ^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:46:13 + | +LL | let _ = x.is_empty(); + | ^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:42:17 + | +LL | let value = "foobar"; + | ^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:48:13 + | +LL | let _ = "".is_empty(); + | ^^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:48:13 + | +LL | let _ = "".is_empty(); + | ^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:50:13 + | +LL | let _ = b"".is_empty(); + | ^^^^^^^^^^^^^^ + | +note: because its initialization value is constant + --> tests/ui/const_is_empty.rs:50:13 + | +LL | let _ = b"".is_empty(); + | ^^^ + +error: aborting due to 10 previous errors + From 89b334d47cf3326af349e2a6a747e707f47bdd30 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 18 Feb 2024 15:39:34 +0100 Subject: [PATCH 0014/1468] chore: update some tests to allow `const_is_empty` --- tests/ui/bool_assert_comparison.fixed | 2 +- tests/ui/bool_assert_comparison.rs | 2 +- tests/ui/len_zero.fixed | 8 ++++- tests/ui/len_zero.rs | 8 ++++- tests/ui/len_zero.stderr | 46 +++++++++++++-------------- tests/ui/needless_bitwise_bool.fixed | 1 + tests/ui/needless_bitwise_bool.rs | 1 + tests/ui/needless_bitwise_bool.stderr | 2 +- tests/ui/redundant_as_str.fixed | 1 + tests/ui/redundant_as_str.rs | 1 + tests/ui/redundant_as_str.stderr | 4 +-- 11 files changed, 46 insertions(+), 30 deletions(-) diff --git a/tests/ui/bool_assert_comparison.fixed b/tests/ui/bool_assert_comparison.fixed index 63b8e27e1c6c5..b05166a055ee3 100644 --- a/tests/ui/bool_assert_comparison.fixed +++ b/tests/ui/bool_assert_comparison.fixed @@ -1,4 +1,4 @@ -#![allow(unused, clippy::assertions_on_constants)] +#![allow(unused, clippy::assertions_on_constants, clippy::const_is_empty)] #![warn(clippy::bool_assert_comparison)] use std::ops::Not; diff --git a/tests/ui/bool_assert_comparison.rs b/tests/ui/bool_assert_comparison.rs index 58f81fedb7959..dc51fcf1d36b9 100644 --- a/tests/ui/bool_assert_comparison.rs +++ b/tests/ui/bool_assert_comparison.rs @@ -1,4 +1,4 @@ -#![allow(unused, clippy::assertions_on_constants)] +#![allow(unused, clippy::assertions_on_constants, clippy::const_is_empty)] #![warn(clippy::bool_assert_comparison)] use std::ops::Not; diff --git a/tests/ui/len_zero.fixed b/tests/ui/len_zero.fixed index 745fc7e1a8b36..c16d7a2661615 100644 --- a/tests/ui/len_zero.fixed +++ b/tests/ui/len_zero.fixed @@ -1,5 +1,11 @@ #![warn(clippy::len_zero)] -#![allow(dead_code, unused, clippy::needless_if, clippy::len_without_is_empty)] +#![allow( + dead_code, + unused, + clippy::needless_if, + clippy::len_without_is_empty, + clippy::const_is_empty +)] extern crate core; use core::ops::Deref; diff --git a/tests/ui/len_zero.rs b/tests/ui/len_zero.rs index 048ad2f4fd34d..5c49a5abf812f 100644 --- a/tests/ui/len_zero.rs +++ b/tests/ui/len_zero.rs @@ -1,5 +1,11 @@ #![warn(clippy::len_zero)] -#![allow(dead_code, unused, clippy::needless_if, clippy::len_without_is_empty)] +#![allow( + dead_code, + unused, + clippy::needless_if, + clippy::len_without_is_empty, + clippy::const_is_empty +)] extern crate core; use core::ops::Deref; diff --git a/tests/ui/len_zero.stderr b/tests/ui/len_zero.stderr index b1f04c94de665..dd07a85d62cac 100644 --- a/tests/ui/len_zero.stderr +++ b/tests/ui/len_zero.stderr @@ -1,5 +1,5 @@ error: length comparison to zero - --> tests/ui/len_zero.rs:82:8 + --> tests/ui/len_zero.rs:88:8 | LL | if x.len() == 0 { | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `x.is_empty()` @@ -8,13 +8,13 @@ LL | if x.len() == 0 { = help: to override `-D warnings` add `#[allow(clippy::len_zero)]` error: length comparison to zero - --> tests/ui/len_zero.rs:86:8 + --> tests/ui/len_zero.rs:92:8 | LL | if "".len() == 0 {} | ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `"".is_empty()` error: comparison to empty slice - --> tests/ui/len_zero.rs:95:20 + --> tests/ui/len_zero.rs:101:20 | LL | println!("{}", *s1 == ""); | ^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s1.is_empty()` @@ -23,121 +23,121 @@ LL | println!("{}", *s1 == ""); = help: to override `-D warnings` add `#[allow(clippy::comparison_to_empty)]` error: comparison to empty slice - --> tests/ui/len_zero.rs:96:20 + --> tests/ui/len_zero.rs:102:20 | LL | println!("{}", **s2 == ""); | ^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s2.is_empty()` error: comparison to empty slice - --> tests/ui/len_zero.rs:97:20 + --> tests/ui/len_zero.rs:103:20 | LL | println!("{}", ***s3 == ""); | ^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s3.is_empty()` error: comparison to empty slice - --> tests/ui/len_zero.rs:98:20 + --> tests/ui/len_zero.rs:104:20 | LL | println!("{}", ****s4 == ""); | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s4.is_empty()` error: comparison to empty slice - --> tests/ui/len_zero.rs:99:20 + --> tests/ui/len_zero.rs:105:20 | LL | println!("{}", *****s5 == ""); | ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s5.is_empty()` error: comparison to empty slice - --> tests/ui/len_zero.rs:100:20 + --> tests/ui/len_zero.rs:106:20 | LL | println!("{}", ******(s6) == ""); | ^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(s6).is_empty()` error: comparison to empty slice - --> tests/ui/len_zero.rs:103:20 + --> tests/ui/len_zero.rs:109:20 | LL | println!("{}", &**d2s == ""); | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(**d2s).is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:118:8 + --> tests/ui/len_zero.rs:124:8 | LL | if has_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:121:8 + --> tests/ui/len_zero.rs:127:8 | LL | if has_is_empty.len() != 0 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:124:8 + --> tests/ui/len_zero.rs:130:8 | LL | if has_is_empty.len() > 0 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:127:8 + --> tests/ui/len_zero.rs:133:8 | LL | if has_is_empty.len() < 1 { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:130:8 + --> tests/ui/len_zero.rs:136:8 | LL | if has_is_empty.len() >= 1 { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:141:8 + --> tests/ui/len_zero.rs:147:8 | LL | if 0 == has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:144:8 + --> tests/ui/len_zero.rs:150:8 | LL | if 0 != has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:147:8 + --> tests/ui/len_zero.rs:153:8 | LL | if 0 < has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:150:8 + --> tests/ui/len_zero.rs:156:8 | LL | if 1 <= has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to one - --> tests/ui/len_zero.rs:153:8 + --> tests/ui/len_zero.rs:159:8 | LL | if 1 > has_is_empty.len() { | ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:167:8 + --> tests/ui/len_zero.rs:173:8 | LL | if with_is_empty.len() == 0 { | ^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `with_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:179:6 + --> tests/ui/len_zero.rs:185:6 | LL | (has_is_empty.len() > 0).then(|| println!("This can happen.")); | ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:180:6 + --> tests/ui/len_zero.rs:186:6 | LL | (has_is_empty.len() == 0).then(|| println!("Or this!")); | ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()` error: length comparison to zero - --> tests/ui/len_zero.rs:184:8 + --> tests/ui/len_zero.rs:190:8 | LL | if b.len() != 0 {} | ^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!b.is_empty()` diff --git a/tests/ui/needless_bitwise_bool.fixed b/tests/ui/needless_bitwise_bool.fixed index 201f8a4c19de5..a8176618c1f23 100644 --- a/tests/ui/needless_bitwise_bool.fixed +++ b/tests/ui/needless_bitwise_bool.fixed @@ -1,4 +1,5 @@ #![warn(clippy::needless_bitwise_bool)] +#![allow(clippy::const_is_empty)] fn returns_bool() -> bool { true diff --git a/tests/ui/needless_bitwise_bool.rs b/tests/ui/needless_bitwise_bool.rs index b0e5014b74b74..f190eb2b76e76 100644 --- a/tests/ui/needless_bitwise_bool.rs +++ b/tests/ui/needless_bitwise_bool.rs @@ -1,4 +1,5 @@ #![warn(clippy::needless_bitwise_bool)] +#![allow(clippy::const_is_empty)] fn returns_bool() -> bool { true diff --git a/tests/ui/needless_bitwise_bool.stderr b/tests/ui/needless_bitwise_bool.stderr index f29d4492540f9..9f14646c3e5ae 100644 --- a/tests/ui/needless_bitwise_bool.stderr +++ b/tests/ui/needless_bitwise_bool.stderr @@ -1,5 +1,5 @@ error: use of bitwise operator instead of lazy operator between booleans - --> tests/ui/needless_bitwise_bool.rs:22:8 + --> tests/ui/needless_bitwise_bool.rs:23:8 | LL | if y & !x { | ^^^^^^ help: try: `y && !x` diff --git a/tests/ui/redundant_as_str.fixed b/tests/ui/redundant_as_str.fixed index 4185b402226c0..708a1cc91506d 100644 --- a/tests/ui/redundant_as_str.fixed +++ b/tests/ui/redundant_as_str.fixed @@ -1,4 +1,5 @@ #![warn(clippy::redundant_as_str)] +#![allow(clippy::const_is_empty)] fn main() { let string = "Hello, world!".to_owned(); diff --git a/tests/ui/redundant_as_str.rs b/tests/ui/redundant_as_str.rs index 7a74d8a55ded7..257af591ceffb 100644 --- a/tests/ui/redundant_as_str.rs +++ b/tests/ui/redundant_as_str.rs @@ -1,4 +1,5 @@ #![warn(clippy::redundant_as_str)] +#![allow(clippy::const_is_empty)] fn main() { let string = "Hello, world!".to_owned(); diff --git a/tests/ui/redundant_as_str.stderr b/tests/ui/redundant_as_str.stderr index f086de5fedebb..f5379d701db82 100644 --- a/tests/ui/redundant_as_str.stderr +++ b/tests/ui/redundant_as_str.stderr @@ -1,5 +1,5 @@ error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too - --> tests/ui/redundant_as_str.rs:7:29 + --> tests/ui/redundant_as_str.rs:8:29 | LL | let _redundant = string.as_str().as_bytes(); | ^^^^^^^^^^^^^^^^^ help: try: `as_bytes` @@ -8,7 +8,7 @@ LL | let _redundant = string.as_str().as_bytes(); = help: to override `-D warnings` add `#[allow(clippy::redundant_as_str)]` error: this `as_str` is redundant and can be removed as the method immediately following exists on `String` too - --> tests/ui/redundant_as_str.rs:8:29 + --> tests/ui/redundant_as_str.rs:9:29 | LL | let _redundant = string.as_str().is_empty(); | ^^^^^^^^^^^^^^^^^ help: try: `is_empty` From 288497093b3bba07d8ab994913135ad6b0cccbc8 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 19 Feb 2024 23:52:09 +0100 Subject: [PATCH 0015/1468] feat: extend `const_is_empty` with many kinds of constants --- clippy_lints/src/methods/is_empty.rs | 49 +++++---- clippy_utils/src/consts.rs | 96 +++++++++++++++-- tests/ui/const_is_empty.rs | 49 +++++++++ tests/ui/const_is_empty.stderr | 153 ++++++++++++++++----------- 4 files changed, 259 insertions(+), 88 deletions(-) diff --git a/clippy_lints/src/methods/is_empty.rs b/clippy_lints/src/methods/is_empty.rs index 4713c99d33b59..7fe66062251bb 100644 --- a/clippy_lints/src/methods/is_empty.rs +++ b/clippy_lints/src/methods/is_empty.rs @@ -1,40 +1,49 @@ -use clippy_utils::diagnostics::span_lint_and_note; -use clippy_utils::expr_or_init; -use rustc_ast::LitKind; -use rustc_hir::{Expr, ExprKind}; +use clippy_utils::consts::constant_is_empty; +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{find_binding_init, path_to_local}; +use rustc_hir::{Expr, HirId}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_span::source_map::Spanned; +use rustc_span::sym; use super::CONST_IS_EMPTY; +/// Expression whose initialization depend on a constant conditioned by a `#[cfg(…)]` directive will +/// not trigger the lint. pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_>) { if in_external_macro(cx.sess(), expr.span) || !receiver.span.eq_ctxt(expr.span) { return; } let init_expr = expr_or_init(cx, receiver); - if let Some(init_is_empty) = is_empty(init_expr) - && init_expr.span.eq_ctxt(receiver.span) - { - span_lint_and_note( + if !receiver.span.eq_ctxt(init_expr.span) { + return; + } + if let Some(init_is_empty) = constant_is_empty(cx, init_expr) { + span_lint( cx, CONST_IS_EMPTY, expr.span, &format!("this expression always evaluates to {init_is_empty:?}"), - Some(init_expr.span), - "because its initialization value is constant", ); } } -fn is_empty(expr: &'_ rustc_hir::Expr<'_>) -> Option { - if let ExprKind::Lit(Spanned { node, .. }) = expr.kind { - match node { - LitKind::Str(sym, _) => Some(sym.is_empty()), - LitKind::ByteStr(value, _) | LitKind::CStr(value, _) => Some(value.is_empty()), - _ => None, - } - } else { - None +fn is_under_cfg(cx: &LateContext<'_>, id: HirId) -> bool { + cx.tcx + .hir() + .parent_id_iter(id) + .any(|id| cx.tcx.hir().attrs(id).iter().any(|attr| attr.has_name(sym::cfg))) +} + +/// Similar to [`clippy_utils::expr_or_init`], but does not go up the chain if the initialization +/// value depends on a `#[cfg(…)]` directive. +fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> { + while let Some(init) = path_to_local(expr) + .and_then(|id| find_binding_init(cx, id)) + .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty()) + .filter(|init| !is_under_cfg(cx, init.hir_id)) + { + expr = init; } + expr } diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 79c691992a85d..774f3f99d46f7 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -10,6 +10,7 @@ use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item use rustc_lexer::tokenize; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{alloc_range, Scalar}; +use rustc_middle::mir::ConstValue; use rustc_middle::ty::{self, EarlyBinder, FloatTy, GenericArgsRef, IntTy, List, ScalarInt, Ty, TyCtxt, UintTy}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::symbol::{Ident, Symbol}; @@ -303,6 +304,12 @@ impl ConstantSource { } } +/// Attempts to check whether the expression is a constant representing an empty slice, str, array, +/// etc… +pub fn constant_is_empty(lcx: &LateContext<'_>, e: &Expr<'_>) -> Option { + ConstEvalLateContext::new(lcx, lcx.typeck_results()).expr_is_empty(e) +} + /// Attempts to evaluate the expression as a constant. pub fn constant<'tcx>( lcx: &LateContext<'tcx>, @@ -402,7 +409,13 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.lcx.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr(e), - ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)), + ExprKind::Path(ref qpath) => { + self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| { + let result = mir_to_const(this.lcx, result)?; + this.source = ConstantSource::Constant; + Some(result) + }) + }, ExprKind::Block(block, _) => self.block(block), ExprKind::Lit(lit) => { if is_direct_expn_of(e.span, "cfg").is_some() { @@ -468,6 +481,39 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } + /// Simple constant folding to determine if an expression is an empty slice, str, array, … + pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option { + match e.kind { + ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.lcx.tcx.hir().body(body).value), + ExprKind::DropTemps(e) => self.expr_is_empty(e), + ExprKind::Path(ref qpath) => { + self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| { + mir_is_empty(this.lcx, result) + }) + }, + ExprKind::Lit(lit) => { + if is_direct_expn_of(e.span, "cfg").is_some() { + None + } else { + match &lit.node { + LitKind::Str(is, _) => Some(is.is_empty()), + LitKind::ByteStr(s, _) | LitKind::CStr(s, _) => Some(s.is_empty()), + _ => None, + } + } + }, + ExprKind::Array(vec) => self.multi(vec).map(|v| v.is_empty()), + ExprKind::Repeat(..) => { + if let ty::Array(_, n) = self.typeck_results.expr_ty(e).kind() { + Some(n.try_eval_target_usize(self.lcx.tcx, self.lcx.param_env)? == 0) + } else { + span_bug!(e.span, "typeck error"); + } + }, + _ => None, + } + } + #[expect(clippy::cast_possible_wrap)] fn constant_not(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option> { use self::Constant::{Bool, Int}; @@ -515,8 +561,11 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { vec.iter().map(|elem| self.expr(elem)).collect::>() } - /// Lookup a possibly constant expression from an `ExprKind::Path`. - fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option> { + /// Lookup a possibly constant expression from an `ExprKind::Path` and apply a function on it. + fn fetch_path_and_apply(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option + where + F: FnOnce(&mut Self, rustc_middle::mir::Const<'tcx>) -> Option, + { let res = self.typeck_results.qpath_res(qpath, id); match res { Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { @@ -549,9 +598,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), None) .ok() .map(|val| rustc_middle::mir::Const::from_value(val, ty))?; - let result = mir_to_const(self.lcx, result)?; - self.source = ConstantSource::Constant; - Some(result) + f(self, result) }, _ => None, } @@ -742,7 +789,6 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option> { - use rustc_middle::mir::ConstValue; let mir::Const::Val(val, _) = result else { // We only work on evaluated consts. return None; @@ -788,6 +834,42 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> } } +fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option { + let mir::Const::Val(val, _) = result else { + // We only work on evaluated consts. + return None; + }; + match (val, result.ty().kind()) { + (_, ty::Ref(_, inner_ty, _)) => match inner_ty.kind() { + ty::Str | ty::Slice(_) => { + if let ConstValue::Indirect { alloc_id, offset } = val { + // Get the length from the slice, using the same formula as + // [`ConstValue::try_get_slice_bytes_for_diagnostics`]. + let a = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner(); + let ptr_size = lcx.tcx.data_layout.pointer_size; + if a.size() < offset + 2 * ptr_size { + // (partially) dangling reference + return None; + } + let len = a + .read_scalar(&lcx.tcx, alloc_range(offset + ptr_size, ptr_size), false) + .ok()? + .to_target_usize(&lcx.tcx) + .ok()?; + Some(len == 0) + } else { + None + } + }, + ty::Array(_, len) => Some(len.try_to_target_usize(lcx.tcx)? == 0), + _ => None, + }, + (ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(lcx.tcx)? == 0), + (ConstValue::ZeroSized, _) => Some(true), + _ => None, + } +} + fn field_of_struct<'tcx>( adt_def: ty::AdtDef<'tcx>, lcx: &LateContext<'tcx>, diff --git a/tests/ui/const_is_empty.rs b/tests/ui/const_is_empty.rs index d61cdbb042496..bd7e3a7c5d5b9 100644 --- a/tests/ui/const_is_empty.rs +++ b/tests/ui/const_is_empty.rs @@ -38,6 +38,55 @@ fn test_propagated() { } } +const EMPTY_STR: &str = ""; +const NON_EMPTY_STR: &str = "foo"; +const EMPTY_BSTR: &[u8] = b""; +const NON_EMPTY_BSTR: &[u8] = b"foo"; +const EMPTY_U8_SLICE: &[u8] = &[]; +const NON_EMPTY_U8_SLICE: &[u8] = &[1, 2]; +const EMPTY_SLICE: &[u32] = &[]; +const NON_EMPTY_SLICE: &[u32] = &[1, 2]; +const NON_EMPTY_SLICE_REPEAT: &[u32] = &[1; 2]; +const EMPTY_ARRAY: [u32; 0] = []; +const EMPTY_ARRAY_REPEAT: [u32; 0] = [1; 0]; +const NON_EMPTY_ARRAY: [u32; 2] = [1, 2]; +const NON_EMPTY_ARRAY_REPEAT: [u32; 2] = [1; 2]; +const EMPTY_REF_ARRAY: &[u32; 0] = &[]; +const NON_EMPTY_REF_ARRAY: &[u32; 3] = &[1, 2, 3]; + +fn test_from_const() { + let _ = EMPTY_STR.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = NON_EMPTY_STR.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = EMPTY_BSTR.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = NON_EMPTY_BSTR.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = EMPTY_ARRAY.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = EMPTY_ARRAY_REPEAT.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = EMPTY_U8_SLICE.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = NON_EMPTY_U8_SLICE.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = NON_EMPTY_ARRAY.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = NON_EMPTY_ARRAY_REPEAT.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = EMPTY_REF_ARRAY.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = NON_EMPTY_REF_ARRAY.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = EMPTY_SLICE.is_empty(); + //~^ ERROR: this expression always evaluates to true + let _ = NON_EMPTY_SLICE.is_empty(); + //~^ ERROR: this expression always evaluates to false + let _ = NON_EMPTY_SLICE_REPEAT.is_empty(); + //~^ ERROR: this expression always evaluates to false +} + fn main() { let value = "foobar"; let _ = value.is_empty(); diff --git a/tests/ui/const_is_empty.stderr b/tests/ui/const_is_empty.stderr index 5a89e686242d8..ef5aa556e6a04 100644 --- a/tests/ui/const_is_empty.stderr +++ b/tests/ui/const_is_empty.stderr @@ -4,11 +4,6 @@ error: this expression always evaluates to true LL | if "".is_empty() { | ^^^^^^^^^^^^^ | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:4:8 - | -LL | if "".is_empty() { - | ^^ = note: `-D clippy::const-is-empty` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::const_is_empty)]` @@ -17,108 +12,144 @@ error: this expression always evaluates to false | LL | if "foobar".is_empty() { | ^^^^^^^^^^^^^^^^^^^ - | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:7:8 - | -LL | if "foobar".is_empty() { - | ^^^^^^^^ error: this expression always evaluates to true --> tests/ui/const_is_empty.rs:13:8 | LL | if b"".is_empty() { | ^^^^^^^^^^^^^^ - | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:13:8 - | -LL | if b"".is_empty() { - | ^^^ error: this expression always evaluates to false --> tests/ui/const_is_empty.rs:16:8 | LL | if b"foobar".is_empty() { | ^^^^^^^^^^^^^^^^^^^^ - | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:16:8 - | -LL | if b"foobar".is_empty() { - | ^^^^^^^^^ error: this expression always evaluates to true --> tests/ui/const_is_empty.rs:33:8 | LL | if empty2.is_empty() { | ^^^^^^^^^^^^^^^^^ - | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:29:17 - | -LL | let empty = ""; - | ^^ error: this expression always evaluates to false --> tests/ui/const_is_empty.rs:36:8 | LL | if non_empty2.is_empty() { | ^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:58:13 | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:30:21 +LL | let _ = EMPTY_STR.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:60:13 | -LL | let non_empty = "foobar"; - | ^^^^^^^^ +LL | let _ = NON_EMPTY_STR.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:62:13 + | +LL | let _ = EMPTY_BSTR.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:43:13 + --> tests/ui/const_is_empty.rs:64:13 | -LL | let _ = value.is_empty(); - | ^^^^^^^^^^^^^^^^ +LL | let _ = NON_EMPTY_BSTR.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:66:13 + | +LL | let _ = EMPTY_ARRAY.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:68:13 | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:42:17 +LL | let _ = EMPTY_ARRAY_REPEAT.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:70:13 | -LL | let value = "foobar"; - | ^^^^^^^^ +LL | let _ = EMPTY_U8_SLICE.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:46:13 + --> tests/ui/const_is_empty.rs:72:13 | -LL | let _ = x.is_empty(); - | ^^^^^^^^^^^^ +LL | let _ = NON_EMPTY_U8_SLICE.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:74:13 | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:42:17 +LL | let _ = NON_EMPTY_ARRAY.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:76:13 | -LL | let value = "foobar"; - | ^^^^^^^^ +LL | let _ = NON_EMPTY_ARRAY_REPEAT.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:48:13 + --> tests/ui/const_is_empty.rs:78:13 | -LL | let _ = "".is_empty(); - | ^^^^^^^^^^^^^ +LL | let _ = EMPTY_REF_ARRAY.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:80:13 + | +LL | let _ = NON_EMPTY_REF_ARRAY.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:82:13 + | +LL | let _ = EMPTY_SLICE.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:84:13 + | +LL | let _ = NON_EMPTY_SLICE.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:86:13 | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:48:13 +LL | let _ = NON_EMPTY_SLICE_REPEAT.is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:92:13 + | +LL | let _ = value.is_empty(); + | ^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to false + --> tests/ui/const_is_empty.rs:95:13 + | +LL | let _ = x.is_empty(); + | ^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:97:13 | LL | let _ = "".is_empty(); - | ^^ + | ^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:50:13 + --> tests/ui/const_is_empty.rs:99:13 | LL | let _ = b"".is_empty(); | ^^^^^^^^^^^^^^ - | -note: because its initialization value is constant - --> tests/ui/const_is_empty.rs:50:13 - | -LL | let _ = b"".is_empty(); - | ^^^ -error: aborting due to 10 previous errors +error: aborting due to 25 previous errors From 1159e2c00fdc97dede1dca0464d57750283ac3df Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Tue, 20 Feb 2024 00:05:59 +0100 Subject: [PATCH 0016/1468] feat: add more tests for the `const_is_empty` lint Suggested by @xFredNet and @matthiaskrgr. --- tests/ui/const_is_empty.rs | 68 ++++++++++++++++++++++++++++++++++ tests/ui/const_is_empty.stderr | 58 ++++++++++++++++------------- 2 files changed, 100 insertions(+), 26 deletions(-) diff --git a/tests/ui/const_is_empty.rs b/tests/ui/const_is_empty.rs index bd7e3a7c5d5b9..df7817e4b5fef 100644 --- a/tests/ui/const_is_empty.rs +++ b/tests/ui/const_is_empty.rs @@ -1,4 +1,6 @@ +#![feature(inline_const)] #![warn(clippy::const_is_empty)] +#![allow(clippy::needless_late_init, unused_must_use)] fn test_literal() { if "".is_empty() { @@ -99,3 +101,69 @@ fn main() { let _ = b"".is_empty(); //~^ ERROR: this expression always evaluates to true } + +fn str_from_arg(var: &str) { + var.is_empty(); + // Do not lint, we know nothiny about var +} + +fn update_str() { + let mut value = "duck"; + value = "penguin"; + + let _ = value.is_empty(); + // Do not lint since value is mutable +} + +fn macros() { + // Content from Macro + let file = include_str!("const_is_empty.rs"); + let _ = file.is_empty(); + // No lint because initializer comes from a macro result + + let var = env!("PATH"); + let _ = var.is_empty(); + // No lint because initializer comes from a macro result +} + +fn conditional_value() { + let value; + + if true { + value = "hey"; + } else { + value = "hej"; + } + + let _ = value.is_empty(); + // Do not lint, current constant folding is too simple to detect this +} + +fn cfg_conditioned() { + #[cfg(test)] + let val = ""; + #[cfg(not(test))] + let val = "foo"; + + let _ = val.is_empty(); + // Do not lint, value depend on a #[cfg(…)] directive +} + +fn not_cfg_conditioned() { + let val = ""; + #[cfg(not(target_os = "inexistent"))] + let _ = val.is_empty(); + //~^ ERROR: this expression always evaluates to true +} + +const fn const_rand() -> &'static str { + "17" +} + +fn const_expressions() { + let _ = const { if true { "1" } else { "2" } }.is_empty(); + // Do not lint, we do not recurse into boolean expressions + + let _ = const_rand().is_empty(); + // Do not lint, we do not recurse into functions +} diff --git a/tests/ui/const_is_empty.stderr b/tests/ui/const_is_empty.stderr index ef5aa556e6a04..0e09da77bb469 100644 --- a/tests/ui/const_is_empty.stderr +++ b/tests/ui/const_is_empty.stderr @@ -1,5 +1,5 @@ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:4:8 + --> tests/ui/const_is_empty.rs:6:8 | LL | if "".is_empty() { | ^^^^^^^^^^^^^ @@ -8,148 +8,154 @@ LL | if "".is_empty() { = help: to override `-D warnings` add `#[allow(clippy::const_is_empty)]` error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:7:8 + --> tests/ui/const_is_empty.rs:9:8 | LL | if "foobar".is_empty() { | ^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:13:8 + --> tests/ui/const_is_empty.rs:15:8 | LL | if b"".is_empty() { | ^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:16:8 + --> tests/ui/const_is_empty.rs:18:8 | LL | if b"foobar".is_empty() { | ^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:33:8 + --> tests/ui/const_is_empty.rs:35:8 | LL | if empty2.is_empty() { | ^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:36:8 + --> tests/ui/const_is_empty.rs:38:8 | LL | if non_empty2.is_empty() { | ^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:58:13 + --> tests/ui/const_is_empty.rs:60:13 | LL | let _ = EMPTY_STR.is_empty(); | ^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:60:13 + --> tests/ui/const_is_empty.rs:62:13 | LL | let _ = NON_EMPTY_STR.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:62:13 + --> tests/ui/const_is_empty.rs:64:13 | LL | let _ = EMPTY_BSTR.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:64:13 + --> tests/ui/const_is_empty.rs:66:13 | LL | let _ = NON_EMPTY_BSTR.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:66:13 + --> tests/ui/const_is_empty.rs:68:13 | LL | let _ = EMPTY_ARRAY.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:68:13 + --> tests/ui/const_is_empty.rs:70:13 | LL | let _ = EMPTY_ARRAY_REPEAT.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:70:13 + --> tests/ui/const_is_empty.rs:72:13 | LL | let _ = EMPTY_U8_SLICE.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:72:13 + --> tests/ui/const_is_empty.rs:74:13 | LL | let _ = NON_EMPTY_U8_SLICE.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:74:13 + --> tests/ui/const_is_empty.rs:76:13 | LL | let _ = NON_EMPTY_ARRAY.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:76:13 + --> tests/ui/const_is_empty.rs:78:13 | LL | let _ = NON_EMPTY_ARRAY_REPEAT.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:78:13 + --> tests/ui/const_is_empty.rs:80:13 | LL | let _ = EMPTY_REF_ARRAY.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:80:13 + --> tests/ui/const_is_empty.rs:82:13 | LL | let _ = NON_EMPTY_REF_ARRAY.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:82:13 + --> tests/ui/const_is_empty.rs:84:13 | LL | let _ = EMPTY_SLICE.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:84:13 + --> tests/ui/const_is_empty.rs:86:13 | LL | let _ = NON_EMPTY_SLICE.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:86:13 + --> tests/ui/const_is_empty.rs:88:13 | LL | let _ = NON_EMPTY_SLICE_REPEAT.is_empty(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:92:13 + --> tests/ui/const_is_empty.rs:94:13 | LL | let _ = value.is_empty(); | ^^^^^^^^^^^^^^^^ error: this expression always evaluates to false - --> tests/ui/const_is_empty.rs:95:13 + --> tests/ui/const_is_empty.rs:97:13 | LL | let _ = x.is_empty(); | ^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:97:13 + --> tests/ui/const_is_empty.rs:99:13 | LL | let _ = "".is_empty(); | ^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:99:13 + --> tests/ui/const_is_empty.rs:101:13 | LL | let _ = b"".is_empty(); | ^^^^^^^^^^^^^^ -error: aborting due to 25 previous errors +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:155:13 + | +LL | let _ = val.is_empty(); + | ^^^^^^^^^^^^^^ + +error: aborting due to 26 previous errors From 898ed8825d610d4f441ccb7c84c770261ce45ded Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Mon, 26 Feb 2024 09:47:13 +0100 Subject: [PATCH 0017/1468] feat: make `const_is_empty` lint ignore external constants --- clippy_utils/src/consts.rs | 11 +++++++++++ tests/ui/const_is_empty.rs | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 774f3f99d46f7..a4cb4ee93caaf 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -13,6 +13,7 @@ use rustc_middle::mir::interpret::{alloc_range, Scalar}; use rustc_middle::mir::ConstValue; use rustc_middle::ty::{self, EarlyBinder, FloatTy, GenericArgsRef, IntTy, List, ScalarInt, Ty, TyCtxt, UintTy}; use rustc_middle::{bug, mir, span_bug}; +use rustc_span::def_id::DefId; use rustc_span::symbol::{Ident, Symbol}; use rustc_span::SyntaxContext; use rustc_target::abi::Size; @@ -482,11 +483,21 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } /// Simple constant folding to determine if an expression is an empty slice, str, array, … + /// `None` will be returned if the constness cannot be determined, or if the resolution + /// leaves the local crate. pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option { match e.kind { ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.lcx.tcx.hir().body(body).value), ExprKind::DropTemps(e) => self.expr_is_empty(e), ExprKind::Path(ref qpath) => { + if !self + .typeck_results + .qpath_res(qpath, e.hir_id) + .opt_def_id() + .is_some_and(DefId::is_local) + { + return None; + } self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| { mir_is_empty(this.lcx, result) }) diff --git a/tests/ui/const_is_empty.rs b/tests/ui/const_is_empty.rs index df7817e4b5fef..ae37a82e4f936 100644 --- a/tests/ui/const_is_empty.rs +++ b/tests/ui/const_is_empty.rs @@ -167,3 +167,8 @@ fn const_expressions() { let _ = const_rand().is_empty(); // Do not lint, we do not recurse into functions } + +fn constant_from_external_crate() { + let _ = std::env::consts::EXE_EXTENSION.is_empty(); + // Do not lint, `exe_ext` comes from the `std` crate +} From aa11bf6f0a1ba4e6eb92f31423feb831fae2b7d2 Mon Sep 17 00:00:00 2001 From: simonschoening Date: Wed, 14 Jun 2023 22:51:51 +0200 Subject: [PATCH 0018/1468] Extending filesystem support for hermit-os --- library/std/src/sys/pal/hermit/fd.rs | 5 + library/std/src/sys/pal/hermit/fs.rs | 306 +++++++++++++++++-------- library/std/src/sys/pal/hermit/time.rs | 10 + 3 files changed, 225 insertions(+), 96 deletions(-) diff --git a/library/std/src/sys/pal/hermit/fd.rs b/library/std/src/sys/pal/hermit/fd.rs index 5eb828fea1f9e..962577bb1ed83 100644 --- a/library/std/src/sys/pal/hermit/fd.rs +++ b/library/std/src/sys/pal/hermit/fd.rs @@ -48,6 +48,11 @@ impl FileDesc { pub fn set_nonblocking(&self, _nonblocking: bool) -> io::Result<()> { unsupported() } + + pub fn fstat(&self, stat: *mut abi::stat) -> io::Result<()> { + cvt(unsafe { abi::fstat(self.fd.as_raw_fd(), stat) })?; + Ok(()) + } } impl<'a> Read for &'a FileDesc { diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs index d4da53fd3df9e..306e4d2210ac6 100644 --- a/library/std/src/sys/pal/hermit/fs.rs +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -2,11 +2,14 @@ use super::abi::{self, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_W use super::fd::FileDesc; use crate::ffi::{CStr, OsString}; use crate::fmt; -use crate::hash::{Hash, Hasher}; use crate::io::{self, Error, ErrorKind}; use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; +use crate::mem; +use crate::os::hermit::ffi::OsStringExt; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::{Path, PathBuf}; +use crate::ptr; +use crate::sync::Arc; use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::cvt; use crate::sys::time::SystemTime; @@ -18,12 +21,44 @@ pub use crate::sys_common::fs::{copy, try_exists}; #[derive(Debug)] pub struct File(FileDesc); +#[derive(Clone)] +pub struct FileAttr { + stat_val: stat_struct, +} -pub struct FileAttr(!); +impl FileAttr { + fn from_stat(stat_val: stat_struct) -> Self { + Self { stat_val } + } +} -pub struct ReadDir(!); +// all DirEntry's will have a reference to this struct +struct InnerReadDir { + dirp: FileDesc, + root: PathBuf, +} + +pub struct ReadDir { + inner: Arc, + end_of_stream: bool, +} -pub struct DirEntry(!); +impl ReadDir { + fn new(inner: InnerReadDir) -> Self { + Self { inner: Arc::new(inner), end_of_stream: false } + } +} + +pub struct DirEntry { + dir: Arc, + entry: dirent_min, + name: OsString, +} + +struct dirent_min { + d_ino: u64, + d_type: u32, +} #[derive(Clone, Debug)] pub struct OpenOptions { @@ -41,72 +76,78 @@ pub struct OpenOptions { #[derive(Copy, Clone, Debug, Default)] pub struct FileTimes {} -pub struct FilePermissions(!); - -pub struct FileType(!); +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct FilePermissions { + mode: u32, +} -#[derive(Debug)] -pub struct DirBuilder {} +#[derive(Copy, Clone, Eq, Debug)] +pub struct FileType { + mode: u32, +} -impl FileAttr { - pub fn size(&self) -> u64 { - self.0 +impl PartialEq for FileType { + fn eq(&self, other: &Self) -> bool { + self.mode == other.mode } +} - pub fn perm(&self) -> FilePermissions { - self.0 +impl core::hash::Hash for FileType { + fn hash(&self, state: &mut H) { + self.mode.hash(state); } +} - pub fn file_type(&self) -> FileType { - self.0 - } +#[derive(Debug)] +pub struct DirBuilder { + mode: u32, +} +impl FileAttr { pub fn modified(&self) -> io::Result { - self.0 + Ok(SystemTime::new(self.stat_val.st_mtime, self.stat_val.st_mtime_nsec)) } pub fn accessed(&self) -> io::Result { - self.0 + Ok(SystemTime::new(self.stat_val.st_atime, self.stat_val.st_atime_nsec)) } pub fn created(&self) -> io::Result { - self.0 + Ok(SystemTime::new(self.stat_val.st_ctime, self.stat_val.st_ctime_nsec)) } -} -impl Clone for FileAttr { - fn clone(&self) -> FileAttr { - self.0 + pub fn size(&self) -> u64 { + self.stat_val.st_size as u64 } -} - -impl FilePermissions { - pub fn readonly(&self) -> bool { - self.0 + pub fn perm(&self) -> FilePermissions { + FilePermissions { mode: (self.stat_val.st_mode) } } - pub fn set_readonly(&mut self, _readonly: bool) { - self.0 + pub fn file_type(&self) -> FileType { + let masked_mode = self.stat_val.st_mode & S_IFMT; + let mode = match masked_mode { + S_IFDIR => DT_DIR, + S_IFLNK => DT_LNK, + S_IFREG => DT_REG, + _ => DT_UNKNOWN, + }; + FileType { mode: mode } } } -impl Clone for FilePermissions { - fn clone(&self) -> FilePermissions { - self.0 +impl FilePermissions { + pub fn readonly(&self) -> bool { + // check if any class (owner, group, others) has write permission + self.mode & 0o222 == 0 } -} -impl PartialEq for FilePermissions { - fn eq(&self, _other: &FilePermissions) -> bool { - self.0 + pub fn set_readonly(&mut self, _readonly: bool) { + unimplemented!() } -} - -impl Eq for FilePermissions {} -impl fmt::Debug for FilePermissions { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 + #[allow(dead_code)] + pub fn mode(&self) -> u32 { + self.mode as u32 } } @@ -117,75 +158,127 @@ impl FileTimes { impl FileType { pub fn is_dir(&self) -> bool { - self.0 + self.mode == DT_DIR } - pub fn is_file(&self) -> bool { - self.0 + self.mode == DT_REG } - pub fn is_symlink(&self) -> bool { - self.0 + self.mode == DT_LNK } } -impl Clone for FileType { - fn clone(&self) -> FileType { - self.0 +impl fmt::Debug for ReadDir { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. + // Thus the result will be e g 'ReadDir("/home")' + fmt::Debug::fmt(&*self.inner.root, f) } } -impl Copy for FileType {} +impl Iterator for ReadDir { + type Item = io::Result; -impl PartialEq for FileType { - fn eq(&self, _other: &FileType) -> bool { - self.0 - } -} + fn next(&mut self) -> Option> { + if self.end_of_stream { + return None; + } -impl Eq for FileType {} + unsafe { + loop { + // As of POSIX.1-2017, readdir() is not required to be thread safe; only + // readdir_r() is. However, readdir_r() cannot correctly handle platforms + // with unlimited or variable NAME_MAX. Many modern platforms guarantee + // thread safety for readdir() as long an individual DIR* is not accessed + // concurrently, which is sufficient for Rust. + let entry_ptr = match abi::readdir(self.inner.dirp.as_raw_fd()) { + abi::DirectoryEntry::Invalid(e) => { + // We either encountered an error, or reached the end. Either way, + // the next call to next() should return None. + self.end_of_stream = true; + + return Some(Err(Error::from_raw_os_error(e))); + } + abi::DirectoryEntry::Valid(ptr) => { + if ptr.is_null() { + return None; + } + + ptr + } + }; + + macro_rules! offset_ptr { + ($entry_ptr:expr, $field:ident) => {{ + const OFFSET: isize = { + let delusion = MaybeUninit::::uninit(); + let entry_ptr = delusion.as_ptr(); + unsafe { + ptr::addr_of!((*entry_ptr).$field) + .cast::() + .offset_from(entry_ptr.cast::()) + } + }; + if true { + // Cast to the same type determined by the else branch. + $entry_ptr.byte_offset(OFFSET).cast::<_>() + } else { + #[allow(deref_nullptr)] + { + ptr::addr_of!((*ptr::null::()).$field) + } + } + }}; + } -impl Hash for FileType { - fn hash(&self, _h: &mut H) { - self.0 - } -} + // d_name is NOT guaranteed to be null-terminated. + let name_bytes = core::slice::from_raw_parts( + offset_ptr!(entry_ptr, d_name) as *const u8, + *offset_ptr!(entry_ptr, d_namelen) as usize, + ) + .to_vec(); -impl fmt::Debug for FileType { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} + if name_bytes == b"." || name_bytes == b".." { + continue; + } -impl fmt::Debug for ReadDir { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0 - } -} + let name = OsString::from_vec(name_bytes); -impl Iterator for ReadDir { - type Item = io::Result; + let entry = dirent_min { + d_ino: *offset_ptr!(entry_ptr, d_ino), + d_type: *offset_ptr!(entry_ptr, d_type), + }; - fn next(&mut self) -> Option> { - self.0 + return Some(Ok(DirEntry { entry, name: name, dir: Arc::clone(&self.inner) })); + } + } } } impl DirEntry { pub fn path(&self) -> PathBuf { - self.0 + self.dir.root.join(self.file_name_os_str()) } pub fn file_name(&self) -> OsString { - self.0 + self.file_name_os_str().to_os_string() } pub fn metadata(&self) -> io::Result { - self.0 + lstat(&self.path()) } pub fn file_type(&self) -> io::Result { - self.0 + Ok(FileType { mode: self.entry.d_type }) + } + + #[allow(dead_code)] + pub fn ino(&self) -> u64 { + self.entry.d_ino + } + + pub fn file_name_os_str(&self) -> &OsStr { + self.name.as_os_str() } } @@ -288,7 +381,9 @@ impl File { } pub fn file_attr(&self) -> io::Result { - Err(Error::from_raw_os_error(22)) + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + self.0.fstat(&mut stat_val)?; + Ok(FileAttr::from_stat(stat_val)) } pub fn fsync(&self) -> io::Result<()> { @@ -357,11 +452,18 @@ impl File { impl DirBuilder { pub fn new() -> DirBuilder { - DirBuilder {} + DirBuilder { mode: 0o777 } } - pub fn mkdir(&self, _p: &Path) -> io::Result<()> { - unsupported() + pub fn mkdir(&self, path: &Path) -> io::Result<()> { + run_path_with_cstr(path, |path| { + cvt(unsafe { abi::mkdir(path.as_ptr(), self.mode) }).map(|_| ()) + }) + } + + #[allow(dead_code)] + pub fn set_mode(&mut self, mode: u32) { + self.mode = mode as u32; } } @@ -416,8 +518,12 @@ impl FromRawFd for File { } } -pub fn readdir(_p: &Path) -> io::Result { - unsupported() +pub fn readdir(path: &Path) -> io::Result { + let fd_raw = run_path_with_cstr(path, |path| cvt(unsafe { abi::opendir(path.as_ptr()) }))?; + let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) }; + let root = path.to_path_buf(); + let inner = InnerReadDir { dirp: fd, root }; + Ok(ReadDir::new(inner)) } pub fn unlink(path: &Path) -> io::Result<()> { @@ -428,12 +534,12 @@ pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { unsupported() } -pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { - match perm.0 {} +pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { + Err(Error::from_raw_os_error(22)) } -pub fn rmdir(_p: &Path) -> io::Result<()> { - unsupported() +pub fn rmdir(path: &Path) -> io::Result<()> { + run_path_with_cstr(path, |path| cvt(unsafe { abi::rmdir(path.as_ptr()) }).map(|_| ())) } pub fn remove_dir_all(_path: &Path) -> io::Result<()> { @@ -453,12 +559,20 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { unsupported() } -pub fn stat(_p: &Path) -> io::Result { - unsupported() +pub fn stat(path: &Path) -> io::Result { + run_path_with_cstr(path, |path| { + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + cvt(unsafe { abi::stat(path.as_ptr(), &mut stat_val) })?; + Ok(FileAttr::from_stat(stat_val)) + }) } -pub fn lstat(_p: &Path) -> io::Result { - unsupported() +pub fn lstat(path: &Path) -> io::Result { + run_path_with_cstr(path, |path| { + let mut stat_val: stat_struct = unsafe { mem::zeroed() }; + cvt(unsafe { abi::lstat(path.as_ptr(), &mut stat_val) })?; + Ok(FileAttr::from_stat(stat_val)) + }) } pub fn canonicalize(_p: &Path) -> io::Result { diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index f289dafd8bc56..fa10b7638ec20 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -18,6 +18,12 @@ impl Timespec { Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } } } + const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { + assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64); + // SAFETY: The assert above checks tv_nsec is within the valid range + Timespec { t: timespec { tv_sec: tv_sec, tv_nsec: tv_nsec } } + } + fn sub_timespec(&self, other: &Timespec) -> Result { if self >= other { Ok(if self.t.tv_nsec >= other.t.tv_nsec { @@ -195,6 +201,10 @@ pub struct SystemTime(Timespec); pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero()); impl SystemTime { + pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { + SystemTime(Timespec::new(tv_sec, tv_nsec)) + } + pub fn now() -> SystemTime { let mut time: Timespec = Timespec::zero(); let _ = unsafe { abi::clock_gettime(CLOCK_REALTIME, core::ptr::addr_of_mut!(time.t)) }; From b1c7804c684301213775a4f490605302aeec7789 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Wed, 17 Jan 2024 20:19:16 +0100 Subject: [PATCH 0019/1468] revise interface to read directory entries The new interface has some similarities to Linux system call getdents64. The system call reads several dirent64 structures. At the end of each dirent64 is stored the name of the file. The length of file name is implictly part of dirent64 because d_reclen contains size of dirent64 plus the length of the file name. --- library/std/src/sys/pal/hermit/fs.rs | 213 +++++++++++++++------------ 1 file changed, 116 insertions(+), 97 deletions(-) diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs index 306e4d2210ac6..6519cc22f1f6f 100644 --- a/library/std/src/sys/pal/hermit/fs.rs +++ b/library/std/src/sys/pal/hermit/fs.rs @@ -1,6 +1,9 @@ -use super::abi::{self, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY}; +use super::abi::{ + self, dirent64, stat as stat_struct, DT_DIR, DT_LNK, DT_REG, DT_UNKNOWN, O_APPEND, O_CREAT, + O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, +}; use super::fd::FileDesc; -use crate::ffi::{CStr, OsString}; +use crate::ffi::{CStr, OsStr, OsString}; use crate::fmt; use crate::io::{self, Error, ErrorKind}; use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom}; @@ -8,7 +11,6 @@ use crate::mem; use crate::os::hermit::ffi::OsStringExt; use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::path::{Path, PathBuf}; -use crate::ptr; use crate::sync::Arc; use crate::sys::common::small_c_string::run_path_with_cstr; use crate::sys::cvt; @@ -17,7 +19,6 @@ use crate::sys::unsupported; use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; pub use crate::sys_common::fs::{copy, try_exists}; -//pub use crate::sys_common::fs::remove_dir_all; #[derive(Debug)] pub struct File(FileDesc); @@ -34,32 +35,38 @@ impl FileAttr { // all DirEntry's will have a reference to this struct struct InnerReadDir { - dirp: FileDesc, root: PathBuf, + dir: Vec, +} + +impl InnerReadDir { + pub fn new(root: PathBuf, dir: Vec) -> Self { + Self { root, dir } + } } pub struct ReadDir { inner: Arc, - end_of_stream: bool, + pos: i64, } impl ReadDir { fn new(inner: InnerReadDir) -> Self { - Self { inner: Arc::new(inner), end_of_stream: false } + Self { inner: Arc::new(inner), pos: 0 } } } pub struct DirEntry { - dir: Arc, - entry: dirent_min, + /// path to the entry + root: PathBuf, + /// 64-bit inode number + ino: u64, + /// File type + type_: u32, + /// name of the entry name: OsString, } -struct dirent_min { - d_ino: u64, - d_type: u32, -} - #[derive(Clone, Debug)] pub struct OpenOptions { // generic @@ -105,15 +112,24 @@ pub struct DirBuilder { impl FileAttr { pub fn modified(&self) -> io::Result { - Ok(SystemTime::new(self.stat_val.st_mtime, self.stat_val.st_mtime_nsec)) + Ok(SystemTime::new( + self.stat_val.st_mtime.try_into().unwrap(), + self.stat_val.st_mtime_nsec.try_into().unwrap(), + )) } pub fn accessed(&self) -> io::Result { - Ok(SystemTime::new(self.stat_val.st_atime, self.stat_val.st_atime_nsec)) + Ok(SystemTime::new( + self.stat_val.st_atime.try_into().unwrap(), + self.stat_val.st_atime_nsec.try_into().unwrap(), + )) } pub fn created(&self) -> io::Result { - Ok(SystemTime::new(self.stat_val.st_ctime, self.stat_val.st_ctime_nsec)) + Ok(SystemTime::new( + self.stat_val.st_ctime.try_into().unwrap(), + self.stat_val.st_ctime_nsec.try_into().unwrap(), + )) } pub fn size(&self) -> u64 { @@ -171,7 +187,7 @@ impl FileType { impl fmt::Debug for ReadDir { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame. - // Thus the result will be e g 'ReadDir("/home")' + // Thus the result will be e.g. 'ReadDir("/home")' fmt::Debug::fmt(&*self.inner.root, f) } } @@ -180,84 +196,55 @@ impl Iterator for ReadDir { type Item = io::Result; fn next(&mut self) -> Option> { - if self.end_of_stream { - return None; - } + let mut counter: usize = 0; + let mut offset: i64 = 0; + + // loop over all directory entries and search the entry for the current position + loop { + // leave function, if the loop reaches the of the buffer (with all entries) + if offset >= self.inner.dir.len().try_into().unwrap() { + return None; + } - unsafe { - loop { - // As of POSIX.1-2017, readdir() is not required to be thread safe; only - // readdir_r() is. However, readdir_r() cannot correctly handle platforms - // with unlimited or variable NAME_MAX. Many modern platforms guarantee - // thread safety for readdir() as long an individual DIR* is not accessed - // concurrently, which is sufficient for Rust. - let entry_ptr = match abi::readdir(self.inner.dirp.as_raw_fd()) { - abi::DirectoryEntry::Invalid(e) => { - // We either encountered an error, or reached the end. Either way, - // the next call to next() should return None. - self.end_of_stream = true; - - return Some(Err(Error::from_raw_os_error(e))); - } - abi::DirectoryEntry::Valid(ptr) => { - if ptr.is_null() { - return None; - } - - ptr - } + let dir = unsafe { + &*(self.inner.dir.as_ptr().offset(offset.try_into().unwrap()) as *const dirent64) + }; + + if counter == self.pos.try_into().unwrap() { + self.pos += 1; + + // After dirent64, the file name is stored. d_reclen represents the length of the dirent64 + // plus the length of the file name. Consequently, file name has a size of d_reclen minus + // the size of dirent64. The file name is always a C string and terminated by `\0`. + // Consequently, we are able to ignore the last byte. + let name_bytes = unsafe { + core::slice::from_raw_parts( + &dir.d_name as *const _ as *const u8, + dir.d_reclen as usize - core::mem::size_of::() - 1, + ) + .to_vec() }; - - macro_rules! offset_ptr { - ($entry_ptr:expr, $field:ident) => {{ - const OFFSET: isize = { - let delusion = MaybeUninit::::uninit(); - let entry_ptr = delusion.as_ptr(); - unsafe { - ptr::addr_of!((*entry_ptr).$field) - .cast::() - .offset_from(entry_ptr.cast::()) - } - }; - if true { - // Cast to the same type determined by the else branch. - $entry_ptr.byte_offset(OFFSET).cast::<_>() - } else { - #[allow(deref_nullptr)] - { - ptr::addr_of!((*ptr::null::()).$field) - } - } - }}; - } - - // d_name is NOT guaranteed to be null-terminated. - let name_bytes = core::slice::from_raw_parts( - offset_ptr!(entry_ptr, d_name) as *const u8, - *offset_ptr!(entry_ptr, d_namelen) as usize, - ) - .to_vec(); - - if name_bytes == b"." || name_bytes == b".." { - continue; - } - - let name = OsString::from_vec(name_bytes); - - let entry = dirent_min { - d_ino: *offset_ptr!(entry_ptr, d_ino), - d_type: *offset_ptr!(entry_ptr, d_type), + let entry = DirEntry { + root: self.inner.root.clone(), + ino: dir.d_ino, + type_: dir.d_type as u32, + name: OsString::from_vec(name_bytes), }; - return Some(Ok(DirEntry { entry, name: name, dir: Arc::clone(&self.inner) })); + return Some(Ok(entry)); } + + counter += 1; + + // move to the next dirent64, which is directly stored after the previous one + offset = offset + dir.d_off; } } } impl DirEntry { pub fn path(&self) -> PathBuf { - self.dir.root.join(self.file_name_os_str()) + self.root.join(self.file_name_os_str()) } pub fn file_name(&self) -> OsString { @@ -265,16 +252,18 @@ impl DirEntry { } pub fn metadata(&self) -> io::Result { - lstat(&self.path()) + let mut path = self.path(); + path.set_file_name(self.file_name_os_str()); + lstat(&path) } pub fn file_type(&self) -> io::Result { - Ok(FileType { mode: self.entry.d_type }) + Ok(FileType { mode: self.type_ as u32 }) } #[allow(dead_code)] pub fn ino(&self) -> u64 { - self.entry.d_ino + self.ino } pub fn file_name_os_str(&self) -> &OsStr { @@ -456,7 +445,7 @@ impl DirBuilder { } pub fn mkdir(&self, path: &Path) -> io::Result<()> { - run_path_with_cstr(path, |path| { + run_path_with_cstr(path, &|path| { cvt(unsafe { abi::mkdir(path.as_ptr(), self.mode) }).map(|_| ()) }) } @@ -519,11 +508,42 @@ impl FromRawFd for File { } pub fn readdir(path: &Path) -> io::Result { - let fd_raw = run_path_with_cstr(path, |path| cvt(unsafe { abi::opendir(path.as_ptr()) }))?; + let fd_raw = run_path_with_cstr(path, &|path| cvt(unsafe { abi::opendir(path.as_ptr()) }))?; let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) }; let root = path.to_path_buf(); - let inner = InnerReadDir { dirp: fd, root }; - Ok(ReadDir::new(inner)) + + // read all director entries + let mut vec: Vec = Vec::new(); + let mut sz = 512; + loop { + // reserve memory to receive all directory entries + vec.resize(sz, 0); + + let readlen = + unsafe { abi::getdents64(fd.as_raw_fd(), vec.as_mut_ptr() as *mut dirent64, sz) }; + if readlen > 0 { + // shrink down to the minimal size + vec.resize(readlen.try_into().unwrap(), 0); + break; + } + + // if the buffer is too small, getdents64 returns EINVAL + // otherwise, getdents64 returns an error number + if readlen != (-abi::errno::EINVAL).into() { + return Err(Error::from_raw_os_error(readlen.try_into().unwrap())); + } + + // we don't have enough memory => try to increase the vector size + sz = sz * 2; + + // 1 MB for directory entries should be enough + // stop here to avoid an endless loop + if sz > 0x100000 { + return Err(Error::from(ErrorKind::Uncategorized)); + } + } + + Ok(ReadDir::new(InnerReadDir::new(root, vec))) } pub fn unlink(path: &Path) -> io::Result<()> { @@ -539,12 +559,11 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> { } pub fn rmdir(path: &Path) -> io::Result<()> { - run_path_with_cstr(path, |path| cvt(unsafe { abi::rmdir(path.as_ptr()) }).map(|_| ())) + run_path_with_cstr(path, &|path| cvt(unsafe { abi::rmdir(path.as_ptr()) }).map(|_| ())) } pub fn remove_dir_all(_path: &Path) -> io::Result<()> { - //unsupported() - Ok(()) + unsupported() } pub fn readlink(_p: &Path) -> io::Result { @@ -560,7 +579,7 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { } pub fn stat(path: &Path) -> io::Result { - run_path_with_cstr(path, |path| { + run_path_with_cstr(path, &|path| { let mut stat_val: stat_struct = unsafe { mem::zeroed() }; cvt(unsafe { abi::stat(path.as_ptr(), &mut stat_val) })?; Ok(FileAttr::from_stat(stat_val)) @@ -568,7 +587,7 @@ pub fn stat(path: &Path) -> io::Result { } pub fn lstat(path: &Path) -> io::Result { - run_path_with_cstr(path, |path| { + run_path_with_cstr(path, &|path| { let mut stat_val: stat_struct = unsafe { mem::zeroed() }; cvt(unsafe { abi::lstat(path.as_ptr(), &mut stat_val) })?; Ok(FileAttr::from_stat(stat_val)) From a50490c5793516acb03c7b27d26177137b3f16f5 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Fri, 1 Mar 2024 14:12:11 +0100 Subject: [PATCH 0020/1468] Use FxIndexMap instead FxHashMap to stabilize iteration order in EffectiveVisibilities. Part of https://github.com/rust-lang/compiler-team/issues/533 --- compiler/rustc_middle/src/middle/privacy.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 500536a9e9ed4..46520d69e1838 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -2,7 +2,7 @@ //! outside their scopes. This pass will also generate a set of exported items //! which are available for use externally when compiled as a library. use crate::ty::{TyCtxt, Visibility}; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_hir::def::DefKind; use rustc_macros::HashStable; @@ -90,7 +90,7 @@ impl EffectiveVisibility { /// Holds a map of effective visibilities for reachable HIR nodes. #[derive(Clone, Debug)] pub struct EffectiveVisibilities { - map: FxHashMap, + map: FxIndexMap, } impl EffectiveVisibilities { @@ -130,9 +130,8 @@ impl EffectiveVisibilities { eff_vis: &EffectiveVisibility, tcx: TyCtxt<'_>, ) { - use std::collections::hash_map::Entry; match self.map.entry(def_id) { - Entry::Occupied(mut occupied) => { + IndexEntry::Occupied(mut occupied) => { let old_eff_vis = occupied.get_mut(); for l in Level::all_levels() { let vis_at_level = eff_vis.at_level(l); @@ -145,7 +144,7 @@ impl EffectiveVisibilities { } old_eff_vis } - Entry::Vacant(vacant) => vacant.insert(*eff_vis), + IndexEntry::Vacant(vacant) => vacant.insert(*eff_vis), }; } From 499a53dd71aecfff2bf5863d509b8c21a6541841 Mon Sep 17 00:00:00 2001 From: avhz Date: Sun, 3 Mar 2024 07:24:19 +0100 Subject: [PATCH 0021/1468] feat: add SIMD float math functions (exp, exp2, log, log2, log10, sin, cos). --- crates/std_float/src/lib.rs | 93 +++++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 3 deletions(-) diff --git a/crates/std_float/src/lib.rs b/crates/std_float/src/lib.rs index 4c547777fdeb3..98f10e94a301c 100644 --- a/crates/std_float/src/lib.rs +++ b/crates/std_float/src/lib.rs @@ -65,6 +65,62 @@ pub trait StdFloat: Sealed + Sized { unsafe { intrinsics::simd_fsqrt(self) } } + /// Produces a vector where every lane has the sine of the value + /// in the equivalently-indexed lane in `self`. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original value"] + fn sin(self) -> Self { + unsafe { intrinsics::simd_fsin(self) } + } + + /// Produces a vector where every lane has the cosine of the value + /// in the equivalently-indexed lane in `self`. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original value"] + fn cos(self) -> Self { + unsafe { intrinsics::simd_fcos(self) } + } + + /// Produces a vector where every lane has the exponential (base e) of the value + /// in the equivalently-indexed lane in `self`. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original value"] + fn exp(self) -> Self { + unsafe { intrinsics::simd_fexp(self) } + } + + /// Produces a vector where every lane has the exponential (base 2) of the value + /// in the equivalently-indexed lane in `self`. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original value"] + fn exp2(self) -> Self { + unsafe { intrinsics::simd_fexp2(self) } + } + + /// Produces a vector where every lane has the natural logarithm of the value + /// in the equivalently-indexed lane in `self`. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original value"] + fn log(self) -> Self { + unsafe { intrinsics::simd_flog(self) } + } + + /// Produces a vector where every lane has the base-2 logarithm of the value + /// in the equivalently-indexed lane in `self`. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original value"] + fn log2(self) -> Self { + unsafe { intrinsics::simd_flog2(self) } + } + + /// Produces a vector where every lane has the base-10 logarithm of the value + /// in the equivalently-indexed lane in `self`. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original value"] + fn log10(self) -> Self { + unsafe { intrinsics::simd_flog10(self) } + } + /// Returns the smallest integer greater than or equal to each lane. #[must_use = "method returns a new vector and does not mutate the original value"] #[inline] @@ -127,13 +183,36 @@ where } #[cfg(test)] -mod tests { +mod tests_simd_floats { use super::*; use simd::prelude::*; #[test] - fn everything_works() { + fn everything_works_f32() { let x = f32x4::from_array([0.1, 0.5, 0.6, -1.5]); + + let x2 = x + x; + let _xc = x.ceil(); + let _xf = x.floor(); + let _xr = x.round(); + let _xt = x.trunc(); + let _xfma = x.mul_add(x, x); + let _xsqrt = x.sqrt(); + let _abs_mul = x2.abs() * x2; + + let _fexp = x.exp(); + let _fexp2 = x.exp2(); + let _flog = x.log(); + let _flog2 = x.log2(); + let _flog10 = x.log10(); + let _fsin = x.sin(); + let _fcos = x.cos(); + } + + #[test] + fn everything_works_f64() { + let x = f64x4::from_array([0.1, 0.5, 0.6, -1.5]); + let x2 = x + x; let _xc = x.ceil(); let _xf = x.floor(); @@ -141,6 +220,14 @@ mod tests { let _xt = x.trunc(); let _xfma = x.mul_add(x, x); let _xsqrt = x.sqrt(); - let _ = x2.abs() * x2; + let _abs_mul = x2.abs() * x2; + + let _fexp = x.exp(); + let _fexp2 = x.exp2(); + let _flog = x.log(); + let _flog2 = x.log2(); + let _flog10 = x.log10(); + let _fsin = x.sin(); + let _fcos = x.cos(); } } From d561a84d48b64dc0f23e0f0a152b72e5a8f62161 Mon Sep 17 00:00:00 2001 From: Veera Date: Sun, 3 Mar 2024 09:10:06 -0500 Subject: [PATCH 0022/1468] Update tests --- tests/ui/asm/bad-template.aarch64.stderr | 4 ++-- tests/ui/asm/bad-template.x86_64.stderr | 4 ++-- tests/ui/asm/x86_64/type-check-3.stderr | 16 ++++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/ui/asm/bad-template.aarch64.stderr b/tests/ui/asm/bad-template.aarch64.stderr index b18946d7c6d85..5023cf317d7b0 100644 --- a/tests/ui/asm/bad-template.aarch64.stderr +++ b/tests/ui/asm/bad-template.aarch64.stderr @@ -194,8 +194,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{:foo}", in(reg) foo); | ^^^^^^ --- for this argument | - = help: use `{0:w}` to have the register formatted as `w0` - = help: or use `{0:x}` to keep the default formatting of `x0` + = help: use `{0:w}` to have the register formatted as `w0` (for 32-bit values) + = help: or use `{0:x}` to keep the default formatting of `x0` (for 64-bit values) = note: `#[warn(asm_sub_register)]` on by default error: aborting due to 21 previous errors; 1 warning emitted diff --git a/tests/ui/asm/bad-template.x86_64.stderr b/tests/ui/asm/bad-template.x86_64.stderr index 2f584c30a3282..1b9775636f564 100644 --- a/tests/ui/asm/bad-template.x86_64.stderr +++ b/tests/ui/asm/bad-template.x86_64.stderr @@ -194,8 +194,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{:foo}", in(reg) foo); | ^^^^^^ --- for this argument | - = help: use `{0:e}` to have the register formatted as `eax` - = help: or use `{0:r}` to keep the default formatting of `rax` + = help: use `{0:e}` to have the register formatted as `eax` (for 32-bit values) + = help: or use `{0:r}` to keep the default formatting of `rax` (for 64-bit values) = note: `#[warn(asm_sub_register)]` on by default error: aborting due to 21 previous errors; 1 warning emitted diff --git a/tests/ui/asm/x86_64/type-check-3.stderr b/tests/ui/asm/x86_64/type-check-3.stderr index 1baf50ff6e0c7..34bfcd71caca7 100644 --- a/tests/ui/asm/x86_64/type-check-3.stderr +++ b/tests/ui/asm/x86_64/type-check-3.stderr @@ -44,8 +44,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{0} {0}", in(reg) 0i16); | ^^^ ^^^ ---- for this argument | - = help: use `{0:x}` to have the register formatted as `ax` - = help: or use `{0:r}` to keep the default formatting of `rax` + = help: use `{0:x}` to have the register formatted as `ax` (for 16-bit values) + = help: or use `{0:r}` to keep the default formatting of `rax` (for 64-bit values) = note: `#[warn(asm_sub_register)]` on by default warning: formatting may not be suitable for sub-register argument @@ -54,8 +54,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{0} {0:x}", in(reg) 0i16); | ^^^ ---- for this argument | - = help: use `{0:x}` to have the register formatted as `ax` - = help: or use `{0:r}` to keep the default formatting of `rax` + = help: use `{0:x}` to have the register formatted as `ax` (for 16-bit values) + = help: or use `{0:r}` to keep the default formatting of `rax` (for 64-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:38:15 @@ -63,8 +63,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(reg) 0i32); | ^^ ---- for this argument | - = help: use `{0:e}` to have the register formatted as `eax` - = help: or use `{0:r}` to keep the default formatting of `rax` + = help: use `{0:e}` to have the register formatted as `eax` (for 32-bit values) + = help: or use `{0:r}` to keep the default formatting of `rax` (for 64-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:41:15 @@ -72,8 +72,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(ymm_reg) 0i64); | ^^ ---- for this argument | - = help: use `{0:x}` to have the register formatted as `xmm0` - = help: or use `{0:y}` to keep the default formatting of `ymm0` + = help: use `{0:x}` to have the register formatted as `xmm0` (for 128-bit values) + = help: or use `{0:y}` to keep the default formatting of `ymm0` (for 256-bit values) error: type `i8` cannot be used with this register class --> $DIR/type-check-3.rs:52:28 From 9aac0c9ae39de7046af35788d19e5f9310e4cbb7 Mon Sep 17 00:00:00 2001 From: Veera Date: Sun, 3 Mar 2024 09:34:26 -0500 Subject: [PATCH 0023/1468] Mention Register Size in `#[warn(asm_sub_register)]` Fixes #121593 --- .../src/check/intrinsicck.rs | 22 ++++++++---- compiler/rustc_target/src/asm/aarch64.rs | 26 ++++++-------- compiler/rustc_target/src/asm/arm.rs | 6 ++-- compiler/rustc_target/src/asm/avr.rs | 6 ++-- compiler/rustc_target/src/asm/bpf.rs | 6 ++-- compiler/rustc_target/src/asm/csky.rs | 6 ++-- compiler/rustc_target/src/asm/hexagon.rs | 6 ++-- compiler/rustc_target/src/asm/loongarch.rs | 6 ++-- compiler/rustc_target/src/asm/m68k.rs | 6 ++-- compiler/rustc_target/src/asm/mips.rs | 6 ++-- compiler/rustc_target/src/asm/mod.rs | 20 +++++++---- compiler/rustc_target/src/asm/msp430.rs | 6 ++-- compiler/rustc_target/src/asm/nvptx.rs | 6 ++-- compiler/rustc_target/src/asm/powerpc.rs | 6 ++-- compiler/rustc_target/src/asm/riscv.rs | 6 ++-- compiler/rustc_target/src/asm/s390x.rs | 6 ++-- compiler/rustc_target/src/asm/spirv.rs | 6 ++-- compiler/rustc_target/src/asm/wasm.rs | 6 ++-- compiler/rustc_target/src/asm/x86.rs | 34 ++++++++----------- 19 files changed, 100 insertions(+), 92 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index d03b02f028da1..c78a6004f7ac2 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -6,7 +6,9 @@ use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::Symbol; use rustc_target::abi::FieldIdx; -use rustc_target::asm::{InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType}; +use rustc_target::asm::{ + InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType, ModifierInfo, +}; pub struct InlineAsmCtxt<'a, 'tcx> { tcx: TyCtxt<'tcx>, @@ -253,8 +255,11 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } // Check whether a modifier is suggested for using this type. - if let Some((suggested_modifier, suggested_result)) = - reg_class.suggest_modifier(asm_arch, asm_ty) + if let Some(ModifierInfo { + modifier: suggested_modifier, + result: suggested_result, + size: suggested_size, + }) = reg_class.suggest_modifier(asm_arch, asm_ty) { // Search for any use of this operand without a modifier and emit // the suggestion for them. @@ -268,8 +273,11 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } } if !spans.is_empty() { - let (default_modifier, default_result) = - reg_class.default_modifier(asm_arch).unwrap(); + let ModifierInfo { + modifier: default_modifier, + result: default_result, + size: default_size, + } = reg_class.default_modifier(asm_arch).unwrap(); self.tcx.node_span_lint( lint::builtin::ASM_SUB_REGISTER, expr.hir_id, @@ -278,10 +286,10 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { |lint| { lint.span_label(expr.span, "for this argument"); lint.help(format!( - "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}`", + "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}` (for {suggested_size}-bit values)", )); lint.help(format!( - "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}`", + "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}` (for {default_size}-bit values)", )); }, ); diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs index 97132311a5c9a..70528c1222c44 100644 --- a/compiler/rustc_target/src/asm/aarch64.rs +++ b/compiler/rustc_target/src/asm/aarch64.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use crate::spec::{RelocModel, Target}; use rustc_data_structures::fx::FxIndexSet; use rustc_macros::HashStable_Generic; @@ -27,32 +27,28 @@ impl AArch64InlineAsmRegClass { None } - pub fn suggest_modifier( - self, - _arch: InlineAsmArch, - ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + pub fn suggest_modifier(self, _arch: InlineAsmArch, ty: InlineAsmType) -> Option { match self { Self::reg => match ty.size().bits() { 64 => None, - _ => Some(('w', "w0")), + _ => Some(('w', "w0", 32).into()), }, Self::vreg | Self::vreg_low16 => match ty.size().bits() { - 8 => Some(('b', "b0")), - 16 => Some(('h', "h0")), - 32 => Some(('s', "s0")), - 64 => Some(('d', "d0")), - 128 => Some(('q', "q0")), + 8 => Some(('b', "b0", 8).into()), + 16 => Some(('h', "h0", 16).into()), + 32 => Some(('s', "s0", 32).into()), + 64 => Some(('d', "d0", 64).into()), + 128 => Some(('q', "q0", 128).into()), _ => None, }, Self::preg => None, } } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { match self { - Self::reg => Some(('x', "x0")), - Self::vreg | Self::vreg_low16 => Some(('v', "v0")), + Self::reg => Some(('x', "x0", 64).into()), + Self::vreg | Self::vreg_low16 => Some(('v', "v0", 128).into()), Self::preg => None, } } diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs index 514e30ae0204d..f56dbac708b65 100644 --- a/compiler/rustc_target/src/asm/arm.rs +++ b/compiler/rustc_target/src/asm/arm.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use crate::spec::{RelocModel, Target}; use rustc_data_structures::fx::FxIndexSet; use rustc_macros::HashStable_Generic; @@ -35,11 +35,11 @@ impl ArmInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/avr.rs b/compiler/rustc_target/src/asm/avr.rs index 9a96a61f5b2cd..eab38a4a4f4a3 100644 --- a/compiler/rustc_target/src/asm/avr.rs +++ b/compiler/rustc_target/src/asm/avr.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -29,11 +29,11 @@ impl AvrInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/bpf.rs b/compiler/rustc_target/src/asm/bpf.rs index 3b03766a089b2..9bc94274675d5 100644 --- a/compiler/rustc_target/src/asm/bpf.rs +++ b/compiler/rustc_target/src/asm/bpf.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -23,11 +23,11 @@ impl BpfInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/csky.rs b/compiler/rustc_target/src/asm/csky.rs index db3d8106040b6..64607ee4b81db 100644 --- a/compiler/rustc_target/src/asm/csky.rs +++ b/compiler/rustc_target/src/asm/csky.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -23,11 +23,11 @@ impl CSKYInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/hexagon.rs b/compiler/rustc_target/src/asm/hexagon.rs index d20270ac9e95a..19da7b8084853 100644 --- a/compiler/rustc_target/src/asm/hexagon.rs +++ b/compiler/rustc_target/src/asm/hexagon.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -22,11 +22,11 @@ impl HexagonInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/loongarch.rs b/compiler/rustc_target/src/asm/loongarch.rs index 9d1a4f3eeeafa..15d0f54ce3b6e 100644 --- a/compiler/rustc_target/src/asm/loongarch.rs +++ b/compiler/rustc_target/src/asm/loongarch.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -23,11 +23,11 @@ impl LoongArchInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/m68k.rs b/compiler/rustc_target/src/asm/m68k.rs index 8c857550cf21a..ac94dcc03dc6b 100644 --- a/compiler/rustc_target/src/asm/m68k.rs +++ b/compiler/rustc_target/src/asm/m68k.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -24,11 +24,11 @@ impl M68kInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/mips.rs b/compiler/rustc_target/src/asm/mips.rs index 4e7c2eb1bf88e..0ac1a43ae183b 100644 --- a/compiler/rustc_target/src/asm/mips.rs +++ b/compiler/rustc_target/src/asm/mips.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -23,11 +23,11 @@ impl MipsInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index a11884bea268f..bfb6ae0b2d9d2 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -6,6 +6,18 @@ use rustc_span::Symbol; use std::fmt; use std::str::FromStr; +pub struct ModifierInfo { + pub modifier: char, + pub result: &'static str, + pub size: u64, +} + +impl From<(char, &'static str, u64)> for ModifierInfo { + fn from(value: (char, &'static str, u64)) -> Self { + Self { modifier: value.0, result: value.1, size: value.2 } + } +} + macro_rules! def_reg_class { ($arch:ident $arch_regclass:ident { $( @@ -512,11 +524,7 @@ impl InlineAsmRegClass { /// Such suggestions are useful if a type smaller than the full register /// size is used and a modifier can be used to point to the subregister of /// the correct size. - pub fn suggest_modifier( - self, - arch: InlineAsmArch, - ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + pub fn suggest_modifier(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option { match self { Self::X86(r) => r.suggest_modifier(arch, ty), Self::Arm(r) => r.suggest_modifier(arch, ty), @@ -545,7 +553,7 @@ impl InlineAsmRegClass { /// This is only needed when the register class can suggest a modifier, so /// that the user can be shown how to get the default behavior without a /// warning. - pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, arch: InlineAsmArch) -> Option { match self { Self::X86(r) => r.default_modifier(arch), Self::Arm(r) => r.default_modifier(arch), diff --git a/compiler/rustc_target/src/asm/msp430.rs b/compiler/rustc_target/src/asm/msp430.rs index a27d6390a72e8..439f3ba0b575b 100644 --- a/compiler/rustc_target/src/asm/msp430.rs +++ b/compiler/rustc_target/src/asm/msp430.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -22,11 +22,11 @@ impl Msp430InlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/nvptx.rs b/compiler/rustc_target/src/asm/nvptx.rs index 8e1e91e7c5f1f..57aa50fceb88b 100644 --- a/compiler/rustc_target/src/asm/nvptx.rs +++ b/compiler/rustc_target/src/asm/nvptx.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; @@ -23,11 +23,11 @@ impl NvptxInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/powerpc.rs b/compiler/rustc_target/src/asm/powerpc.rs index d3ccb30350a27..4e8cbe34de948 100644 --- a/compiler/rustc_target/src/asm/powerpc.rs +++ b/compiler/rustc_target/src/asm/powerpc.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -26,11 +26,11 @@ impl PowerPCInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs index dea6d50fe2ba8..2505d36f5b8a4 100644 --- a/compiler/rustc_target/src/asm/riscv.rs +++ b/compiler/rustc_target/src/asm/riscv.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use crate::spec::{RelocModel, Target}; use rustc_data_structures::fx::FxIndexSet; use rustc_macros::HashStable_Generic; @@ -26,11 +26,11 @@ impl RiscVInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/s390x.rs b/compiler/rustc_target/src/asm/s390x.rs index b8afeb824d847..6bc668454b191 100644 --- a/compiler/rustc_target/src/asm/s390x.rs +++ b/compiler/rustc_target/src/asm/s390x.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -24,11 +24,11 @@ impl S390xInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/spirv.rs b/compiler/rustc_target/src/asm/spirv.rs index 31073da10b21b..d13a6131f9ab4 100644 --- a/compiler/rustc_target/src/asm/spirv.rs +++ b/compiler/rustc_target/src/asm/spirv.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; @@ -21,11 +21,11 @@ impl SpirVInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/wasm.rs b/compiler/rustc_target/src/asm/wasm.rs index f095b7c6e118c..eb0b23ef43dd8 100644 --- a/compiler/rustc_target/src/asm/wasm.rs +++ b/compiler/rustc_target/src/asm/wasm.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; @@ -21,11 +21,11 @@ impl WasmInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { None } diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs index 3902dac7ff654..3b5da8806ccc2 100644 --- a/compiler/rustc_target/src/asm/x86.rs +++ b/compiler/rustc_target/src/asm/x86.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use crate::spec::{RelocModel, Target}; use rustc_data_structures::fx::FxIndexSet; use rustc_macros::HashStable_Generic; @@ -53,32 +53,28 @@ impl X86InlineAsmRegClass { } } - pub fn suggest_modifier( - self, - arch: InlineAsmArch, - ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + pub fn suggest_modifier(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option { match self { Self::reg => match ty.size().bits() { - 16 => Some(('x', "ax")), - 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax")), + 16 => Some(('x', "ax", 16).into()), + 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax", 32).into()), _ => None, }, Self::reg_abcd => match ty.size().bits() { - 16 => Some(('x', "ax")), - 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax")), + 16 => Some(('x', "ax", 16).into()), + 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax", 32).into()), _ => None, }, Self::reg_byte => None, Self::xmm_reg => None, Self::ymm_reg => match ty.size().bits() { 256 => None, - _ => Some(('x', "xmm0")), + _ => Some(('x', "xmm0", 128).into()), }, Self::zmm_reg => match ty.size().bits() { 512 => None, - 256 => Some(('y', "ymm0")), - _ => Some(('x', "xmm0")), + 256 => Some(('y', "ymm0", 256).into()), + _ => Some(('x', "xmm0", 128).into()), }, Self::kreg | Self::kreg0 => None, Self::mmx_reg | Self::x87_reg => None, @@ -86,19 +82,19 @@ impl X86InlineAsmRegClass { } } - pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, arch: InlineAsmArch) -> Option { match self { Self::reg | Self::reg_abcd => { if arch == InlineAsmArch::X86_64 { - Some(('r', "rax")) + Some(('r', "rax", 64).into()) } else { - Some(('e', "eax")) + Some(('e', "eax", 32).into()) } } Self::reg_byte => None, - Self::xmm_reg => Some(('x', "xmm0")), - Self::ymm_reg => Some(('y', "ymm0")), - Self::zmm_reg => Some(('z', "zmm0")), + Self::xmm_reg => Some(('x', "xmm0", 128).into()), + Self::ymm_reg => Some(('y', "ymm0", 256).into()), + Self::zmm_reg => Some(('z', "zmm0", 512).into()), Self::kreg | Self::kreg0 => None, Self::mmx_reg | Self::x87_reg => None, Self::tmm_reg => None, From 5b5b259bf3488a07501e638b0af1d00af0c3c1c0 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sun, 3 Mar 2024 10:06:20 -0500 Subject: [PATCH 0024/1468] Test std_float --- Cargo.lock | 1 + crates/std_float/Cargo.toml | 3 ++ crates/std_float/src/lib.rs | 61 ++++++------------------------- crates/std_float/tests/float.rs | 64 +++++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 51 deletions(-) create mode 100644 crates/std_float/tests/float.rs diff --git a/Cargo.lock b/Cargo.lock index 46312c09657d5..1ede15ff00286 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -177,6 +177,7 @@ name = "std_float" version = "0.1.0" dependencies = [ "core_simd", + "test_helpers", ] [[package]] diff --git a/crates/std_float/Cargo.toml b/crates/std_float/Cargo.toml index 84c69774cbdfe..8842b226104dc 100644 --- a/crates/std_float/Cargo.toml +++ b/crates/std_float/Cargo.toml @@ -8,6 +8,9 @@ edition = "2021" [dependencies] core_simd = { path = "../core_simd", default-features = false } +[dev-dependencies.test_helpers] +path = "../test_helpers" + [features] default = ["as_crate"] as_crate = [] diff --git a/crates/std_float/src/lib.rs b/crates/std_float/src/lib.rs index 98f10e94a301c..5237f7cce15cf 100644 --- a/crates/std_float/src/lib.rs +++ b/crates/std_float/src/lib.rs @@ -101,10 +101,19 @@ pub trait StdFloat: Sealed + Sized { /// in the equivalently-indexed lane in `self`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn log(self) -> Self { + fn ln(self) -> Self { unsafe { intrinsics::simd_flog(self) } } + /// Produces a vector where every lane has the logarithm with respect to an arbitrary + /// in the equivalently-indexed lanes in `self` and `base`. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original value"] + fn log(self, base: Self) -> Self { + unsafe { intrinsics::simd_div(self.ln(), base.ln()) } + } + + /// Produces a vector where every lane has the base-2 logarithm of the value /// in the equivalently-indexed lane in `self`. #[inline] @@ -181,53 +190,3 @@ where self - self.trunc() } } - -#[cfg(test)] -mod tests_simd_floats { - use super::*; - use simd::prelude::*; - - #[test] - fn everything_works_f32() { - let x = f32x4::from_array([0.1, 0.5, 0.6, -1.5]); - - let x2 = x + x; - let _xc = x.ceil(); - let _xf = x.floor(); - let _xr = x.round(); - let _xt = x.trunc(); - let _xfma = x.mul_add(x, x); - let _xsqrt = x.sqrt(); - let _abs_mul = x2.abs() * x2; - - let _fexp = x.exp(); - let _fexp2 = x.exp2(); - let _flog = x.log(); - let _flog2 = x.log2(); - let _flog10 = x.log10(); - let _fsin = x.sin(); - let _fcos = x.cos(); - } - - #[test] - fn everything_works_f64() { - let x = f64x4::from_array([0.1, 0.5, 0.6, -1.5]); - - let x2 = x + x; - let _xc = x.ceil(); - let _xf = x.floor(); - let _xr = x.round(); - let _xt = x.trunc(); - let _xfma = x.mul_add(x, x); - let _xsqrt = x.sqrt(); - let _abs_mul = x2.abs() * x2; - - let _fexp = x.exp(); - let _fexp2 = x.exp2(); - let _flog = x.log(); - let _flog2 = x.log2(); - let _flog10 = x.log10(); - let _fsin = x.sin(); - let _fcos = x.cos(); - } -} diff --git a/crates/std_float/tests/float.rs b/crates/std_float/tests/float.rs new file mode 100644 index 0000000000000..60bdf00fba886 --- /dev/null +++ b/crates/std_float/tests/float.rs @@ -0,0 +1,64 @@ +#![feature(portable_simd)] + +macro_rules! unary_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func() { + test_helpers::test_unary_elementwise( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_| true, + ) + } + )* + } + } +} + +macro_rules! binary_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func() { + test_helpers::test_binary_elementwise( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_, _| true, + ) + } + )* + } + } +} + +macro_rules! ternary_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func() { + test_helpers::test_ternary_elementwise( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_, _, _| true, + ) + } + )* + } + } +} + +macro_rules! impl_tests { + { $scalar:tt } => { + mod $scalar { + use std_float::StdFloat; + + unary_test! { $scalar, sqrt, sin, cos, exp, exp2, ln, log2, log10, ceil, floor, round, trunc, fract } + binary_test! { $scalar, log } + ternary_test! { $scalar, mul_add } + } + } +} + +impl_tests! { f32 } +impl_tests! { f64 } From e5d5006cf319ef31027d0a0c6c20aa136f7737a4 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sun, 3 Mar 2024 10:11:52 -0500 Subject: [PATCH 0025/1468] Update docs --- crates/std_float/src/lib.rs | 42 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/crates/std_float/src/lib.rs b/crates/std_float/src/lib.rs index 5237f7cce15cf..23ee55d8ac3ac 100644 --- a/crates/std_float/src/lib.rs +++ b/crates/std_float/src/lib.rs @@ -44,7 +44,7 @@ use crate::sealed::Sealed; /// For now this trait is available to permit experimentation with SIMD float /// operations that may lack hardware support, such as `mul_add`. pub trait StdFloat: Sealed + Sized { - /// Fused multiply-add. Computes `(self * a) + b` with only one rounding error, + /// Elementwise fused multiply-add. Computes `(self * a) + b` with only one rounding error, /// yielding a more accurate result than an unfused multiply-add. /// /// Using `mul_add` *may* be more performant than an unfused multiply-add if the target @@ -57,56 +57,56 @@ pub trait StdFloat: Sealed + Sized { unsafe { intrinsics::simd_fma(self, a, b) } } - /// Produces a vector where every lane has the square root value - /// of the equivalently-indexed lane in `self` + /// Produces a vector where every element has the square root value + /// of the equivalently-indexed element in `self` #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn sqrt(self) -> Self { unsafe { intrinsics::simd_fsqrt(self) } } - /// Produces a vector where every lane has the sine of the value - /// in the equivalently-indexed lane in `self`. + /// Produces a vector where every element has the sine of the value + /// in the equivalently-indexed element in `self`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn sin(self) -> Self { unsafe { intrinsics::simd_fsin(self) } } - /// Produces a vector where every lane has the cosine of the value - /// in the equivalently-indexed lane in `self`. + /// Produces a vector where every element has the cosine of the value + /// in the equivalently-indexed element in `self`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn cos(self) -> Self { unsafe { intrinsics::simd_fcos(self) } } - /// Produces a vector where every lane has the exponential (base e) of the value - /// in the equivalently-indexed lane in `self`. + /// Produces a vector where every element has the exponential (base e) of the value + /// in the equivalently-indexed element in `self`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn exp(self) -> Self { unsafe { intrinsics::simd_fexp(self) } } - /// Produces a vector where every lane has the exponential (base 2) of the value - /// in the equivalently-indexed lane in `self`. + /// Produces a vector where every element has the exponential (base 2) of the value + /// in the equivalently-indexed element in `self`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn exp2(self) -> Self { unsafe { intrinsics::simd_fexp2(self) } } - /// Produces a vector where every lane has the natural logarithm of the value - /// in the equivalently-indexed lane in `self`. + /// Produces a vector where every element has the natural logarithm of the value + /// in the equivalently-indexed element in `self`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn ln(self) -> Self { unsafe { intrinsics::simd_flog(self) } } - /// Produces a vector where every lane has the logarithm with respect to an arbitrary - /// in the equivalently-indexed lanes in `self` and `base`. + /// Produces a vector where every element has the logarithm with respect to an arbitrary + /// in the equivalently-indexed elements in `self` and `base`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn log(self, base: Self) -> Self { @@ -114,30 +114,30 @@ pub trait StdFloat: Sealed + Sized { } - /// Produces a vector where every lane has the base-2 logarithm of the value - /// in the equivalently-indexed lane in `self`. + /// Produces a vector where every element has the base-2 logarithm of the value + /// in the equivalently-indexed element in `self`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn log2(self) -> Self { unsafe { intrinsics::simd_flog2(self) } } - /// Produces a vector where every lane has the base-10 logarithm of the value - /// in the equivalently-indexed lane in `self`. + /// Produces a vector where every element has the base-10 logarithm of the value + /// in the equivalently-indexed element in `self`. #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn log10(self) -> Self { unsafe { intrinsics::simd_flog10(self) } } - /// Returns the smallest integer greater than or equal to each lane. + /// Returns the smallest integer greater than or equal to each element. #[must_use = "method returns a new vector and does not mutate the original value"] #[inline] fn ceil(self) -> Self { unsafe { intrinsics::simd_ceil(self) } } - /// Returns the largest integer value less than or equal to each lane. + /// Returns the largest integer value less than or equal to each element. #[must_use = "method returns a new vector and does not mutate the original value"] #[inline] fn floor(self) -> Self { From bcedde54568f6bfb0f150ff8fe5d26f427b129d3 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sun, 3 Mar 2024 10:28:33 -0500 Subject: [PATCH 0026/1468] Fix formatting --- crates/std_float/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/std_float/src/lib.rs b/crates/std_float/src/lib.rs index 23ee55d8ac3ac..44bbcba412f6d 100644 --- a/crates/std_float/src/lib.rs +++ b/crates/std_float/src/lib.rs @@ -113,7 +113,6 @@ pub trait StdFloat: Sealed + Sized { unsafe { intrinsics::simd_div(self.ln(), base.ln()) } } - /// Produces a vector where every element has the base-2 logarithm of the value /// in the equivalently-indexed element in `self`. #[inline] From 2f062b8f5eb40d566b2e88ab960b176687167faa Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sun, 3 Mar 2024 10:29:32 -0500 Subject: [PATCH 0027/1468] Fix wasm tests --- crates/std_float/Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/std_float/Cargo.toml b/crates/std_float/Cargo.toml index 8842b226104dc..0896094ee63f4 100644 --- a/crates/std_float/Cargo.toml +++ b/crates/std_float/Cargo.toml @@ -11,6 +11,10 @@ core_simd = { path = "../core_simd", default-features = false } [dev-dependencies.test_helpers] path = "../test_helpers" +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +wasm-bindgen = "0.2" +wasm-bindgen-test = "0.3" + [features] default = ["as_crate"] as_crate = [] From 59a3a5dba314d4b693d141a56fde2e6a879d0af0 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Sun, 3 Mar 2024 07:55:23 -0800 Subject: [PATCH 0028/1468] Clarify atomic bit validity The previous definition used the phrase "representation", which is ambiguous given the current state of memory model nomenclature in Rust. The new wording clarifies that size and bit validity are guaranteed to match the corresponding native integer type. --- library/core/src/sync/atomic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 45193c11e1d6b..303f1bf8f65e4 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -2121,7 +2121,7 @@ macro_rules! atomic_int { $int_type:ident $atomic_type:ident) => { /// An integer type which can be safely shared between threads. /// - /// This type has the same in-memory representation as the underlying + /// This type has the same in-memory size and bit validity as the underlying /// integer type, [` #[doc = $s_int_type] /// `]. From db34b082c0895e80bc9eda17a4c859c51e6509bd Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Sun, 3 Mar 2024 07:57:35 -0800 Subject: [PATCH 0029/1468] Clarify bit validity for AtomicBool --- library/core/src/sync/atomic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 303f1bf8f65e4..5947f6569a7fa 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -243,7 +243,7 @@ const EMULATE_ATOMIC_BOOL: bool = /// A boolean type which can be safely shared between threads. /// -/// This type has the same in-memory representation as a [`bool`]. +/// This type has the same memory layout and bit validity as a [`bool`]. /// /// **Note**: This type is only available on platforms that support atomic /// loads and stores of `u8`. From 00d21c91f0cafc97dabe261f934af7ea0df089b4 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Sun, 3 Mar 2024 08:04:41 -0800 Subject: [PATCH 0030/1468] Document AtomicPtr bit validity --- library/core/src/sync/atomic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 5947f6569a7fa..4eaacdfe7945b 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -272,7 +272,7 @@ unsafe impl Sync for AtomicBool {} /// A raw pointer type which can be safely shared between threads. /// -/// This type has the same in-memory representation as a `*mut T`. +/// This type has the same memory layout and bit validity as a `*mut T`. /// /// **Note**: This type is only available on platforms that support atomic /// loads and stores of pointers. Its size depends on the target pointer's size. From fba87f6892a052fdaed45993a1ec03d6c51ab87d Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Sun, 3 Mar 2024 08:06:41 -0800 Subject: [PATCH 0031/1468] Use "size and alignment" rather than layout --- library/core/src/sync/atomic.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 4eaacdfe7945b..0a0daa49926a2 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -243,7 +243,7 @@ const EMULATE_ATOMIC_BOOL: bool = /// A boolean type which can be safely shared between threads. /// -/// This type has the same memory layout and bit validity as a [`bool`]. +/// This type has the same size, alignment, and bit validity as a [`bool`]. /// /// **Note**: This type is only available on platforms that support atomic /// loads and stores of `u8`. @@ -272,7 +272,7 @@ unsafe impl Sync for AtomicBool {} /// A raw pointer type which can be safely shared between threads. /// -/// This type has the same memory layout and bit validity as a `*mut T`. +/// This type has the same size, alignment, and bit validity as a `*mut T`. /// /// **Note**: This type is only available on platforms that support atomic /// loads and stores of pointers. Its size depends on the target pointer's size. From c50804cfdd5deac77a8eff6e1b2303b6453e786f Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Sun, 3 Mar 2024 08:08:52 -0800 Subject: [PATCH 0032/1468] Update library/core/src/sync/atomic.rs Co-authored-by: Taiki Endo --- library/core/src/sync/atomic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 0a0daa49926a2..aac0acbadbfc9 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -2121,7 +2121,7 @@ macro_rules! atomic_int { $int_type:ident $atomic_type:ident) => { /// An integer type which can be safely shared between threads. /// - /// This type has the same in-memory size and bit validity as the underlying + /// This type has the same size and bit validity as the underlying /// integer type, [` #[doc = $s_int_type] /// `]. From a6e3e02d70708c6777e2b4090e12d7ee528fd1ac Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Sun, 3 Mar 2024 08:12:56 -0800 Subject: [PATCH 0033/1468] Update library/core/src/sync/atomic.rs Co-authored-by: Taiki Endo --- library/core/src/sync/atomic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index aac0acbadbfc9..632f5f4be685d 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -272,7 +272,7 @@ unsafe impl Sync for AtomicBool {} /// A raw pointer type which can be safely shared between threads. /// -/// This type has the same size, alignment, and bit validity as a `*mut T`. +/// This type has the same size and bit validity as a `*mut T`. /// /// **Note**: This type is only available on platforms that support atomic /// loads and stores of pointers. Its size depends on the target pointer's size. From 301d293ef6cc1aeb341b31a5339f7d857a38b930 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 4 Mar 2024 16:38:29 +0000 Subject: [PATCH 0034/1468] Move `iter_nth` to `style`, add machine applicable suggestion --- clippy_lints/src/methods/iter_nth.rs | 49 +++++++++++++---------- clippy_lints/src/methods/mod.rs | 16 ++++---- tests/ui/iter_nth.fixed | 60 ++++++++++++++++++++++++++++ tests/ui/iter_nth.rs | 3 ++ tests/ui/iter_nth.stderr | 48 ++++++++++++++++++---- 5 files changed, 139 insertions(+), 37 deletions(-) create mode 100644 tests/ui/iter_nth.fixed diff --git a/clippy_lints/src/methods/iter_nth.rs b/clippy_lints/src/methods/iter_nth.rs index 121043104058f..5b0b70b4b9659 100644 --- a/clippy_lints/src/methods/iter_nth.rs +++ b/clippy_lints/src/methods/iter_nth.rs @@ -1,10 +1,10 @@ -use super::utils::derefs_to_slice; -use crate::methods::iter_nth_zero; -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::get_type_diagnostic_name; +use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::symbol::sym; +use rustc_span::Span; use super::ITER_NTH; @@ -12,28 +12,33 @@ pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, iter_recv: &'tcx hir::Expr<'tcx>, - nth_recv: &hir::Expr<'_>, - nth_arg: &hir::Expr<'_>, - is_mut: bool, -) { - let mut_str = if is_mut { "_mut" } else { "" }; - let caller_type = if derefs_to_slice(cx, iter_recv, cx.typeck_results().expr_ty(iter_recv)).is_some() { - "slice" - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(iter_recv), sym::Vec) { - "`Vec`" - } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(iter_recv), sym::VecDeque) { - "`VecDeque`" - } else { - iter_nth_zero::check(cx, expr, nth_recv, nth_arg); - return; // caller is not a type that we want to lint + iter_method: &str, + iter_span: Span, + nth_span: Span, +) -> bool { + let caller_type = match get_type_diagnostic_name(cx, cx.typeck_results().expr_ty(iter_recv).peel_refs()) { + Some(sym::Vec) => "`Vec`", + Some(sym::VecDeque) => "`VecDeque`", + _ if cx.typeck_results().expr_ty_adjusted(iter_recv).peel_refs().is_slice() => "slice", + // caller is not a type that we want to lint + _ => return false, }; - span_lint_and_help( + span_lint_and_then( cx, ITER_NTH, expr.span, - &format!("called `.iter{mut_str}().nth()` on a {caller_type}"), - None, - &format!("calling `.get{mut_str}()` is both faster and more readable"), + &format!("called `.{iter_method}().nth()` on a {caller_type}"), + |diag| { + let get_method = if iter_method == "iter_mut" { "get_mut" } else { "get" }; + diag.span_suggestion_verbose( + iter_span.to(nth_span), + format!("`{get_method}` is equivalent but more concise"), + get_method, + Applicability::MachineApplicable, + ); + }, ); + + true } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8a24ccea3a1eb..5445391c03503 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1235,12 +1235,11 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for usage of `.iter().nth()` (and the related - /// `.iter_mut().nth()`) on standard library types with *O*(1) element access. + /// Checks for usage of `.iter().nth()`/`.iter_mut().nth()` on standard library types that have + /// equivalent `.get()`/`.get_mut()` methods. /// /// ### Why is this bad? - /// `.get()` and `.get_mut()` are more efficient and more - /// readable. + /// `.get()` and `.get_mut()` are equivalent but more concise. /// /// ### Example /// ```no_run @@ -1256,7 +1255,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub ITER_NTH, - perf, + style, "using `.iter().nth()` on a standard library type with O(1) element access" } @@ -4723,8 +4722,11 @@ impl Methods { iter_overeager_cloned::Op::LaterCloned, false, ), - Some(("iter", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), - Some(("iter_mut", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), + Some((iter_method @ ("iter" | "iter_mut"), iter_recv, [], iter_span, _)) => { + if !iter_nth::check(cx, expr, iter_recv, iter_method, iter_span, span) { + iter_nth_zero::check(cx, expr, recv, n_arg); + } + }, _ => iter_nth_zero::check(cx, expr, recv, n_arg), }, ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"), diff --git a/tests/ui/iter_nth.fixed b/tests/ui/iter_nth.fixed new file mode 100644 index 0000000000000..aff3731a88372 --- /dev/null +++ b/tests/ui/iter_nth.fixed @@ -0,0 +1,60 @@ +//@aux-build:option_helpers.rs + +#![warn(clippy::iter_nth)] +#![allow(clippy::useless_vec)] + +#[macro_use] +extern crate option_helpers; + +use option_helpers::IteratorFalsePositives; +use std::collections::VecDeque; + +/// Struct to generate false positives for things with `.iter()`. +#[derive(Copy, Clone)] +struct HasIter; + +impl HasIter { + fn iter(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } + + fn iter_mut(self) -> IteratorFalsePositives { + IteratorFalsePositives { foo: 0 } + } +} + +/// Checks implementation of `ITER_NTH` lint. +fn iter_nth() { + let mut some_vec = vec![0, 1, 2, 3]; + let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let mut some_vec_deque: VecDeque<_> = some_vec.iter().cloned().collect(); + + { + // Make sure we lint `.iter()` for relevant types. + let bad_vec = some_vec.get(3); + let bad_slice = &some_vec[..].get(3); + let bad_boxed_slice = boxed_slice.get(3); + let bad_vec_deque = some_vec_deque.get(3); + } + + { + // Make sure we lint `.iter_mut()` for relevant types. + let bad_vec = some_vec.get_mut(3); + } + { + let bad_slice = &some_vec[..].get_mut(3); + } + { + let bad_vec_deque = some_vec_deque.get_mut(3); + } + + let vec_ref = &Vec::::new(); + vec_ref.get(3); + + // Make sure we don't lint for non-relevant types. + let false_positive = HasIter; + let ok = false_positive.iter().nth(3); + let ok_mut = false_positive.iter_mut().nth(3); +} + +fn main() {} diff --git a/tests/ui/iter_nth.rs b/tests/ui/iter_nth.rs index 7c567bb81d88a..89d68044dddac 100644 --- a/tests/ui/iter_nth.rs +++ b/tests/ui/iter_nth.rs @@ -48,6 +48,9 @@ fn iter_nth() { let bad_vec_deque = some_vec_deque.iter_mut().nth(3); } + let vec_ref = &Vec::::new(); + vec_ref.iter().nth(3); + // Make sure we don't lint for non-relevant types. let false_positive = HasIter; let ok = false_positive.iter().nth(3); diff --git a/tests/ui/iter_nth.stderr b/tests/ui/iter_nth.stderr index c5dd0c99727be..178463f534754 100644 --- a/tests/ui/iter_nth.stderr +++ b/tests/ui/iter_nth.stderr @@ -4,9 +4,12 @@ error: called `.iter().nth()` on a `Vec` LL | let bad_vec = some_vec.iter().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get()` is both faster and more readable = note: `-D clippy::iter-nth` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::iter_nth)]` +help: `get` is equivalent but more concise + | +LL | let bad_vec = some_vec.get(3); + | ~~~ error: called `.iter().nth()` on a slice --> tests/ui/iter_nth.rs:35:26 @@ -14,7 +17,10 @@ error: called `.iter().nth()` on a slice LL | let bad_slice = &some_vec[..].iter().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get()` is both faster and more readable +help: `get` is equivalent but more concise + | +LL | let bad_slice = &some_vec[..].get(3); + | ~~~ error: called `.iter().nth()` on a slice --> tests/ui/iter_nth.rs:36:31 @@ -22,7 +28,10 @@ error: called `.iter().nth()` on a slice LL | let bad_boxed_slice = boxed_slice.iter().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get()` is both faster and more readable +help: `get` is equivalent but more concise + | +LL | let bad_boxed_slice = boxed_slice.get(3); + | ~~~ error: called `.iter().nth()` on a `VecDeque` --> tests/ui/iter_nth.rs:37:29 @@ -30,7 +39,10 @@ error: called `.iter().nth()` on a `VecDeque` LL | let bad_vec_deque = some_vec_deque.iter().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get()` is both faster and more readable +help: `get` is equivalent but more concise + | +LL | let bad_vec_deque = some_vec_deque.get(3); + | ~~~ error: called `.iter_mut().nth()` on a `Vec` --> tests/ui/iter_nth.rs:42:23 @@ -38,7 +50,10 @@ error: called `.iter_mut().nth()` on a `Vec` LL | let bad_vec = some_vec.iter_mut().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get_mut()` is both faster and more readable +help: `get_mut` is equivalent but more concise + | +LL | let bad_vec = some_vec.get_mut(3); + | ~~~~~~~ error: called `.iter_mut().nth()` on a slice --> tests/ui/iter_nth.rs:45:26 @@ -46,7 +61,10 @@ error: called `.iter_mut().nth()` on a slice LL | let bad_slice = &some_vec[..].iter_mut().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get_mut()` is both faster and more readable +help: `get_mut` is equivalent but more concise + | +LL | let bad_slice = &some_vec[..].get_mut(3); + | ~~~~~~~ error: called `.iter_mut().nth()` on a `VecDeque` --> tests/ui/iter_nth.rs:48:29 @@ -54,7 +72,21 @@ error: called `.iter_mut().nth()` on a `VecDeque` LL | let bad_vec_deque = some_vec_deque.iter_mut().nth(3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: calling `.get_mut()` is both faster and more readable +help: `get_mut` is equivalent but more concise + | +LL | let bad_vec_deque = some_vec_deque.get_mut(3); + | ~~~~~~~ + +error: called `.iter().nth()` on a `Vec` + --> tests/ui/iter_nth.rs:52:5 + | +LL | vec_ref.iter().nth(3); + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: `get` is equivalent but more concise + | +LL | vec_ref.get(3); + | ~~~ -error: aborting due to 7 previous errors +error: aborting due to 8 previous errors From 278eb287b34d6335a3ac4720f1517a12671fd0b8 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sun, 3 Mar 2024 11:28:58 -0500 Subject: [PATCH 0035/1468] Attempt to avoid LLVM error --- Cargo.lock | 2 + crates/std_float/src/lib.rs | 114 ++++++++++++++++++-------------- crates/std_float/tests/float.rs | 12 +++- 3 files changed, 78 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1ede15ff00286..1584c704fb221 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -178,6 +178,8 @@ version = "0.1.0" dependencies = [ "core_simd", "test_helpers", + "wasm-bindgen", + "wasm-bindgen-test", ] [[package]] diff --git a/crates/std_float/src/lib.rs b/crates/std_float/src/lib.rs index 44bbcba412f6d..148aa5f9f1771 100644 --- a/crates/std_float/src/lib.rs +++ b/crates/std_float/src/lib.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "as_crate", no_std)] // We are std! #![cfg_attr( feature = "as_crate", feature(core_intrinsics), @@ -67,43 +66,28 @@ pub trait StdFloat: Sealed + Sized { /// Produces a vector where every element has the sine of the value /// in the equivalently-indexed element in `self`. - #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn sin(self) -> Self { - unsafe { intrinsics::simd_fsin(self) } - } + fn sin(self) -> Self; /// Produces a vector where every element has the cosine of the value /// in the equivalently-indexed element in `self`. - #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn cos(self) -> Self { - unsafe { intrinsics::simd_fcos(self) } - } + fn cos(self) -> Self; /// Produces a vector where every element has the exponential (base e) of the value /// in the equivalently-indexed element in `self`. - #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn exp(self) -> Self { - unsafe { intrinsics::simd_fexp(self) } - } + fn exp(self) -> Self; /// Produces a vector where every element has the exponential (base 2) of the value /// in the equivalently-indexed element in `self`. - #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn exp2(self) -> Self { - unsafe { intrinsics::simd_fexp2(self) } - } + fn exp2(self) -> Self; /// Produces a vector where every element has the natural logarithm of the value /// in the equivalently-indexed element in `self`. - #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn ln(self) -> Self { - unsafe { intrinsics::simd_flog(self) } - } + fn ln(self) -> Self; /// Produces a vector where every element has the logarithm with respect to an arbitrary /// in the equivalently-indexed elements in `self` and `base`. @@ -115,19 +99,13 @@ pub trait StdFloat: Sealed + Sized { /// Produces a vector where every element has the base-2 logarithm of the value /// in the equivalently-indexed element in `self`. - #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn log2(self) -> Self { - unsafe { intrinsics::simd_flog2(self) } - } + fn log2(self) -> Self; /// Produces a vector where every element has the base-10 logarithm of the value /// in the equivalently-indexed element in `self`. - #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] - fn log10(self) -> Self { - unsafe { intrinsics::simd_flog10(self) } - } + fn log10(self) -> Self; /// Returns the smallest integer greater than or equal to each element. #[must_use = "method returns a new vector and does not mutate the original value"] @@ -165,27 +143,65 @@ pub trait StdFloat: Sealed + Sized { impl Sealed for Simd where LaneCount: SupportedLaneCount {} impl Sealed for Simd where LaneCount: SupportedLaneCount {} -// We can safely just use all the defaults. -impl StdFloat for Simd -where - LaneCount: SupportedLaneCount, -{ - /// Returns the floating point's fractional value, with its integer part removed. - #[must_use = "method returns a new vector and does not mutate the original value"] - #[inline] - fn fract(self) -> Self { - self - self.trunc() +macro_rules! impl_float { + { + $($fn:ident: $intrinsic:ident,)* + } => { + impl StdFloat for Simd + where + LaneCount: SupportedLaneCount, + { + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } + + $( + #[inline] + fn $fn(self) -> Self { + unsafe { intrinsics::$intrinsic(self) } + } + )* + } + + impl StdFloat for Simd + where + LaneCount: SupportedLaneCount, + { + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } + + $( + #[inline] + fn $fn(self) -> Self { + // https://github.com/llvm/llvm-project/issues/83729 + #[cfg(target_arch = "aarch64")] + { + let mut ln = Self::splat(0f64); + for i in 0..N { + ln[i] = self[i].$fn() + } + ln + } + + #[cfg(not(target_arch = "aarch64"))] + { + unsafe { intrinsics::$intrinsic(self) } + } + } + )* + } } } -impl StdFloat for Simd -where - LaneCount: SupportedLaneCount, -{ - /// Returns the floating point's fractional value, with its integer part removed. - #[must_use = "method returns a new vector and does not mutate the original value"] - #[inline] - fn fract(self) -> Self { - self - self.trunc() - } +impl_float! { + sin: simd_fsin, + cos: simd_fcos, + exp: simd_fexp, + exp2: simd_fexp2, + ln: simd_flog, + log2: simd_flog2, + log10: simd_flog10, } diff --git a/crates/std_float/tests/float.rs b/crates/std_float/tests/float.rs index 60bdf00fba886..c66c968f8c667 100644 --- a/crates/std_float/tests/float.rs +++ b/crates/std_float/tests/float.rs @@ -53,9 +53,19 @@ macro_rules! impl_tests { mod $scalar { use std_float::StdFloat; - unary_test! { $scalar, sqrt, sin, cos, exp, exp2, ln, log2, log10, ceil, floor, round, trunc, fract } + unary_test! { $scalar, sqrt, sin, cos, exp, exp2, ln, log2, log10, ceil, floor, round, trunc } binary_test! { $scalar, log } ternary_test! { $scalar, mul_add } + + test_helpers::test_lanes! { + fn fract() { + test_helpers::test_unary_elementwise_flush_subnormals( + &core_simd::simd::Simd::<$scalar, LANES>::fract, + &$scalar::fract, + &|_| true, + ) + } + } } } } From 68a58f255ac7b7487769667fdd5fe2536f952c15 Mon Sep 17 00:00:00 2001 From: Ross Smyth Date: Thu, 15 Feb 2024 19:54:35 -0500 Subject: [PATCH 0036/1468] Add postfix-match experimental feature Co-authored-by: Josh Stone --- compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_parse/src/parser/expr.rs | 19 +++++- compiler/rustc_span/src/symbol.rs | 1 + .../src/language-features/postfix-match.md | 22 +++++++ .../feature-gate-postfix_match.rs | 17 +++++ .../feature-gate-postfix_match.stderr | 23 +++++++ .../ui/match/postfix-match/pf-match-chain.rs | 16 +++++ .../postfix-match/pf-match-exhaustiveness.rs | 7 +++ .../pf-match-exhaustiveness.stderr | 21 +++++++ .../ui/match/postfix-match/pf-match-types.rs | 15 +++++ .../match/postfix-match/pf-match-types.stderr | 21 +++++++ tests/ui/match/postfix-match/postfix-match.rs | 62 +++++++++++++++++++ 13 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 src/doc/unstable-book/src/language-features/postfix-match.md create mode 100644 tests/ui/feature-gates/feature-gate-postfix_match.rs create mode 100644 tests/ui/feature-gates/feature-gate-postfix_match.stderr create mode 100644 tests/ui/match/postfix-match/pf-match-chain.rs create mode 100644 tests/ui/match/postfix-match/pf-match-exhaustiveness.rs create mode 100644 tests/ui/match/postfix-match/pf-match-exhaustiveness.stderr create mode 100644 tests/ui/match/postfix-match/pf-match-types.rs create mode 100644 tests/ui/match/postfix-match/pf-match-types.stderr create mode 100644 tests/ui/match/postfix-match/postfix-match.rs diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index a28fcb0077948..296f237763eb2 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -565,6 +565,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(generic_const_items, "generic const items are experimental"); gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented"); gate_all!(fn_delegation, "functions delegation is not yet fully implemented"); + gate_all!(postfix_match, "postfix match is experimental"); if !visitor.features.never_patterns { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 17c4d81474e27..89f471d21bb07 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -555,6 +555,8 @@ declare_features! ( (unstable, offset_of_nested, "1.77.0", Some(120140)), /// Allows using `#[optimize(X)]`. (unstable, optimize_attribute, "1.34.0", Some(54882)), + /// Allows postfix match `expr.match { ... }` + (unstable, postfix_match, "CURRENT_RUSTC_VERSION", Some(121618)), /// Allows macro attributes on expressions, statements and non-inline modules. (unstable, proc_macro_hygiene, "1.30.0", Some(54727)), /// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions. diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 6cc358db9fcae..02ff452473dd0 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1375,6 +1375,12 @@ impl<'a> Parser<'a> { return Ok(self.mk_await_expr(self_arg, lo)); } + if self.eat_keyword(kw::Match) { + let match_span = self.prev_token.span; + self.psess.gated_spans.gate(sym::postfix_match, match_span); + return self.parse_match_block(lo, match_span, self_arg); + } + let fn_span_lo = self.token.span; let mut seg = self.parse_path_segment(PathStyle::Expr, None)?; self.check_trailing_angle_brackets(&seg, &[&token::OpenDelim(Delimiter::Parenthesis)]); @@ -2889,8 +2895,19 @@ impl<'a> Parser<'a> { /// Parses a `match ... { ... }` expression (`match` token already eaten). fn parse_expr_match(&mut self) -> PResult<'a, P> { let match_span = self.prev_token.span; - let lo = self.prev_token.span; let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?; + + self.parse_match_block(match_span, match_span, scrutinee) + } + + /// Parses a `match expr { ... }` or a `expr.match { ... }` expression. + /// This is after the match token and scrutinee are eaten + fn parse_match_block( + &mut self, + lo: Span, + match_span: Span, + scrutinee: P, + ) -> PResult<'a, P> { if let Err(mut e) = self.expect(&token::OpenDelim(Delimiter::Brace)) { if self.token == token::Semi { e.span_suggestion_short( diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3784a08b1b720..2474189fef6e1 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1319,6 +1319,7 @@ symbols! { poll, poll_next, post_dash_lto: "post-lto", + postfix_match, powerpc_target_feature, powf128, powf16, diff --git a/src/doc/unstable-book/src/language-features/postfix-match.md b/src/doc/unstable-book/src/language-features/postfix-match.md new file mode 100644 index 0000000000000..cd6b6a7442c5a --- /dev/null +++ b/src/doc/unstable-book/src/language-features/postfix-match.md @@ -0,0 +1,22 @@ +# `postfix-match` + +`postfix-match` adds the feature for matching upon values postfix +the expressions that generate the values. + +```rust,edition2021 +#![feature(postfix_match)] + +enum Foo { + Bar, + Baz +} + +fn get_foo() -> Foo { + Foo::Bar +} + +get_foo().match { + Foo::Bar => {}, + Foo::Baz => panic!(), +} +``` diff --git a/tests/ui/feature-gates/feature-gate-postfix_match.rs b/tests/ui/feature-gates/feature-gate-postfix_match.rs new file mode 100644 index 0000000000000..dce7e81a9ae44 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-postfix_match.rs @@ -0,0 +1,17 @@ +// Testing that postfix match doesn't work without enabling the feature + +fn main() { + let val = Some(42); + + val.match { //~ ERROR postfix match is experimental + Some(42) => "the answer to life, the universe, and everything", + _ => "might be the answer to something" + }; + + // Test that the gate works behind a cfg + #[cfg(FALSE)] + val.match { //~ ERROR postfix match is experimental + Some(42) => "the answer to life, the universe, and everything", + _ => "might be the answer to something" + }; +} diff --git a/tests/ui/feature-gates/feature-gate-postfix_match.stderr b/tests/ui/feature-gates/feature-gate-postfix_match.stderr new file mode 100644 index 0000000000000..136838788dd29 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-postfix_match.stderr @@ -0,0 +1,23 @@ +error[E0658]: postfix match is experimental + --> $DIR/feature-gate-postfix_match.rs:6:9 + | +LL | val.match { + | ^^^^^ + | + = note: see issue #121618 for more information + = help: add `#![feature(postfix_match)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: postfix match is experimental + --> $DIR/feature-gate-postfix_match.rs:13:9 + | +LL | val.match { + | ^^^^^ + | + = note: see issue #121618 for more information + = help: add `#![feature(postfix_match)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/match/postfix-match/pf-match-chain.rs b/tests/ui/match/postfix-match/pf-match-chain.rs new file mode 100644 index 0000000000000..80546e1963bb6 --- /dev/null +++ b/tests/ui/match/postfix-match/pf-match-chain.rs @@ -0,0 +1,16 @@ +//@ run-pass + +#![feature(postfix_match)] + +fn main() { + 1.match { + 2 => Some(0), + _ => None, + }.match { + None => Ok(true), + Some(_) => Err("nope") + }.match { + Ok(_) => (), + Err(_) => panic!() + } +} diff --git a/tests/ui/match/postfix-match/pf-match-exhaustiveness.rs b/tests/ui/match/postfix-match/pf-match-exhaustiveness.rs new file mode 100644 index 0000000000000..f4cac46f7cd64 --- /dev/null +++ b/tests/ui/match/postfix-match/pf-match-exhaustiveness.rs @@ -0,0 +1,7 @@ +#![feature(postfix_match)] + +fn main() { + Some(1).match { //~ non-exhaustive patterns + None => {}, + } +} diff --git a/tests/ui/match/postfix-match/pf-match-exhaustiveness.stderr b/tests/ui/match/postfix-match/pf-match-exhaustiveness.stderr new file mode 100644 index 0000000000000..f458218bb5d67 --- /dev/null +++ b/tests/ui/match/postfix-match/pf-match-exhaustiveness.stderr @@ -0,0 +1,21 @@ +error[E0004]: non-exhaustive patterns: `Some(_)` not covered + --> $DIR/pf-match-exhaustiveness.rs:4:5 + | +LL | Some(1).match { + | ^^^^^^^ pattern `Some(_)` not covered + | +note: `Option` defined here + --> $SRC_DIR/core/src/option.rs:LL:COL + ::: $SRC_DIR/core/src/option.rs:LL:COL + | + = note: not covered + = note: the matched value is of type `Option` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ None => {}, +LL ~ Some(_) => todo!(), + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/match/postfix-match/pf-match-types.rs b/tests/ui/match/postfix-match/pf-match-types.rs new file mode 100644 index 0000000000000..af205926fb610 --- /dev/null +++ b/tests/ui/match/postfix-match/pf-match-types.rs @@ -0,0 +1,15 @@ +#![feature(postfix_match)] + +fn main() { + Some(10).match { + //~^ NOTE `match` arms have incompatible types + Some(5) => false, + //~^ NOTE this is found to be of type `bool` + Some(2) => true, + //~^ NOTE this is found to be of type `bool` + None => (), + //~^ ERROR `match` arms have incompatible types + //~| NOTE expected `bool`, found `()` + _ => true + } +} diff --git a/tests/ui/match/postfix-match/pf-match-types.stderr b/tests/ui/match/postfix-match/pf-match-types.stderr new file mode 100644 index 0000000000000..0cfc1363d5f1e --- /dev/null +++ b/tests/ui/match/postfix-match/pf-match-types.stderr @@ -0,0 +1,21 @@ +error[E0308]: `match` arms have incompatible types + --> $DIR/pf-match-types.rs:10:20 + | +LL | / Some(10).match { +LL | | +LL | | Some(5) => false, + | | ----- this is found to be of type `bool` +LL | | +LL | | Some(2) => true, + | | ---- this is found to be of type `bool` +LL | | +LL | | None => (), + | | ^^ expected `bool`, found `()` +... | +LL | | _ => true +LL | | } + | |_____- `match` arms have incompatible types + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/match/postfix-match/postfix-match.rs b/tests/ui/match/postfix-match/postfix-match.rs new file mode 100644 index 0000000000000..03c4e8ab5451f --- /dev/null +++ b/tests/ui/match/postfix-match/postfix-match.rs @@ -0,0 +1,62 @@ +//@ run-pass + +#![feature(postfix_match)] + +struct Bar { + foo: u8, + baz: u8, +} + +pub fn main() { + let thing = Some("thing"); + + thing.match { + Some("nothing") => {}, + Some(text) if text.eq_ignore_ascii_case("tapir") => {}, + Some("true") | Some("false") => {}, + Some("thing") => {}, + Some(_) => {}, + None => {} + }; + + let num = 2u8; + + num.match { + 0 => {}, + 1..=5 => {}, + _ => {}, + }; + + let slic = &[1, 2, 3, 4][..]; + + slic.match { + [1] => {}, + [2, _tail @ ..] => {}, + [1, _] => {}, + _ => {}, + }; + + slic[0].match { + 1 => 0, + i => i, + }; + + let out = (1, 2).match { + (1, 3) => 0, + (_, 1) => 0, + (1, i) => i, + _ => 3, + }; + assert!(out == 2); + + let strct = Bar { + foo: 3, + baz: 4 + }; + + strct.match { + Bar { foo: 1, .. } => {}, + Bar { baz: 2, .. } => {}, + _ => (), + }; +} From 78b3bf98c3425d139b42a6326701573ff98b1a1f Mon Sep 17 00:00:00 2001 From: Ross Smyth Date: Sat, 17 Feb 2024 12:43:54 -0500 Subject: [PATCH 0037/1468] Add MatchKind member to the Match expr for pretty printing & fmt --- compiler/rustc_ast/src/ast.rs | 11 +++++++++- compiler/rustc_ast/src/mut_visit.rs | 2 +- compiler/rustc_ast/src/visit.rs | 2 +- compiler/rustc_ast_lowering/src/expr.rs | 2 +- .../rustc_ast_pretty/src/pprust/state/expr.rs | 20 +++++++++++++----- .../src/assert/context.rs | 2 +- .../src/deriving/cmp/partial_ord.rs | 2 +- .../src/deriving/debug.rs | 3 +-- compiler/rustc_expand/src/build.rs | 4 ++-- compiler/rustc_lint/src/unused.rs | 4 ++-- compiler/rustc_parse/src/parser/expr.rs | 18 +++++++++------- .../clippy/clippy_lints/src/redundant_else.rs | 2 +- .../src/suspicious_operation_groupings.rs | 2 +- .../clippy/clippy_utils/src/ast_utils.rs | 2 +- src/tools/rustfmt/src/expr.rs | 8 +++---- src/tools/rustfmt/src/matches.rs | 4 +++- tests/pretty/postfix-match.rs | 21 +++++++++++++++++++ 17 files changed, 76 insertions(+), 33 deletions(-) create mode 100644 tests/pretty/postfix-match.rs diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 13b1a589d9ba9..6ffb3d65abcad 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1436,7 +1436,7 @@ pub enum ExprKind { /// `'label: loop { block }` Loop(P, Option