From 861a6e85e106850583a98f2254def0c9510de091 Mon Sep 17 00:00:00 2001 From: Dean Li Date: Sun, 28 Nov 2021 15:19:01 +0800 Subject: [PATCH 01/23] Add spectral_norm example from packed_simd --- crates/core_simd/examples/spectral_norm.rs | 77 ++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 crates/core_simd/examples/spectral_norm.rs diff --git a/crates/core_simd/examples/spectral_norm.rs b/crates/core_simd/examples/spectral_norm.rs new file mode 100644 index 0000000000000..c515dad4deabd --- /dev/null +++ b/crates/core_simd/examples/spectral_norm.rs @@ -0,0 +1,77 @@ +#![feature(portable_simd)] + +use core_simd::simd::*; + +fn a(i: usize, j: usize) -> f64 { + ((i + j) * (i + j + 1) / 2 + i + 1) as f64 +} + +fn mult_av(v: &[f64], out: &mut [f64]) { + assert!(v.len() == out.len()); + assert!(v.len() % 2 == 0); + + for (i, out) in out.iter_mut().enumerate() { + let mut sum = f64x2::splat(0.0); + + let mut j = 0; + while j < v.len() { + let b = f64x2::from_slice(&v[j..]); + let a = f64x2::from_array([a(i, j), a(i, j + 1)]); + sum += b / a; + j += 2 + } + *out = sum.horizontal_sum(); + } +} + +fn mult_atv(v: &[f64], out: &mut [f64]) { + assert!(v.len() == out.len()); + assert!(v.len() % 2 == 0); + + for (i, out) in out.iter_mut().enumerate() { + let mut sum = f64x2::splat(0.0); + + let mut j = 0; + while j < v.len() { + let b = f64x2::from_slice(&v[j..]); + let a = f64x2::from_array([a(j, i), a(j + 1, i)]); + sum += b / a; + j += 2 + } + *out = sum.horizontal_sum(); + } +} + +fn mult_atav(v: &[f64], out: &mut [f64], tmp: &mut [f64]) { + mult_av(v, tmp); + mult_atv(tmp, out); +} + +pub fn spectral_norm(n: usize) -> f64 { + assert!(n % 2 == 0, "only even lengths are accepted"); + + let mut u = vec![1.0; n]; + let mut v = u.clone(); + let mut tmp = u.clone(); + + for _ in 0..10 { + mult_atav(&u, &mut v, &mut tmp); + mult_atav(&v, &mut u, &mut tmp); + } + (dot(&u, &v) / dot(&v, &v)).sqrt() +} + +fn dot(x: &[f64], y: &[f64]) -> f64 { + // This is auto-vectorized: + x.iter().zip(y).map(|(&x, &y)| x * y).sum() +} + +#[cfg(test)] +#[test] +fn test() { + assert_eq!(&format!("{:.9}", spectral_norm(100)), "1.274219991"); +} + +fn main() { + // Empty main to make cargo happy +} From 2f7b7d5f4a1cbe2d63eea95d0ef1af3c2b30fdd6 Mon Sep 17 00:00:00 2001 From: Rune Tynan Date: Wed, 26 Jan 2022 19:51:32 -0500 Subject: [PATCH 02/23] Don't use is_local to determine function cleaning method call intent --- src/librustdoc/clean/inline.rs | 2 +- src/librustdoc/clean/mod.rs | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index a2e612955b349..4fe433188c90c 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -228,7 +228,7 @@ fn build_external_function(cx: &mut DocContext<'_>, did: DefId) -> clean::Functi let (generics, decl) = clean::enter_impl_trait(cx, |cx| { // NOTE: generics need to be cleaned before the decl! let generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates); - let decl = clean_fn_decl_from_did_and_sig(cx, did, sig); + let decl = clean_fn_decl_from_did_and_sig(cx, Some(did), sig); (generics, decl) }); clean::Function { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 33612c8065477..f9b4d1435bd03 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -890,10 +890,10 @@ fn clean_fn_decl_with_args( fn clean_fn_decl_from_did_and_sig( cx: &mut DocContext<'_>, - did: DefId, + did: Option, sig: ty::PolyFnSig<'_>, ) -> FnDecl { - let mut names = if did.is_local() { &[] } else { cx.tcx.fn_arg_names(did) }.iter(); + let mut names = did.map_or(&[] as &[_], |did| cx.tcx.fn_arg_names(did)).iter(); FnDecl { output: Return(sig.skip_binder().output().clean(cx)), @@ -1067,7 +1067,7 @@ impl Clean for ty::AssocItem { tcx.explicit_predicates_of(self.def_id), ); let sig = tcx.fn_sig(self.def_id); - let mut decl = clean_fn_decl_from_did_and_sig(cx, self.def_id, sig); + let mut decl = clean_fn_decl_from_did_and_sig(cx, Some(self.def_id), sig); if self.fn_has_self_parameter { let self_ty = match self.container { @@ -1466,8 +1466,7 @@ impl<'tcx> Clean for Ty<'tcx> { ty::FnDef(..) | ty::FnPtr(_) => { let ty = cx.tcx.lift(*self).expect("FnPtr lift failed"); let sig = ty.fn_sig(cx.tcx); - let def_id = DefId::local(CRATE_DEF_INDEX); - let decl = clean_fn_decl_from_did_and_sig(cx, def_id, sig); + let decl = clean_fn_decl_from_did_and_sig(cx, None, sig); BareFunction(box BareFunctionDecl { unsafety: sig.unsafety(), generic_params: Vec::new(), From 1c82e5ed9cd2611bf80d4de3cc7202258fe2edb9 Mon Sep 17 00:00:00 2001 From: Rune Tynan Date: Wed, 26 Jan 2022 19:53:14 -0500 Subject: [PATCH 03/23] Convert empty tuple to DefaultReturn in ty path --- src/librustdoc/clean/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index f9b4d1435bd03..be83338f675ed 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -895,8 +895,15 @@ fn clean_fn_decl_from_did_and_sig( ) -> FnDecl { let mut names = did.map_or(&[] as &[_], |did| cx.tcx.fn_arg_names(did)).iter(); + // We assume all empty tuples are default return type. This theoretically can discard `-> ()`, + // but shouldn't change any code meaning. + let output = match sig.skip_binder().output().clean(cx) { + Type::Tuple(inner) if inner.len() == 0 => DefaultReturn, + ty => Return(ty), + }; + FnDecl { - output: Return(sig.skip_binder().output().clean(cx)), + output, c_variadic: sig.skip_binder().c_variadic, inputs: Arguments { values: sig From eae2026326058cb7021816d3630da1f8c47ebc6d Mon Sep 17 00:00:00 2001 From: Rune Tynan Date: Thu, 27 Jan 2022 12:33:46 -0500 Subject: [PATCH 04/23] Set visibility to inherited in trait impls to match HIR --- src/librustdoc/clean/mod.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index be83338f675ed..c6e66f0966a75 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1204,7 +1204,16 @@ impl Clean for ty::AssocItem { } }; - Item::from_def_id_and_parts(self.def_id, Some(self.name), kind, cx) + let mut output = Item::from_def_id_and_parts(self.def_id, Some(self.name), kind, cx); + + // HACK: Override visibility for items in a trait implementation to match HIR + let impl_ref = tcx.parent(self.def_id).and_then(|did| tcx.impl_trait_ref(did)); + + if impl_ref.is_some() { + output.visibility = Visibility::Inherited; + } + + output } } From d90138bec8f406b5c01c6bed4c0e597df2f3b00f Mon Sep 17 00:00:00 2001 From: Rune Tynan Date: Thu, 27 Jan 2022 12:35:52 -0500 Subject: [PATCH 05/23] Remove now-unnecessary blanket impl HIR check --- src/librustdoc/clean/blanket_impl.rs | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 75ee663b926c4..6f4b87750ff85 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -101,27 +101,6 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { cx.generated_synthetics.insert((ty, trait_def_id)); - let hir_imp = impl_def_id.as_local() - .map(|local| cx.tcx.hir().expect_item(local)) - .and_then(|item| if let hir::ItemKind::Impl(i) = &item.kind { - Some(i) - } else { - None - }); - - let items = match hir_imp { - Some(imp) => imp - .items - .iter() - .map(|ii| cx.tcx.hir().impl_item(ii.id).clean(cx)) - .collect::>(), - None => cx.tcx - .associated_items(impl_def_id) - .in_definition_order() - .map(|x| x.clean(cx)) - .collect::>(), - }; - impls.push(Item { name: None, attrs: Default::default(), @@ -138,7 +117,11 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> { // the post-inference `trait_ref`, as it's more accurate. trait_: Some(trait_ref.clean(cx)), for_: ty.clean(cx), - items, + items: cx.tcx + .associated_items(impl_def_id) + .in_definition_order() + .map(|x| x.clean(cx)) + .collect::>(), polarity: ty::ImplPolarity::Positive, kind: ImplKind::Blanket(box trait_ref.self_ty().clean(cx)), }), From 2d2163bd3a4815c1a68de7b4246503977d99b21b Mon Sep 17 00:00:00 2001 From: Rune Tynan Date: Thu, 27 Jan 2022 13:21:17 -0500 Subject: [PATCH 06/23] Same code for ty and hir impl items --- src/librustdoc/clean/mod.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index c6e66f0966a75..a7eced296106f 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1036,20 +1036,18 @@ impl Clean for hir::ImplItem<'_> { } }; - let what_rustc_thinks = + let mut what_rustc_thinks = Item::from_def_id_and_parts(local_did, Some(self.ident.name), inner, cx); - let parent_item = cx.tcx.hir().expect_item(cx.tcx.hir().get_parent_item(self.hir_id())); - if let hir::ItemKind::Impl(impl_) = &parent_item.kind { - if impl_.of_trait.is_some() { - // Trait impl items always inherit the impl's visibility -- - // we don't want to show `pub`. - Item { visibility: Inherited, ..what_rustc_thinks } - } else { - what_rustc_thinks - } - } else { - panic!("found impl item with non-impl parent {:?}", parent_item); + + let impl_ref = cx.tcx.parent(local_did).and_then(|did| cx.tcx.impl_trait_ref(did)); + + // Trait impl items always inherit the impl's visibility -- + // we don't want to show `pub`. + if impl_ref.is_some() { + what_rustc_thinks.visibility = Inherited; } + + what_rustc_thinks }) } } @@ -1204,16 +1202,18 @@ impl Clean for ty::AssocItem { } }; - let mut output = Item::from_def_id_and_parts(self.def_id, Some(self.name), kind, cx); + let mut what_rustc_thinks = + Item::from_def_id_and_parts(self.def_id, Some(self.name), kind, cx); - // HACK: Override visibility for items in a trait implementation to match HIR let impl_ref = tcx.parent(self.def_id).and_then(|did| tcx.impl_trait_ref(did)); + // Trait impl items always inherit the impl's visibility -- + // we don't want to show `pub`. if impl_ref.is_some() { - output.visibility = Visibility::Inherited; + what_rustc_thinks.visibility = Visibility::Inherited; } - output + what_rustc_thinks } } From ebf65de2ce331f428a174f4ade13f60c8654472f Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Wed, 2 Feb 2022 18:14:16 -0800 Subject: [PATCH 07/23] Delete outmoded fn round_from_int --- crates/core_simd/src/round.rs | 7 ------- crates/core_simd/tests/round.rs | 8 -------- 2 files changed, 15 deletions(-) diff --git a/crates/core_simd/src/round.rs b/crates/core_simd/src/round.rs index 06ccab3ec494c..08339593600a5 100644 --- a/crates/core_simd/src/round.rs +++ b/crates/core_simd/src/round.rs @@ -22,13 +22,6 @@ macro_rules! implement { pub unsafe fn to_int_unchecked(self) -> Simd<$int_type, LANES> { unsafe { intrinsics::simd_cast(self) } } - - /// Creates a floating-point vector from an integer vector. Rounds values that are - /// not exactly representable. - #[inline] - pub fn round_from_int(value: Simd<$int_type, LANES>) -> Self { - unsafe { intrinsics::simd_cast(value) } - } } } } diff --git a/crates/core_simd/tests/round.rs b/crates/core_simd/tests/round.rs index 1a1bc9ebca76a..90547d5782c71 100644 --- a/crates/core_simd/tests/round.rs +++ b/crates/core_simd/tests/round.rs @@ -53,14 +53,6 @@ macro_rules! float_rounding_test { } test_helpers::test_lanes! { - fn from_int() { - test_helpers::test_unary_elementwise( - &Vector::::round_from_int, - &|x| x as Scalar, - &|_| true, - ) - } - fn to_int_unchecked() { // The maximum integer that can be represented by the equivalently sized float has // all of the mantissa digits set to 1, pushed up to the MSB. From 4910274686bcd144228a04d8d4d5dece4c7f5e3d Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Wed, 2 Feb 2022 18:21:12 -0800 Subject: [PATCH 08/23] Genericize to_int_unchecked --- crates/core_simd/src/lib.rs | 1 + crates/core_simd/src/round.rs | 15 ++++++++++----- crates/core_simd/tests/round.rs | 6 +++--- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index 960a66400839f..41f64e972d950 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -1,6 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![feature( const_fn_trait_bound, + convert_float_to_int, decl_macro, platform_intrinsics, repr_simd, diff --git a/crates/core_simd/src/round.rs b/crates/core_simd/src/round.rs index 08339593600a5..f1724cbc26337 100644 --- a/crates/core_simd/src/round.rs +++ b/crates/core_simd/src/round.rs @@ -1,9 +1,10 @@ use crate::simd::intrinsics; -use crate::simd::{LaneCount, Simd, SupportedLaneCount}; +use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; +use core::convert::FloatToInt; macro_rules! implement { { - $type:ty, $int_type:ty + $type:ty } => { impl Simd<$type, LANES> where @@ -19,12 +20,16 @@ macro_rules! implement { /// * Not be infinite /// * Be representable in the return type, after truncating off its fractional part #[inline] - pub unsafe fn to_int_unchecked(self) -> Simd<$int_type, LANES> { + pub unsafe fn to_int_unchecked(self) -> Simd + where + $type: FloatToInt, + I: SimdElement, + { unsafe { intrinsics::simd_cast(self) } } } } } -implement! { f32, i32 } -implement! { f64, i64 } +implement! { f32 } +implement! { f64 } diff --git a/crates/core_simd/tests/round.rs b/crates/core_simd/tests/round.rs index 90547d5782c71..5373232923760 100644 --- a/crates/core_simd/tests/round.rs +++ b/crates/core_simd/tests/round.rs @@ -64,11 +64,11 @@ macro_rules! float_rounding_test { runner.run( &test_helpers::array::UniformArrayStrategy::new(-MAX_REPRESENTABLE_VALUE..MAX_REPRESENTABLE_VALUE), |x| { - let result_1 = unsafe { Vector::from_array(x).to_int_unchecked().to_array() }; + let result_1 = unsafe { Vector::from_array(x).to_int_unchecked::().to_array() }; let result_2 = { - let mut result = [0; LANES]; + let mut result: [IntScalar; LANES] = [0; LANES]; for (i, o) in x.iter().zip(result.iter_mut()) { - *o = unsafe { i.to_int_unchecked() }; + *o = unsafe { i.to_int_unchecked::() }; } result }; From 672bfebfd89b7d7ebdac3dbcf714c6010430d5fc Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Mon, 7 Feb 2022 21:24:21 -0800 Subject: [PATCH 09/23] Remove overflow panic from divrem Includes some remarks in intrinsics.rs, generated while auditing the interface for remaining UB. --- crates/core_simd/src/intrinsics.rs | 9 +++++++ crates/core_simd/src/ops.rs | 39 +++++++++++++++++----------- crates/core_simd/tests/ops_macros.rs | 20 +++++++------- 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/crates/core_simd/src/intrinsics.rs b/crates/core_simd/src/intrinsics.rs index 233657202f7e8..b5d0df7548fe9 100644 --- a/crates/core_simd/src/intrinsics.rs +++ b/crates/core_simd/src/intrinsics.rs @@ -17,9 +17,15 @@ extern "platform-intrinsic" { pub(crate) fn simd_mul(x: T, y: T) -> T; /// udiv/sdiv/fdiv + /// ints and uints: {s,u}div incur UB if division by zero occurs. + /// ints: sdiv is UB for int::MIN / -1. + /// floats: fdiv is never UB, but may create NaNs or infinities. pub(crate) fn simd_div(x: T, y: T) -> T; /// urem/srem/frem + /// ints and uints: {s,u}rem incur UB if division by zero occurs. + /// ints: srem is UB for int::MIN / -1. + /// floats: frem is equivalent to libm::fmod in the "default" floating point environment, sans errno. pub(crate) fn simd_rem(x: T, y: T) -> T; /// shl @@ -45,6 +51,9 @@ extern "platform-intrinsic" { pub(crate) fn simd_as(x: T) -> U; /// neg/fneg + /// ints: ultimately becomes a call to cg_ssa's BuilderMethods::neg. cg_llvm equates this to `simd_sub(Simd::splat(0), x)`. + /// floats: LLVM's fneg, which changes the floating point sign bit. Some arches have instructions for it. + /// Rust panics for Neg::neg(int::MIN) due to overflow, but it is not UB in LLVM without `nsw`. pub(crate) fn simd_neg(x: T) -> T; /// fabs diff --git a/crates/core_simd/src/ops.rs b/crates/core_simd/src/ops.rs index b65038933bf33..1b35b3e717a32 100644 --- a/crates/core_simd/src/ops.rs +++ b/crates/core_simd/src/ops.rs @@ -57,29 +57,40 @@ macro_rules! wrap_bitshift { }; } -// Division by zero is poison, according to LLVM. -// So is dividing the MIN value of a signed integer by -1, -// since that would return MAX + 1. -// FIXME: Rust allows ::MIN / -1, -// so we should probably figure out how to make that safe. +/// SAFETY: This macro must only be used to impl Div or Rem and given the matching intrinsic. +/// It guards against LLVM's UB conditions for integer div or rem using masks and selects, +/// thus guaranteeing a Rust value returns instead. +/// +/// | | LLVM | Rust +/// | :--------------: | :--- | :---------- +/// | N {/,%} 0 | UB | panic!() +/// | <$int>::MIN / -1 | UB | <$int>::MIN +/// | <$int>::MIN % -1 | UB | 0 +/// macro_rules! int_divrem_guard { ( $lhs:ident, $rhs:ident, { const PANIC_ZERO: &'static str = $zero:literal; - const PANIC_OVERFLOW: &'static str = $overflow:literal; $simd_call:ident }, $int:ident ) => { if $rhs.lanes_eq(Simd::splat(0)).any() { panic!($zero); - } else if <$int>::MIN != 0 - && ($lhs.lanes_eq(Simd::splat(<$int>::MIN)) - // type inference can break here, so cut an SInt to size - & $rhs.lanes_eq(Simd::splat(-1i64 as _))).any() - { - panic!($overflow); } else { - unsafe { $crate::simd::intrinsics::$simd_call($lhs, $rhs) } + // Prevent otherwise-UB overflow on the MIN / -1 case. + let rhs = if <$int>::MIN != 0 { + // This should, at worst, optimize to a few branchless logical ops + // Ideally, this entire conditional should evaporate + // Fire LLVM and implement those manually if it doesn't get the hint + ($lhs.lanes_eq(Simd::splat(<$int>::MIN)) + // type inference can break here, so cut an SInt to size + & $rhs.lanes_eq(Simd::splat(-1i64 as _))) + .select(Simd::splat(1), $rhs) + } else { + // Nice base case to make it easy to const-fold away the other branch. + $rhs + }; + unsafe { $crate::simd::intrinsics::$simd_call($lhs, rhs) } } }; } @@ -183,7 +194,6 @@ for_base_ops! { impl Div::div { int_divrem_guard { const PANIC_ZERO: &'static str = "attempt to divide by zero"; - const PANIC_OVERFLOW: &'static str = "attempt to divide with overflow"; simd_div } } @@ -191,7 +201,6 @@ for_base_ops! { impl Rem::rem { int_divrem_guard { const PANIC_ZERO: &'static str = "attempt to calculate the remainder with a divisor of zero"; - const PANIC_OVERFLOW: &'static str = "attempt to calculate the remainder with overflow"; simd_rem } } diff --git a/crates/core_simd/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs index 4fb9de198ee15..9ba66fb8dd97f 100644 --- a/crates/core_simd/tests/ops_macros.rs +++ b/crates/core_simd/tests/ops_macros.rs @@ -210,15 +210,21 @@ macro_rules! impl_signed_tests { ) } - } + fn div_min_may_overflow() { + let a = Vector::::splat(Scalar::MIN); + let b = Vector::::splat(-1); + assert_eq!(a / b, a / (b * b)); + } - test_helpers::test_lanes_panic! { - fn div_min_overflow_panics() { + fn rem_min_may_overflow() { let a = Vector::::splat(Scalar::MIN); let b = Vector::::splat(-1); - let _ = a / b; + assert_eq!(a % b, a % (b * b)); } + } + + test_helpers::test_lanes_panic! { fn div_by_all_zeros_panics() { let a = Vector::::splat(42); let b = Vector::::splat(0); @@ -232,12 +238,6 @@ macro_rules! impl_signed_tests { let _ = a / b; } - fn rem_min_overflow_panic() { - let a = Vector::::splat(Scalar::MIN); - let b = Vector::::splat(-1); - let _ = a % b; - } - fn rem_zero_panic() { let a = Vector::::splat(42); let b = Vector::::splat(0); From e628a2991c47a771340cc5f8f06826c918f79609 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Tue, 8 Feb 2022 14:15:45 -0800 Subject: [PATCH 10/23] Document Simd is Simd, N> and other quirks like panicking and the equivalence to zipping and mapping binary ops --- crates/core_simd/src/vector.rs | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index b7ef7a56c7319..0a2f681f66b2b 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -12,7 +12,39 @@ pub(crate) mod ptr; use crate::simd::intrinsics; use crate::simd::{LaneCount, Mask, MaskElement, SupportedLaneCount}; -/// A SIMD vector of `LANES` elements of type `T`. +/// A SIMD vector of `LANES` elements of type `T`. `Simd` has the same shape as [`[T; N]`](array), but operates like `T`. +/// This type is commonly known by names like `f32x4` or `Vec4` in many programming languages. +/// +/// Two vectors of the same type and length will, by convention, support the binary operations (+, *, etc.) that `T` does. +/// These take the lanes at each index on the left-hand side and right-hand side, perform the binary operation, +/// and return the result in the same lane in a vector of equal size. For a given operator, this is equivalent to zipping +/// the two arrays together and mapping the operator over each lane. +/// +/// ```rust +/// # #![feature(array_zip, portable_simd)] +/// # use core::simd::{Simd}; +/// let a0: [i32; 4] = [-2, 0, 2, 4]; +/// let a1 = [10, 9, 8, 7]; +/// let zm_add = a0.zip(a1).map(|(lhs, rhs)| lhs + rhs); +/// let zm_mul = a0.zip(a1).map(|(lhs, rhs)| lhs * rhs); +/// +/// // `Simd` implements `From<[T; N]> +/// let [v0, v1] = [a0, a1].map(|a| Simd::from(a)); +/// // Which means arrays implement `Into>`. +/// assert_eq!(v0 + v1, zm_add.into()); +/// assert_eq!(v0 * v1, zm_mul.into()); +/// ``` +/// +/// `Simd` with integers has the quirk that these operations are also inherently wrapping, as if `T` was [`Wrapping`]. +/// Thus, `Simd` does not implement `wrapping_add`, because that is the behavior of the normal operation. +/// This means there is no warning on overflows, even in "debug" builds. +/// For most applications where `Simd` is appropriate, it is "not a bug" to wrap, +/// and even "debug builds" are unlikely to tolerate the loss of performance. +/// You may want to consider using explicitly checked arithmetic if such is required. +/// Division by zero still causes a panic, so you may want to consider using floating point numbers if that is unacceptable. +/// +/// [`Wrapping`]: core::num::Wrapping +/// #[repr(simd)] pub struct Simd([T; LANES]) where From 5d52455c65bf5a5eb258ed11591b8ebfa61ea5c7 Mon Sep 17 00:00:00 2001 From: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Tue, 8 Feb 2022 17:38:21 -0800 Subject: [PATCH 11/23] Review for clarity and concision Co-authored-by: Caleb Zulawski --- crates/core_simd/src/vector.rs | 9 ++++----- crates/core_simd/tests/ops_macros.rs | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 0a2f681f66b2b..5bd8ed69535e1 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -13,10 +13,9 @@ use crate::simd::intrinsics; use crate::simd::{LaneCount, Mask, MaskElement, SupportedLaneCount}; /// A SIMD vector of `LANES` elements of type `T`. `Simd` has the same shape as [`[T; N]`](array), but operates like `T`. -/// This type is commonly known by names like `f32x4` or `Vec4` in many programming languages. /// -/// Two vectors of the same type and length will, by convention, support the binary operations (+, *, etc.) that `T` does. -/// These take the lanes at each index on the left-hand side and right-hand side, perform the binary operation, +/// Two vectors of the same type and length will, by convention, support the operators (+, *, etc.) that `T` does. +/// These take the lanes at each index on the left-hand side and right-hand side, perform the operation, /// and return the result in the same lane in a vector of equal size. For a given operator, this is equivalent to zipping /// the two arrays together and mapping the operator over each lane. /// @@ -29,14 +28,14 @@ use crate::simd::{LaneCount, Mask, MaskElement, SupportedLaneCount}; /// let zm_mul = a0.zip(a1).map(|(lhs, rhs)| lhs * rhs); /// /// // `Simd` implements `From<[T; N]> -/// let [v0, v1] = [a0, a1].map(|a| Simd::from(a)); +/// let (v0, v1) = (Simd::from(a0), Simd::from(a1)); /// // Which means arrays implement `Into>`. /// assert_eq!(v0 + v1, zm_add.into()); /// assert_eq!(v0 * v1, zm_mul.into()); /// ``` /// /// `Simd` with integers has the quirk that these operations are also inherently wrapping, as if `T` was [`Wrapping`]. -/// Thus, `Simd` does not implement `wrapping_add`, because that is the behavior of the normal operation. +/// Thus, `Simd` does not implement `wrapping_add`, because that is the default behavior. /// This means there is no warning on overflows, even in "debug" builds. /// For most applications where `Simd` is appropriate, it is "not a bug" to wrap, /// and even "debug builds" are unlikely to tolerate the loss of performance. diff --git a/crates/core_simd/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs index 9ba66fb8dd97f..50f7a4ca170db 100644 --- a/crates/core_simd/tests/ops_macros.rs +++ b/crates/core_simd/tests/ops_macros.rs @@ -213,13 +213,13 @@ macro_rules! impl_signed_tests { fn div_min_may_overflow() { let a = Vector::::splat(Scalar::MIN); let b = Vector::::splat(-1); - assert_eq!(a / b, a / (b * b)); + assert_eq!(a / b, a); } fn rem_min_may_overflow() { let a = Vector::::splat(Scalar::MIN); let b = Vector::::splat(-1); - assert_eq!(a % b, a % (b * b)); + assert_eq!(a % b, Vector::::splat(0)); } } From dddfffcfb3e49c752d6a28039735ada2552d0307 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Fri, 5 Nov 2021 01:42:29 +0000 Subject: [PATCH 12/23] Add some safety comments --- crates/core_simd/src/comparisons.rs | 12 ++++++++++++ crates/core_simd/src/masks.rs | 9 +++++++++ crates/core_simd/src/masks/bitmask.rs | 1 + crates/core_simd/src/masks/full_masks.rs | 6 ++++++ crates/core_simd/src/math.rs | 4 ++++ crates/core_simd/src/reduction.rs | 8 ++++++++ crates/core_simd/src/swizzle.rs | 2 ++ crates/core_simd/src/to_bytes.rs | 2 ++ crates/core_simd/src/vector.rs | 12 +++++++----- crates/core_simd/src/vector/ptr.rs | 4 ++++ crates/core_simd/src/vendor.rs | 2 ++ 11 files changed, 57 insertions(+), 5 deletions(-) diff --git a/crates/core_simd/src/comparisons.rs b/crates/core_simd/src/comparisons.rs index edef5af3687a2..d024cf4ddbe30 100644 --- a/crates/core_simd/src/comparisons.rs +++ b/crates/core_simd/src/comparisons.rs @@ -10,6 +10,8 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] pub fn lanes_eq(self, other: Self) -> Mask { + // Safety: `self` is a vector, and the result of the comparison + // is always a valid mask. unsafe { Mask::from_int_unchecked(intrinsics::simd_eq(self, other)) } } @@ -17,6 +19,8 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] pub fn lanes_ne(self, other: Self) -> Mask { + // Safety: `self` is a vector, and the result of the comparison + // is always a valid mask. unsafe { Mask::from_int_unchecked(intrinsics::simd_ne(self, other)) } } } @@ -30,6 +34,8 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] pub fn lanes_lt(self, other: Self) -> Mask { + // Safety: `self` is a vector, and the result of the comparison + // is always a valid mask. unsafe { Mask::from_int_unchecked(intrinsics::simd_lt(self, other)) } } @@ -37,6 +43,8 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] pub fn lanes_gt(self, other: Self) -> Mask { + // Safety: `self` is a vector, and the result of the comparison + // is always a valid mask. unsafe { Mask::from_int_unchecked(intrinsics::simd_gt(self, other)) } } @@ -44,6 +52,8 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] pub fn lanes_le(self, other: Self) -> Mask { + // Safety: `self` is a vector, and the result of the comparison + // is always a valid mask. unsafe { Mask::from_int_unchecked(intrinsics::simd_le(self, other)) } } @@ -51,6 +61,8 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] pub fn lanes_ge(self, other: Self) -> Mask { + // Safety: `self` is a vector, and the result of the comparison + // is always a valid mask. unsafe { Mask::from_int_unchecked(intrinsics::simd_ge(self, other)) } } } diff --git a/crates/core_simd/src/masks.rs b/crates/core_simd/src/masks.rs index ae1fef53da88e..b1f98d9eb4e67 100644 --- a/crates/core_simd/src/masks.rs +++ b/crates/core_simd/src/masks.rs @@ -42,6 +42,9 @@ mod sealed { use sealed::Sealed; /// Marker trait for types that may be used as SIMD mask elements. +/// +/// # Safety +/// Type must be a signed integer. pub unsafe trait MaskElement: SimdElement + Sealed {} macro_rules! impl_element { @@ -149,6 +152,7 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] pub unsafe fn from_int_unchecked(value: Simd) -> Self { + // Safety: the caller must confirm this invariant unsafe { Self(mask_impl::Mask::from_int_unchecked(value)) } } @@ -161,6 +165,7 @@ where #[must_use = "method returns a new mask and does not mutate the original value"] pub fn from_int(value: Simd) -> Self { assert!(T::valid(value), "all values must be either 0 or -1",); + // Safety: the validity has been checked unsafe { Self::from_int_unchecked(value) } } @@ -179,6 +184,7 @@ where #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] pub unsafe fn test_unchecked(&self, lane: usize) -> bool { + // Safety: the caller must confirm this invariant unsafe { self.0.test_unchecked(lane) } } @@ -190,6 +196,7 @@ where #[must_use = "method returns a new bool and does not mutate the original value"] pub fn test(&self, lane: usize) -> bool { assert!(lane < LANES, "lane index out of range"); + // Safety: the lane index has been checked unsafe { self.test_unchecked(lane) } } @@ -199,6 +206,7 @@ where /// `lane` must be less than `LANES`. #[inline] pub unsafe fn set_unchecked(&mut self, lane: usize, value: bool) { + // Safety: the caller must confirm this invariant unsafe { self.0.set_unchecked(lane, value); } @@ -211,6 +219,7 @@ where #[inline] pub fn set(&mut self, lane: usize, value: bool) { assert!(lane < LANES, "lane index out of range"); + // Safety: the lane index has been checked unsafe { self.set_unchecked(lane, value); } diff --git a/crates/core_simd/src/masks/bitmask.rs b/crates/core_simd/src/masks/bitmask.rs index b4217dc87ba9c..b7f8b2c236eff 100644 --- a/crates/core_simd/src/masks/bitmask.rs +++ b/crates/core_simd/src/masks/bitmask.rs @@ -137,6 +137,7 @@ where where U: MaskElement, { + // Safety: bitmask layout does not depend on the element width unsafe { core::mem::transmute_copy(&self) } } diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs index e5bb784bb910f..02b5593c8f465 100644 --- a/crates/core_simd/src/masks/full_masks.rs +++ b/crates/core_simd/src/masks/full_masks.rs @@ -106,6 +106,7 @@ where where U: MaskElement, { + // Safety: masks are simply integer vectors of 0 and -1, and we can cast the element type. unsafe { Mask(intrinsics::simd_cast(self.0)) } } @@ -155,12 +156,14 @@ where #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] pub fn any(self) -> bool { + // Safety: use `self` as an integer vector unsafe { intrinsics::simd_reduce_any(self.to_int()) } } #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] pub fn all(self) -> bool { + // Safety: use `self` as an integer vector unsafe { intrinsics::simd_reduce_all(self.to_int()) } } } @@ -184,6 +187,7 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] fn bitand(self, rhs: Self) -> Self { + // Safety: `self` is an integer vector unsafe { Self(intrinsics::simd_and(self.0, rhs.0)) } } } @@ -197,6 +201,7 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] fn bitor(self, rhs: Self) -> Self { + // Safety: `self` is an integer vector unsafe { Self(intrinsics::simd_or(self.0, rhs.0)) } } } @@ -210,6 +215,7 @@ where #[inline] #[must_use = "method returns a new mask and does not mutate the original value"] fn bitxor(self, rhs: Self) -> Self { + // Safety: `self` is an integer vector unsafe { Self(intrinsics::simd_xor(self.0, rhs.0)) } } } diff --git a/crates/core_simd/src/math.rs b/crates/core_simd/src/math.rs index 7435b6df91860..0b4e40983af53 100644 --- a/crates/core_simd/src/math.rs +++ b/crates/core_simd/src/math.rs @@ -22,6 +22,7 @@ macro_rules! impl_uint_arith { /// ``` #[inline] pub fn saturating_add(self, second: Self) -> Self { + // Safety: `self` is a vector unsafe { simd_saturating_add(self, second) } } @@ -41,6 +42,7 @@ macro_rules! impl_uint_arith { /// assert_eq!(sat, Simd::splat(0)); #[inline] pub fn saturating_sub(self, second: Self) -> Self { + // Safety: `self` is a vector unsafe { simd_saturating_sub(self, second) } } })+ @@ -68,6 +70,7 @@ macro_rules! impl_int_arith { /// ``` #[inline] pub fn saturating_add(self, second: Self) -> Self { + // Safety: `self` is a vector unsafe { simd_saturating_add(self, second) } } @@ -87,6 +90,7 @@ macro_rules! impl_int_arith { /// assert_eq!(sat, Simd::from_array([MIN, MIN, MIN, 0])); #[inline] pub fn saturating_sub(self, second: Self) -> Self { + // Safety: `self` is a vector unsafe { simd_saturating_sub(self, second) } } diff --git a/crates/core_simd/src/reduction.rs b/crates/core_simd/src/reduction.rs index e79a185816bfb..e1cd743e44247 100644 --- a/crates/core_simd/src/reduction.rs +++ b/crates/core_simd/src/reduction.rs @@ -14,24 +14,28 @@ macro_rules! impl_integer_reductions { /// Horizontal wrapping add. Returns the sum of the lanes of the vector, with wrapping addition. #[inline] pub fn horizontal_sum(self) -> $scalar { + // Safety: `self` is an integer vector unsafe { simd_reduce_add_ordered(self, 0) } } /// Horizontal wrapping multiply. Returns the product of the lanes of the vector, with wrapping multiplication. #[inline] pub fn horizontal_product(self) -> $scalar { + // Safety: `self` is an integer vector unsafe { simd_reduce_mul_ordered(self, 1) } } /// Horizontal maximum. Returns the maximum lane in the vector. #[inline] pub fn horizontal_max(self) -> $scalar { + // Safety: `self` is an integer vector unsafe { simd_reduce_max(self) } } /// Horizontal minimum. Returns the minimum lane in the vector. #[inline] pub fn horizontal_min(self) -> $scalar { + // Safety: `self` is an integer vector unsafe { simd_reduce_min(self) } } } @@ -63,6 +67,7 @@ macro_rules! impl_float_reductions { if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) { self.as_array().iter().sum() } else { + // Safety: `self` is a float vector unsafe { simd_reduce_add_ordered(self, 0.) } } } @@ -74,6 +79,7 @@ macro_rules! impl_float_reductions { if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) { self.as_array().iter().product() } else { + // Safety: `self` is a float vector unsafe { simd_reduce_mul_ordered(self, 1.) } } } @@ -84,6 +90,7 @@ macro_rules! impl_float_reductions { /// return either. This function will not return `NaN` unless all lanes are `NaN`. #[inline] pub fn horizontal_max(self) -> $scalar { + // Safety: `self` is a float vector unsafe { simd_reduce_max(self) } } @@ -93,6 +100,7 @@ macro_rules! impl_float_reductions { /// return either. This function will not return `NaN` unless all lanes are `NaN`. #[inline] pub fn horizontal_min(self) -> $scalar { + // Safety: `self` is a float vector unsafe { simd_reduce_min(self) } } } diff --git a/crates/core_simd/src/swizzle.rs b/crates/core_simd/src/swizzle.rs index bdc489774a54a..08b2add11667a 100644 --- a/crates/core_simd/src/swizzle.rs +++ b/crates/core_simd/src/swizzle.rs @@ -95,6 +95,7 @@ pub trait Swizzle { LaneCount: SupportedLaneCount, LaneCount: SupportedLaneCount, { + // Safety: `vector` is a vector, and `INDEX_IMPL` is a const array of u32. unsafe { intrinsics::simd_shuffle(vector, vector, Self::INDEX_IMPL) } } } @@ -119,6 +120,7 @@ pub trait Swizzle2 { LaneCount: SupportedLaneCount, LaneCount: SupportedLaneCount, { + // Safety: `first` and `second` are vectors, and `INDEX_IMPL` is a const array of u32. unsafe { intrinsics::simd_shuffle(first, second, Self::INDEX_IMPL) } } } diff --git a/crates/core_simd/src/to_bytes.rs b/crates/core_simd/src/to_bytes.rs index 8d9b3e8ff85ea..b36b1a347b226 100644 --- a/crates/core_simd/src/to_bytes.rs +++ b/crates/core_simd/src/to_bytes.rs @@ -8,12 +8,14 @@ macro_rules! impl_to_bytes { /// Return the memory representation of this integer as a byte array in native byte /// order. pub fn to_ne_bytes(self) -> crate::simd::Simd { + // Safety: transmuting between vectors is safe unsafe { core::mem::transmute_copy(&self) } } /// Create a native endian integer value from its memory representation as a byte array /// in native endianness. pub fn from_ne_bytes(bytes: crate::simd::Simd) -> Self { + // Safety: transmuting between vectors is safe unsafe { core::mem::transmute_copy(&bytes) } } } diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index 5bd8ed69535e1..e452fa8bfc829 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -206,7 +206,7 @@ where or: Self, ) -> Self { let enable: Mask = enable & idxs.lanes_lt(Simd::splat(slice.len())); - // SAFETY: We have masked-off out-of-bounds lanes. + // Safety: We have masked-off out-of-bounds lanes. unsafe { Self::gather_select_unchecked(slice, enable, idxs, or) } } @@ -247,7 +247,7 @@ where let base_ptr = crate::simd::ptr::SimdConstPtr::splat(slice.as_ptr()); // Ferris forgive me, I have done pointer arithmetic here. let ptrs = base_ptr.wrapping_add(idxs); - // SAFETY: The ptrs have been bounds-masked to prevent memory-unsafe reads insha'allah + // Safety: The ptrs have been bounds-masked to prevent memory-unsafe reads insha'allah unsafe { intrinsics::simd_gather(or, ptrs, enable.to_int()) } } @@ -299,7 +299,7 @@ where idxs: Simd, ) { let enable: Mask = enable & idxs.lanes_lt(Simd::splat(slice.len())); - // SAFETY: We have masked-off out-of-bounds lanes. + // Safety: We have masked-off out-of-bounds lanes. unsafe { self.scatter_select_unchecked(slice, enable, idxs) } } @@ -338,7 +338,7 @@ where enable: Mask, idxs: Simd, ) { - // SAFETY: This block works with *mut T derived from &mut 'a [T], + // Safety: This block works with *mut T derived from &mut 'a [T], // which means it is delicate in Rust's borrowing model, circa 2021: // &mut 'a [T] asserts uniqueness, so deriving &'a [T] invalidates live *mut Ts! // Even though this block is largely safe methods, it must be exactly this way @@ -518,7 +518,9 @@ mod sealed { use sealed::Sealed; /// Marker trait for types that may be used as SIMD vector elements. -/// SAFETY: This trait, when implemented, asserts the compiler can monomorphize +/// +/// # Safety +/// This trait, when implemented, asserts the compiler can monomorphize /// `#[repr(simd)]` structs with the marked type as an element. /// Strictly, it is valid to impl if the vector will not be miscompiled. /// Practically, it is user-unfriendly to impl it if the vector won't compile, diff --git a/crates/core_simd/src/vector/ptr.rs b/crates/core_simd/src/vector/ptr.rs index c668d9a6eaee3..417d255c28d63 100644 --- a/crates/core_simd/src/vector/ptr.rs +++ b/crates/core_simd/src/vector/ptr.rs @@ -21,6 +21,8 @@ where #[inline] #[must_use] pub fn wrapping_add(self, addend: Simd) -> Self { + // Safety: converting pointers to usize and vice-versa is safe + // (even if using that pointer is not) unsafe { let x: Simd = mem::transmute_copy(&self); mem::transmute_copy(&{ x + (addend * Simd::splat(mem::size_of::())) }) @@ -47,6 +49,8 @@ where #[inline] #[must_use] pub fn wrapping_add(self, addend: Simd) -> Self { + // Safety: converting pointers to usize and vice-versa is safe + // (even if using that pointer is not) unsafe { let x: Simd = mem::transmute_copy(&self); mem::transmute_copy(&{ x + (addend * Simd::splat(mem::size_of::())) }) diff --git a/crates/core_simd/src/vendor.rs b/crates/core_simd/src/vendor.rs index e8ce7176b4f21..9fb70218c9543 100644 --- a/crates/core_simd/src/vendor.rs +++ b/crates/core_simd/src/vendor.rs @@ -9,6 +9,8 @@ macro_rules! from_transmute { impl core::convert::From<$from> for $to { #[inline] fn from(value: $from) -> $to { + // Safety: transmuting between vectors is safe, but the caller of this macro + // checks the invariants unsafe { core::mem::transmute(value) } } } From 78a18c3433c13bbd6259242185667c385eafe859 Mon Sep 17 00:00:00 2001 From: Jubilee <46493976+workingjubilee@users.noreply.github.com> Date: Thu, 10 Feb 2022 09:32:44 -0800 Subject: [PATCH 13/23] rust-lang/portable-simd#245: Explain unsafe contracts of core::simd * Explain unsafe contracts of core::simd This permeates the module with remarks on safety for pub methods, layout of the Simd type, correct use of intrinsics, et cetera. This is mostly to help others curious about how core::simd works, including other Rust contributors, `unsafe` library authors, and eventually ourselves. --- crates/core_simd/src/intrinsics.rs | 59 ++++++++++++++++++++++++------ crates/core_simd/src/lib.rs | 1 + crates/core_simd/src/round.rs | 5 +++ crates/core_simd/src/select.rs | 4 ++ crates/core_simd/src/vector.rs | 42 +++++++++++++++++++++ 5 files changed, 100 insertions(+), 11 deletions(-) diff --git a/crates/core_simd/src/intrinsics.rs b/crates/core_simd/src/intrinsics.rs index b5d0df7548fe9..e150946c705c9 100644 --- a/crates/core_simd/src/intrinsics.rs +++ b/crates/core_simd/src/intrinsics.rs @@ -2,16 +2,31 @@ //! crate. //! //! The LLVM assembly language is documented here: +//! +//! A quick glossary of jargon that may appear in this module, mostly paraphrasing LLVM's LangRef: +//! - poison: "undefined behavior as a value". specifically, it is like uninit memory (such as padding bytes). it is "safe" to create poison, BUT +//! poison MUST NOT be observed from safe code, as operations on poison return poison, like NaN. unlike NaN, which has defined comparisons, +//! poison is neither true nor false, and LLVM may also convert it to undef (at which point it is both). so, it can't be conditioned on, either. +//! - undef: "a value that is every value". functionally like poison, insofar as Rust is concerned. poison may become this. note: +//! this means that division by poison or undef is like division by zero, which means it inflicts... +//! - "UB": poison and undef cover most of what people call "UB". "UB" means this operation immediately invalidates the program: +//! LLVM is allowed to lower it to `ud2` or other opcodes that may cause an illegal instruction exception, and this is the "good end". +//! The "bad end" is that LLVM may reverse time to the moment control flow diverged on a path towards undefined behavior, +//! and destroy the other branch, potentially deleting safe code and violating Rust's `unsafe` contract. +//! +//! Note that according to LLVM, vectors are not arrays, but they are equivalent when stored to and loaded from memory. +//! +//! Unless stated otherwise, all intrinsics for binary operations require SIMD vectors of equal types and lengths. /// These intrinsics aren't linked directly from LLVM and are mostly undocumented, however they are -/// simply lowered to the matching LLVM instructions by the compiler. The associated instruction -/// is documented alongside each intrinsic. +/// mostly lowered to the matching LLVM instructions by the compiler in a fairly straightforward manner. +/// The associated LLVM instruction or intrinsic is documented alongside each Rust intrinsic function. extern "platform-intrinsic" { /// add/fadd pub(crate) fn simd_add(x: T, y: T) -> T; /// sub/fsub - pub(crate) fn simd_sub(x: T, y: T) -> T; + pub(crate) fn simd_sub(lhs: T, rhs: T) -> T; /// mul/fmul pub(crate) fn simd_mul(x: T, y: T) -> T; @@ -20,19 +35,22 @@ extern "platform-intrinsic" { /// ints and uints: {s,u}div incur UB if division by zero occurs. /// ints: sdiv is UB for int::MIN / -1. /// floats: fdiv is never UB, but may create NaNs or infinities. - pub(crate) fn simd_div(x: T, y: T) -> T; + pub(crate) fn simd_div(lhs: T, rhs: T) -> T; /// urem/srem/frem /// ints and uints: {s,u}rem incur UB if division by zero occurs. /// ints: srem is UB for int::MIN / -1. /// floats: frem is equivalent to libm::fmod in the "default" floating point environment, sans errno. - pub(crate) fn simd_rem(x: T, y: T) -> T; + pub(crate) fn simd_rem(lhs: T, rhs: T) -> T; /// shl - pub(crate) fn simd_shl(x: T, y: T) -> T; + /// for (u)ints. poison if rhs >= lhs::BITS + pub(crate) fn simd_shl(lhs: T, rhs: T) -> T; - /// lshr/ashr - pub(crate) fn simd_shr(x: T, y: T) -> T; + /// ints: ashr + /// uints: lshr + /// poison if rhs >= lhs::BITS + pub(crate) fn simd_shr(lhs: T, rhs: T) -> T; /// and pub(crate) fn simd_and(x: T, y: T) -> T; @@ -44,6 +62,9 @@ extern "platform-intrinsic" { pub(crate) fn simd_xor(x: T, y: T) -> T; /// fptoui/fptosi/uitofp/sitofp + /// casting floats to integers is truncating, so it is safe to convert values like e.g. 1.5 + /// but the truncated value must fit in the target type or the result is poison. + /// use `simd_as` instead for a cast that performs a saturating conversion. pub(crate) fn simd_cast(x: T) -> U; /// follows Rust's `T as U` semantics, including saturating float casts /// which amounts to the same as `simd_cast` for many cases @@ -63,6 +84,7 @@ extern "platform-intrinsic" { pub(crate) fn simd_fmin(x: T, y: T) -> T; pub(crate) fn simd_fmax(x: T, y: T) -> T; + // these return Simd with the same BITS size as the inputs pub(crate) fn simd_eq(x: T, y: T) -> U; pub(crate) fn simd_ne(x: T, y: T) -> U; pub(crate) fn simd_lt(x: T, y: T) -> U; @@ -71,19 +93,31 @@ extern "platform-intrinsic" { pub(crate) fn simd_ge(x: T, y: T) -> U; // shufflevector + // idx: LLVM calls it a "shuffle mask vector constant", a vector of i32s pub(crate) fn simd_shuffle(x: T, y: T, idx: U) -> V; + /// llvm.masked.gather + /// like a loop of pointer reads + /// val: vector of values to select if a lane is masked + /// ptr: vector of pointers to read from + /// mask: a "wide" mask of integers, selects as if simd_select(mask, read(ptr), val) + /// note, the LLVM intrinsic accepts a mask vector of + /// FIXME: review this if/when we fix up our mask story in general? pub(crate) fn simd_gather(val: T, ptr: U, mask: V) -> T; + /// llvm.masked.scatter + /// like gather, but more spicy, as it writes instead of reads pub(crate) fn simd_scatter(val: T, ptr: U, mask: V); // {s,u}add.sat pub(crate) fn simd_saturating_add(x: T, y: T) -> T; // {s,u}sub.sat - pub(crate) fn simd_saturating_sub(x: T, y: T) -> T; + pub(crate) fn simd_saturating_sub(lhs: T, rhs: T) -> T; // reductions + // llvm.vector.reduce.{add,fadd} pub(crate) fn simd_reduce_add_ordered(x: T, y: U) -> U; + // llvm.vector.reduce.{mul,fmul} pub(crate) fn simd_reduce_mul_ordered(x: T, y: U) -> U; #[allow(unused)] pub(crate) fn simd_reduce_all(x: T) -> bool; @@ -100,7 +134,10 @@ extern "platform-intrinsic" { pub(crate) fn simd_bitmask(x: T) -> U; // select - pub(crate) fn simd_select(m: M, a: T, b: T) -> T; + // first argument is a vector of integers, -1 (all bits 1) is "true" + // logically equivalent to (yes & m) | (no & (m^-1), + // but you can use it on floats. + pub(crate) fn simd_select(m: M, yes: T, no: T) -> T; #[allow(unused)] - pub(crate) fn simd_select_bitmask(m: M, a: T, b: T) -> T; + pub(crate) fn simd_select_bitmask(m: M, yes: T, no: T) -> T; } diff --git a/crates/core_simd/src/lib.rs b/crates/core_simd/src/lib.rs index 41f64e972d950..91ae34c05e095 100644 --- a/crates/core_simd/src/lib.rs +++ b/crates/core_simd/src/lib.rs @@ -3,6 +3,7 @@ const_fn_trait_bound, convert_float_to_int, decl_macro, + intra_doc_pointers, platform_intrinsics, repr_simd, simd_ffi, diff --git a/crates/core_simd/src/round.rs b/crates/core_simd/src/round.rs index f1724cbc26337..556bc2cc1feee 100644 --- a/crates/core_simd/src/round.rs +++ b/crates/core_simd/src/round.rs @@ -19,6 +19,11 @@ macro_rules! implement { /// * Not be NaN /// * Not be infinite /// * Be representable in the return type, after truncating off its fractional part + /// + /// If these requirements are infeasible or costly, consider using the safe function [cast], + /// which saturates on conversion. + /// + /// [cast]: Simd::cast #[inline] pub unsafe fn to_int_unchecked(self) -> Simd where diff --git a/crates/core_simd/src/select.rs b/crates/core_simd/src/select.rs index 8d521057fbd3e..3acf07260e12b 100644 --- a/crates/core_simd/src/select.rs +++ b/crates/core_simd/src/select.rs @@ -11,6 +11,7 @@ where /// For each lane in the mask, choose the corresponding lane from `true_values` if /// that lane mask is true, and `false_values` if that lane mask is false. /// + /// # Examples /// ``` /// # #![feature(portable_simd)] /// # #[cfg(feature = "std")] use core_simd::{Simd, Mask}; @@ -31,6 +32,8 @@ where where U: SimdElement, { + // Safety: The mask has been cast to a vector of integers, + // and the operands to select between are vectors of the same type and length. unsafe { intrinsics::simd_select(self.to_int(), true_values, false_values) } } @@ -39,6 +42,7 @@ where /// For each lane in the mask, choose the corresponding lane from `true_values` if /// that lane mask is true, and `false_values` if that lane mask is false. /// + /// # Examples /// ``` /// # #![feature(portable_simd)] /// # #[cfg(feature = "std")] use core_simd::Mask; diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs index e452fa8bfc829..ff1b2c756ad40 100644 --- a/crates/core_simd/src/vector.rs +++ b/crates/core_simd/src/vector.rs @@ -44,6 +44,47 @@ use crate::simd::{LaneCount, Mask, MaskElement, SupportedLaneCount}; /// /// [`Wrapping`]: core::num::Wrapping /// +/// # Layout +/// `Simd` has a layout similar to `[T; N]` (identical "shapes"), but with a greater alignment. +/// `[T; N]` is aligned to `T`, but `Simd` will have an alignment based on both `T` and `N`. +/// It is thus sound to [`transmute`] `Simd` to `[T; N]`, and will typically optimize to zero cost, +/// but the reverse transmutation is more likely to require a copy the compiler cannot simply elide. +/// +/// # ABI "Features" +/// Due to Rust's safety guarantees, `Simd` is currently passed to and from functions via memory, not SIMD registers, +/// except as an optimization. `#[inline]` hints are recommended on functions that accept `Simd` or return it. +/// The need for this may be corrected in the future. +/// +/// # Safe SIMD with Unsafe Rust +/// +/// Operations with `Simd` are typically safe, but there are many reasons to want to combine SIMD with `unsafe` code. +/// Care must be taken to respect differences between `Simd` and other types it may be transformed into or derived from. +/// In particular, the layout of `Simd` may be similar to `[T; N]`, and may allow some transmutations, +/// but references to `[T; N]` are not interchangeable with those to `Simd`. +/// Thus, when using `unsafe` Rust to read and write `Simd` through [raw pointers], it is a good idea to first try with +/// [`read_unaligned`] and [`write_unaligned`]. This is because: +/// - [`read`] and [`write`] require full alignment (in this case, `Simd`'s alignment) +/// - the likely source for reading or destination for writing `Simd` is [`[T]`](slice) and similar types, aligned to `T` +/// - combining these actions would violate the `unsafe` contract and explode the program into a puff of **undefined behavior** +/// - the compiler can implicitly adjust layouts to make unaligned reads or writes fully aligned if it sees the optimization +/// - most contemporary processors suffer no performance penalty for "unaligned" reads and writes that are aligned at runtime +/// +/// By imposing less obligations, unaligned functions are less likely to make the program unsound, +/// and may be just as fast as stricter alternatives. +/// When trying to guarantee alignment, [`[T]::as_simd`][as_simd] is an option for converting `[T]` to `[Simd]`, +/// and allows soundly operating on an aligned SIMD body, but it may cost more time when handling the scalar head and tail. +/// If these are not sufficient, then it is most ideal to design data structures to be already aligned +/// to the `Simd` you wish to use before using `unsafe` Rust to read or write. +/// More conventional ways to compensate for these facts, like materializing `Simd` to or from an array first, +/// are handled by safe methods like [`Simd::from_array`] and [`Simd::from_slice`]. +/// +/// [`transmute`]: core::mem::transmute +/// [raw pointers]: pointer +/// [`read_unaligned`]: pointer::read_unaligned +/// [`write_unaligned`]: pointer::write_unaligned +/// [`read`]: pointer::read +/// [`write`]: pointer::write +/// [as_simd]: slice::as_simd #[repr(simd)] pub struct Simd([T; LANES]) where @@ -133,6 +174,7 @@ where #[inline] #[cfg(not(bootstrap))] pub fn cast(self) -> Simd { + // Safety: The input argument is a vector of a known SIMD type. unsafe { intrinsics::simd_as(self) } } From 3eb983ed9916147ad4f2d93679cbff5276ca87ca Mon Sep 17 00:00:00 2001 From: Chris Copeland Date: Sat, 19 Feb 2022 12:05:51 -0800 Subject: [PATCH 14/23] Fix `setsockopt` and `getsockopt` parameter names. Previously `level` was named `opt` and `option_name` was named `val`, then extra names of `payload` or `slot` were used for the option value. This change aligns the wrapper parameters with their names in POSIX. Winsock uses similar but more abbreviated names: `level`, `optname`, `optval`, `optlen`. --- library/std/src/sys_common/net.rs | 34 +++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 70b29d4a92ed5..ede34c832e72f 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -58,21 +58,37 @@ cfg_if::cfg_if! { // sockaddr and misc bindings //////////////////////////////////////////////////////////////////////////////// -pub fn setsockopt(sock: &Socket, opt: c_int, val: c_int, payload: T) -> io::Result<()> { +pub fn setsockopt( + sock: &Socket, + level: c_int, + option_name: c_int, + option_value: T, +) -> io::Result<()> { unsafe { - let payload = &payload as *const T as *const c_void; - cvt(c::setsockopt(sock.as_raw(), opt, val, payload, mem::size_of::() as c::socklen_t))?; + cvt(c::setsockopt( + sock.as_raw(), + level, + option_name, + &option_value as *const T as *const _, + mem::size_of::() as c::socklen_t, + ))?; Ok(()) } } -pub fn getsockopt(sock: &Socket, opt: c_int, val: c_int) -> io::Result { +pub fn getsockopt(sock: &Socket, level: c_int, option_name: c_int) -> io::Result { unsafe { - let mut slot: T = mem::zeroed(); - let mut len = mem::size_of::() as c::socklen_t; - cvt(c::getsockopt(sock.as_raw(), opt, val, &mut slot as *mut _ as *mut _, &mut len))?; - assert_eq!(len as usize, mem::size_of::()); - Ok(slot) + let mut option_value: T = mem::zeroed(); + let mut option_len = mem::size_of::() as c::socklen_t; + cvt(c::getsockopt( + sock.as_raw(), + level, + option_name, + &mut option_value as *mut T as *mut _, + &mut option_len, + ))?; + assert_eq!(option_len as usize, mem::size_of::()); + Ok(option_value) } } From f2ebd0a11fd7ed131d669fcac20fa3b8df642f39 Mon Sep 17 00:00:00 2001 From: Chris Copeland Date: Sat, 19 Feb 2022 12:08:06 -0800 Subject: [PATCH 15/23] Remove assertion on output length for `getsockopt`. POSIX allows `getsockopt` to set `*option_len` to a smaller value if necessary. Windows will set `*option_len` to 1 for boolean options even when the caller passes a `BOOL` (`int`) with `*option_len` as 4. --- library/std/src/sys_common/net.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index ede34c832e72f..3b7cdd55a081c 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -87,7 +87,6 @@ pub fn getsockopt(sock: &Socket, level: c_int, option_name: c_int) -> i &mut option_value as *mut T as *mut _, &mut option_len, ))?; - assert_eq!(option_len as usize, mem::size_of::()); Ok(option_value) } } From b02698c7e6843d6feacc394cb7f83f3fc347c3e2 Mon Sep 17 00:00:00 2001 From: Chris Copeland Date: Wed, 16 Feb 2022 22:02:58 -0800 Subject: [PATCH 16/23] use `BOOL` for `TCP_NODELAY` `setsockopt` value on Windows This issue was found by the Wine project and mitigated there [1]. Windows' documented interface for `setsockopt` expects a `BOOL` (a `typedef` for `int`) for `TCP_NODELAY` [2]. Windows is forgiving and will accept any positive length and interpret the first byte of `*option_value` as the value, so this bug does not affect Windows itself, but does affect systems implementing Windows' interface more strictly, such as Wine. Wine was previously passing this through to the host's `setsockopt`, where, e.g., Linux requires that `option_len` be correct for the chosen option, and `TCP_NODELAY` expects an `int`. [1]: https://source.winehq.org/git/wine.git/commit/d6ea38f32dfd3edbe107a255c37e9f7f3da06ae7 [2]: https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-setsockopt --- library/std/src/sys/windows/net.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs index aa6400aeefa0d..5de1231378488 100644 --- a/library/std/src/sys/windows/net.rs +++ b/library/std/src/sys/windows/net.rs @@ -407,11 +407,11 @@ impl Socket { } pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - net::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BYTE) + net::setsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY, nodelay as c::BOOL) } pub fn nodelay(&self) -> io::Result { - let raw: c::BYTE = net::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; + let raw: c::BOOL = net::getsockopt(self, c::IPPROTO_TCP, c::TCP_NODELAY)?; Ok(raw != 0) } From 842ac87747c4a6f8002ada6bab04d97320d206fc Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Thu, 13 Jan 2022 21:20:17 -0500 Subject: [PATCH 17/23] Use bitmask trait --- crates/core_simd/src/masks.rs | 22 ++----- crates/core_simd/src/masks/bitmask.rs | 12 +--- crates/core_simd/src/masks/full_masks.rs | 35 ++--------- crates/core_simd/src/masks/to_bitmask.rs | 78 ++++++++++++++++++++++++ crates/core_simd/tests/masks.rs | 6 +- 5 files changed, 93 insertions(+), 60 deletions(-) create mode 100644 crates/core_simd/src/masks/to_bitmask.rs diff --git a/crates/core_simd/src/masks.rs b/crates/core_simd/src/masks.rs index ae1fef53da88e..22514728ffafc 100644 --- a/crates/core_simd/src/masks.rs +++ b/crates/core_simd/src/masks.rs @@ -12,8 +12,10 @@ )] mod mask_impl; -use crate::simd::intrinsics; -use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount}; +mod to_bitmask; +pub use to_bitmask::ToBitMask; + +use crate::simd::{intrinsics, LaneCount, Simd, SimdElement, SupportedLaneCount}; use core::cmp::Ordering; use core::{fmt, mem}; @@ -216,22 +218,6 @@ where } } - /// Convert this mask to a bitmask, with one bit set per lane. - #[cfg(feature = "generic_const_exprs")] - #[inline] - #[must_use = "method returns a new array and does not mutate the original value"] - pub fn to_bitmask(self) -> [u8; LaneCount::::BITMASK_LEN] { - self.0.to_bitmask() - } - - /// Convert a bitmask to a mask. - #[cfg(feature = "generic_const_exprs")] - #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn from_bitmask(bitmask: [u8; LaneCount::::BITMASK_LEN]) -> Self { - Self(mask_impl::Mask::from_bitmask(bitmask)) - } - /// Returns true if any lane is set, or false otherwise. #[inline] #[must_use = "method returns a new bool and does not mutate the original value"] diff --git a/crates/core_simd/src/masks/bitmask.rs b/crates/core_simd/src/masks/bitmask.rs index b4217dc87ba9c..f20f83ecb38e8 100644 --- a/crates/core_simd/src/masks/bitmask.rs +++ b/crates/core_simd/src/masks/bitmask.rs @@ -115,20 +115,14 @@ where unsafe { Self(intrinsics::simd_bitmask(value), PhantomData) } } - #[cfg(feature = "generic_const_exprs")] #[inline] - #[must_use = "method returns a new array and does not mutate the original value"] - pub fn to_bitmask(self) -> [u8; LaneCount::::BITMASK_LEN] { - // Safety: these are the same type and we are laundering the generic + pub unsafe fn to_bitmask_intrinsic(self) -> U { unsafe { core::mem::transmute_copy(&self.0) } } - #[cfg(feature = "generic_const_exprs")] #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn from_bitmask(bitmask: [u8; LaneCount::::BITMASK_LEN]) -> Self { - // Safety: these are the same type and we are laundering the generic - Self(unsafe { core::mem::transmute_copy(&bitmask) }, PhantomData) + pub unsafe fn from_bitmask_intrinsic(bitmask: U) -> Self { + unsafe { Self(core::mem::transmute_copy(&bitmask), PhantomData) } } #[inline] diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs index e5bb784bb910f..b20b0a4b70849 100644 --- a/crates/core_simd/src/masks/full_masks.rs +++ b/crates/core_simd/src/masks/full_masks.rs @@ -109,41 +109,16 @@ where unsafe { Mask(intrinsics::simd_cast(self.0)) } } - #[cfg(feature = "generic_const_exprs")] #[inline] - #[must_use = "method returns a new array and does not mutate the original value"] - pub fn to_bitmask(self) -> [u8; LaneCount::::BITMASK_LEN] { - unsafe { - let mut bitmask: [u8; LaneCount::::BITMASK_LEN] = - intrinsics::simd_bitmask(self.0); - - // There is a bug where LLVM appears to implement this operation with the wrong - // bit order. - // TODO fix this in a better way - if cfg!(target_endian = "big") { - for x in bitmask.as_mut() { - *x = x.reverse_bits(); - } - } - - bitmask - } + pub unsafe fn to_bitmask_intrinsic(self) -> U { + // Safety: caller must only return bitmask types + unsafe { intrinsics::simd_bitmask(self.0) } } - #[cfg(feature = "generic_const_exprs")] #[inline] - #[must_use = "method returns a new mask and does not mutate the original value"] - pub fn from_bitmask(mut bitmask: [u8; LaneCount::::BITMASK_LEN]) -> Self { + pub unsafe fn from_bitmask_intrinsic(bitmask: U) -> Self { + // Safety: caller must only pass bitmask types unsafe { - // There is a bug where LLVM appears to implement this operation with the wrong - // bit order. - // TODO fix this in a better way - if cfg!(target_endian = "big") { - for x in bitmask.as_mut() { - *x = x.reverse_bits(); - } - } - Self::from_int_unchecked(intrinsics::simd_select_bitmask( bitmask, Self::splat(true).to_int(), diff --git a/crates/core_simd/src/masks/to_bitmask.rs b/crates/core_simd/src/masks/to_bitmask.rs new file mode 100644 index 0000000000000..3a9f89f19eba0 --- /dev/null +++ b/crates/core_simd/src/masks/to_bitmask.rs @@ -0,0 +1,78 @@ +use super::{mask_impl, Mask, MaskElement}; + +/// Converts masks to and from bitmasks. +/// +/// In a bitmask, each bit represents if the corresponding lane in the mask is set. +pub trait ToBitMask { + /// Converts a mask to a bitmask. + fn to_bitmask(self) -> BitMask; + + /// Converts a bitmask to a mask. + fn from_bitmask(bitmask: BitMask) -> Self; +} + +macro_rules! impl_integer_intrinsic { + { $(unsafe impl ToBitMask<$int:ty> for Mask<_, $lanes:literal>)* } => { + $( + impl ToBitMask<$int> for Mask { + fn to_bitmask(self) -> $int { + unsafe { self.0.to_bitmask_intrinsic() } + } + + fn from_bitmask(bitmask: $int) -> Self { + unsafe { Self(mask_impl::Mask::from_bitmask_intrinsic(bitmask)) } + } + } + )* + } +} + +impl_integer_intrinsic! { + unsafe impl ToBitMask for Mask<_, 8> + unsafe impl ToBitMask for Mask<_, 16> + unsafe impl ToBitMask for Mask<_, 32> + unsafe impl ToBitMask for Mask<_, 64> +} + +macro_rules! impl_integer_via { + { $(impl ToBitMask<$int:ty, via $via:ty> for Mask<_, $lanes:literal>)* } => { + $( + impl ToBitMask<$int> for Mask { + fn to_bitmask(self) -> $int { + let bitmask: $via = self.to_bitmask(); + bitmask as _ + } + + fn from_bitmask(bitmask: $int) -> Self { + Self::from_bitmask(bitmask as $via) + } + } + )* + } +} + +impl_integer_via! { + impl ToBitMask for Mask<_, 8> + impl ToBitMask for Mask<_, 8> + impl ToBitMask for Mask<_, 8> + + impl ToBitMask for Mask<_, 16> + impl ToBitMask for Mask<_, 16> + + impl ToBitMask for Mask<_, 32> +} + +#[cfg(target_pointer_width = "32")] +impl_integer_via! { + impl ToBitMask for Mask<_, 8> + impl ToBitMask for Mask<_, 16> + impl ToBitMask for Mask<_, 32> +} + +#[cfg(target_pointer_width = "64")] +impl_integer_via! { + impl ToBitMask for Mask<_, 8> + impl ToBitMask for Mask<_, 16> + impl ToBitMask for Mask<_, 32> + impl ToBitMask for Mask<_, 64> +} diff --git a/crates/core_simd/tests/masks.rs b/crates/core_simd/tests/masks.rs index 6a8ecd33a73cf..965c0fa263597 100644 --- a/crates/core_simd/tests/masks.rs +++ b/crates/core_simd/tests/masks.rs @@ -68,16 +68,16 @@ macro_rules! test_mask_api { assert_eq!(core_simd::Mask::<$type, 8>::from_int(int), mask); } - #[cfg(feature = "generic_const_exprs")] #[test] fn roundtrip_bitmask_conversion() { + use core_simd::ToBitMask; let values = [ true, false, false, true, false, false, true, false, true, true, false, false, false, false, false, true, ]; let mask = core_simd::Mask::<$type, 16>::from_array(values); - let bitmask = mask.to_bitmask(); - assert_eq!(bitmask, [0b01001001, 0b10000011]); + let bitmask: u16 = mask.to_bitmask(); + assert_eq!(bitmask, 0b1000001101001001); assert_eq!(core_simd::Mask::<$type, 16>::from_bitmask(bitmask), mask); } } From 11c3eefa3594055192612d0d6f844e764dcbda15 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Sun, 6 Feb 2022 03:25:27 +0000 Subject: [PATCH 18/23] Manually implement for supported lanes --- crates/core_simd/src/masks/bitmask.rs | 5 +- crates/core_simd/src/masks/full_masks.rs | 6 +- crates/core_simd/src/masks/to_bitmask.rs | 84 +++++++++--------------- crates/core_simd/tests/masks.rs | 2 +- 4 files changed, 38 insertions(+), 59 deletions(-) diff --git a/crates/core_simd/src/masks/bitmask.rs b/crates/core_simd/src/masks/bitmask.rs index f20f83ecb38e8..e27b268960648 100644 --- a/crates/core_simd/src/masks/bitmask.rs +++ b/crates/core_simd/src/masks/bitmask.rs @@ -116,12 +116,13 @@ where } #[inline] - pub unsafe fn to_bitmask_intrinsic(self) -> U { + pub unsafe fn to_bitmask_integer(self) -> U { unsafe { core::mem::transmute_copy(&self.0) } } + // Safety: U must be the integer with the exact number of bits required to hold the bitmask for #[inline] - pub unsafe fn from_bitmask_intrinsic(bitmask: U) -> Self { + pub unsafe fn from_bitmask_integer(bitmask: U) -> Self { unsafe { Self(core::mem::transmute_copy(&bitmask), PhantomData) } } diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs index b20b0a4b70849..90af486a887b1 100644 --- a/crates/core_simd/src/masks/full_masks.rs +++ b/crates/core_simd/src/masks/full_masks.rs @@ -110,13 +110,15 @@ where } #[inline] - pub unsafe fn to_bitmask_intrinsic(self) -> U { + pub unsafe fn to_bitmask_integer(self) -> U { // Safety: caller must only return bitmask types unsafe { intrinsics::simd_bitmask(self.0) } } + // Safety: U must be the integer with the exact number of bits required to hold the bitmask for + // this mask #[inline] - pub unsafe fn from_bitmask_intrinsic(bitmask: U) -> Self { + pub unsafe fn from_bitmask_integer(bitmask: U) -> Self { // Safety: caller must only pass bitmask types unsafe { Self::from_int_unchecked(intrinsics::simd_select_bitmask( diff --git a/crates/core_simd/src/masks/to_bitmask.rs b/crates/core_simd/src/masks/to_bitmask.rs index 3a9f89f19eba0..86143f2331fce 100644 --- a/crates/core_simd/src/masks/to_bitmask.rs +++ b/crates/core_simd/src/masks/to_bitmask.rs @@ -1,78 +1,54 @@ use super::{mask_impl, Mask, MaskElement}; -/// Converts masks to and from bitmasks. +/// Converts masks to and from integer bitmasks. /// -/// In a bitmask, each bit represents if the corresponding lane in the mask is set. -pub trait ToBitMask { +/// Each bit of the bitmask corresponds to a mask lane, starting with the LSB. +pub trait ToBitMask { + /// The integer bitmask type. + type BitMask; + /// Converts a mask to a bitmask. - fn to_bitmask(self) -> BitMask; + fn to_bitmask(self) -> Self::BitMask; /// Converts a bitmask to a mask. - fn from_bitmask(bitmask: BitMask) -> Self; + fn from_bitmask(bitmask: Self::BitMask) -> Self; } -macro_rules! impl_integer_intrinsic { - { $(unsafe impl ToBitMask<$int:ty> for Mask<_, $lanes:literal>)* } => { - $( - impl ToBitMask<$int> for Mask { - fn to_bitmask(self) -> $int { - unsafe { self.0.to_bitmask_intrinsic() } - } +/// Converts masks to and from byte array bitmasks. +/// +/// Each bit of the bitmask corresponds to a mask lane, starting with the LSB of the first byte. +pub trait ToBitMaskArray { + /// The length of the bitmask array. + const BYTES: usize; - fn from_bitmask(bitmask: $int) -> Self { - unsafe { Self(mask_impl::Mask::from_bitmask_intrinsic(bitmask)) } - } - } - )* - } -} + /// Converts a mask to a bitmask. + fn to_bitmask_array(self) -> [u8; Self::BYTES]; -impl_integer_intrinsic! { - unsafe impl ToBitMask for Mask<_, 8> - unsafe impl ToBitMask for Mask<_, 16> - unsafe impl ToBitMask for Mask<_, 32> - unsafe impl ToBitMask for Mask<_, 64> + /// Converts a bitmask to a mask. + fn from_bitmask_array(bitmask: [u8; Self::BYTES]) -> Self; } -macro_rules! impl_integer_via { - { $(impl ToBitMask<$int:ty, via $via:ty> for Mask<_, $lanes:literal>)* } => { +macro_rules! impl_integer_intrinsic { + { $(unsafe impl ToBitMask for Mask<_, $lanes:literal>)* } => { $( - impl ToBitMask<$int> for Mask { + impl ToBitMask for Mask { + type BitMask = $int; + fn to_bitmask(self) -> $int { - let bitmask: $via = self.to_bitmask(); - bitmask as _ + unsafe { self.0.to_bitmask_integer() } } fn from_bitmask(bitmask: $int) -> Self { - Self::from_bitmask(bitmask as $via) + unsafe { Self(mask_impl::Mask::from_bitmask_integer(bitmask)) } } } )* } } -impl_integer_via! { - impl ToBitMask for Mask<_, 8> - impl ToBitMask for Mask<_, 8> - impl ToBitMask for Mask<_, 8> - - impl ToBitMask for Mask<_, 16> - impl ToBitMask for Mask<_, 16> - - impl ToBitMask for Mask<_, 32> -} - -#[cfg(target_pointer_width = "32")] -impl_integer_via! { - impl ToBitMask for Mask<_, 8> - impl ToBitMask for Mask<_, 16> - impl ToBitMask for Mask<_, 32> -} - -#[cfg(target_pointer_width = "64")] -impl_integer_via! { - impl ToBitMask for Mask<_, 8> - impl ToBitMask for Mask<_, 16> - impl ToBitMask for Mask<_, 32> - impl ToBitMask for Mask<_, 64> +impl_integer_intrinsic! { + unsafe impl ToBitMask for Mask<_, 8> + unsafe impl ToBitMask for Mask<_, 16> + unsafe impl ToBitMask for Mask<_, 32> + unsafe impl ToBitMask for Mask<_, 64> } diff --git a/crates/core_simd/tests/masks.rs b/crates/core_simd/tests/masks.rs index 965c0fa263597..3aec36ca7b746 100644 --- a/crates/core_simd/tests/masks.rs +++ b/crates/core_simd/tests/masks.rs @@ -76,7 +76,7 @@ macro_rules! test_mask_api { true, true, false, false, false, false, false, true, ]; let mask = core_simd::Mask::<$type, 16>::from_array(values); - let bitmask: u16 = mask.to_bitmask(); + let bitmask = mask.to_bitmask(); assert_eq!(bitmask, 0b1000001101001001); assert_eq!(core_simd::Mask::<$type, 16>::from_bitmask(bitmask), mask); } From 20fa4b76235afb6a2ad543781a10a14e8013b143 Mon Sep 17 00:00:00 2001 From: Caleb Zulawski Date: Wed, 9 Feb 2022 04:54:05 +0000 Subject: [PATCH 19/23] Make internal mask implementation safe --- crates/core_simd/src/masks/bitmask.rs | 15 +++++-- crates/core_simd/src/masks/full_masks.rs | 51 ++++++++++++++++++++---- crates/core_simd/src/masks/to_bitmask.rs | 39 +++++++++--------- 3 files changed, 75 insertions(+), 30 deletions(-) diff --git a/crates/core_simd/src/masks/bitmask.rs b/crates/core_simd/src/masks/bitmask.rs index e27b268960648..7bf2add20364d 100644 --- a/crates/core_simd/src/masks/bitmask.rs +++ b/crates/core_simd/src/masks/bitmask.rs @@ -1,7 +1,7 @@ #![allow(unused_imports)] use super::MaskElement; use crate::simd::intrinsics; -use crate::simd::{LaneCount, Simd, SupportedLaneCount}; +use crate::simd::{LaneCount, Simd, SupportedLaneCount, ToBitMask}; use core::marker::PhantomData; /// A mask where each lane is represented by a single bit. @@ -116,13 +116,20 @@ where } #[inline] - pub unsafe fn to_bitmask_integer(self) -> U { + pub fn to_bitmask_integer(self) -> U + where + super::Mask: ToBitMask, + { + // Safety: these are the same types unsafe { core::mem::transmute_copy(&self.0) } } - // Safety: U must be the integer with the exact number of bits required to hold the bitmask for #[inline] - pub unsafe fn from_bitmask_integer(bitmask: U) -> Self { + pub fn from_bitmask_integer(bitmask: U) -> Self + where + super::Mask: ToBitMask, + { + // Safety: these are the same types unsafe { Self(core::mem::transmute_copy(&bitmask), PhantomData) } } diff --git a/crates/core_simd/src/masks/full_masks.rs b/crates/core_simd/src/masks/full_masks.rs index 90af486a887b1..848997a07925a 100644 --- a/crates/core_simd/src/masks/full_masks.rs +++ b/crates/core_simd/src/masks/full_masks.rs @@ -2,7 +2,7 @@ use super::MaskElement; use crate::simd::intrinsics; -use crate::simd::{LaneCount, Simd, SupportedLaneCount}; +use crate::simd::{LaneCount, Simd, SupportedLaneCount, ToBitMask}; #[repr(transparent)] pub struct Mask(Simd) @@ -66,6 +66,23 @@ where } } +// Used for bitmask bit order workaround +pub(crate) trait ReverseBits { + fn reverse_bits(self) -> Self; +} + +macro_rules! impl_reverse_bits { + { $($int:ty),* } => { + $( + impl ReverseBits for $int { + fn reverse_bits(self) -> Self { <$int>::reverse_bits(self) } + } + )* + } +} + +impl_reverse_bits! { u8, u16, u32, u64 } + impl Mask where T: MaskElement, @@ -110,16 +127,34 @@ where } #[inline] - pub unsafe fn to_bitmask_integer(self) -> U { - // Safety: caller must only return bitmask types - unsafe { intrinsics::simd_bitmask(self.0) } + pub(crate) fn to_bitmask_integer(self) -> U + where + super::Mask: ToBitMask, + { + // Safety: U is required to be the appropriate bitmask type + let bitmask: U = unsafe { intrinsics::simd_bitmask(self.0) }; + + // LLVM assumes bit order should match endianness + if cfg!(target_endian = "big") { + bitmask.reverse_bits() + } else { + bitmask + } } - // Safety: U must be the integer with the exact number of bits required to hold the bitmask for - // this mask #[inline] - pub unsafe fn from_bitmask_integer(bitmask: U) -> Self { - // Safety: caller must only pass bitmask types + pub(crate) fn from_bitmask_integer(bitmask: U) -> Self + where + super::Mask: ToBitMask, + { + // LLVM assumes bit order should match endianness + let bitmask = if cfg!(target_endian = "big") { + bitmask.reverse_bits() + } else { + bitmask + }; + + // Safety: U is required to be the appropriate bitmask type unsafe { Self::from_int_unchecked(intrinsics::simd_select_bitmask( bitmask, diff --git a/crates/core_simd/src/masks/to_bitmask.rs b/crates/core_simd/src/masks/to_bitmask.rs index 86143f2331fce..1c2037764c1e4 100644 --- a/crates/core_simd/src/masks/to_bitmask.rs +++ b/crates/core_simd/src/masks/to_bitmask.rs @@ -1,9 +1,26 @@ use super::{mask_impl, Mask, MaskElement}; +use crate::simd::{LaneCount, SupportedLaneCount}; + +mod sealed { + pub trait Sealed {} +} +pub use sealed::Sealed; + +impl Sealed for Mask +where + T: MaskElement, + LaneCount: SupportedLaneCount, +{ +} /// Converts masks to and from integer bitmasks. /// /// Each bit of the bitmask corresponds to a mask lane, starting with the LSB. -pub trait ToBitMask { +/// +/// # Safety +/// This trait is `unsafe` and sealed, since the `BitMask` type must match the number of lanes in +/// the mask. +pub unsafe trait ToBitMask: Sealed { /// The integer bitmask type. type BitMask; @@ -14,32 +31,18 @@ pub trait ToBitMask { fn from_bitmask(bitmask: Self::BitMask) -> Self; } -/// Converts masks to and from byte array bitmasks. -/// -/// Each bit of the bitmask corresponds to a mask lane, starting with the LSB of the first byte. -pub trait ToBitMaskArray { - /// The length of the bitmask array. - const BYTES: usize; - - /// Converts a mask to a bitmask. - fn to_bitmask_array(self) -> [u8; Self::BYTES]; - - /// Converts a bitmask to a mask. - fn from_bitmask_array(bitmask: [u8; Self::BYTES]) -> Self; -} - macro_rules! impl_integer_intrinsic { { $(unsafe impl ToBitMask for Mask<_, $lanes:literal>)* } => { $( - impl ToBitMask for Mask { + unsafe impl ToBitMask for Mask { type BitMask = $int; fn to_bitmask(self) -> $int { - unsafe { self.0.to_bitmask_integer() } + self.0.to_bitmask_integer() } fn from_bitmask(bitmask: $int) -> Self { - unsafe { Self(mask_impl::Mask::from_bitmask_integer(bitmask)) } + Self(mask_impl::Mask::from_bitmask_integer(bitmask)) } } )* From d3d2a279fe7afa2c7f06c50ef5e70b8446bb68ee Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 25 Feb 2022 15:30:29 -0800 Subject: [PATCH 20/23] Add Atomic*::from_mut_slice --- library/core/src/sync/atomic.rs | 94 +++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 9ee88dd601493..2b8bbe1924450 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -340,6 +340,32 @@ impl AtomicBool { unsafe { &mut *(v as *mut bool as *mut Self) } } + /// Get atomic access to a `&mut [bool]` slice. + /// + /// # Examples + /// + /// ``` + /// #![feature(atomic_from_mut, scoped_threads)] + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// + /// let mut some_bools = [false; 10]; + /// let a = &*AtomicBool::from_mut_slice(&mut some_bools); + /// std::thread::scope(|s| { + /// for i in 0..a.len() { + /// s.spawn(move |_| a[i].store(true, Ordering::Relaxed)); + /// } + /// }); + /// assert_eq!(some_bools, [true; 10]); + /// ``` + #[inline] + #[cfg(target_has_atomic_equal_alignment = "8")] + #[unstable(feature = "atomic_from_mut", issue = "76314")] + pub fn from_mut_slice(v: &mut [bool]) -> &mut [Self] { + // SAFETY: the mutable reference guarantees unique ownership, and + // alignment of both `bool` and `Self` is 1. + unsafe { &mut *(v as *mut [bool] as *mut [Self]) } + } + /// Consumes the atomic and returns the contained value. /// /// This is safe because passing `self` by value guarantees that no other threads are @@ -945,6 +971,42 @@ impl AtomicPtr { unsafe { &mut *(v as *mut *mut T as *mut Self) } } + /// Get atomic access to a slice of pointers. + /// + /// # Examples + /// + /// ``` + /// #![feature(atomic_from_mut, scoped_threads)] + /// use std::ptr::null_mut; + /// use std::sync::atomic::{AtomicPtr, Ordering}; + /// + /// let mut some_ptrs = [null_mut::(); 10]; + /// let a = &*AtomicPtr::from_mut_slice(&mut some_ptrs); + /// std::thread::scope(|s| { + /// for i in 0..a.len() { + /// s.spawn(move |_| { + /// let name = Box::new(format!("thread{i}")); + /// a[i].store(Box::into_raw(name), Ordering::Relaxed); + /// }); + /// } + /// }); + /// for p in some_ptrs { + /// assert!(!p.is_null()); + /// let name = unsafe { Box::from_raw(p) }; + /// println!("Hello, {name}!"); + /// } + /// ``` + #[inline] + #[cfg(target_has_atomic_equal_alignment = "ptr")] + #[unstable(feature = "atomic_from_mut", issue = "76314")] + pub fn from_mut_slice(v: &mut [*mut T]) -> &mut [Self] { + // SAFETY: + // - the mutable reference guarantees unique ownership. + // - the alignment of `*mut T` and `Self` is the same on all platforms + // supported by rust, as verified above. + unsafe { &mut *(v as *mut [*mut T] as *mut [Self]) } + } + /// Consumes the atomic and returns the contained value. /// /// This is safe because passing `self` by value guarantees that no other threads are @@ -1459,6 +1521,38 @@ macro_rules! atomic_int { unsafe { &mut *(v as *mut $int_type as *mut Self) } } + #[doc = concat!("Get atomic access to a `&mut [", stringify!($int_type), "]` slice.")] + /// + /// # Examples + /// + /// ``` + /// #![feature(atomic_from_mut, scoped_threads)] + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + /// let mut some_ints = [0; 10]; + #[doc = concat!("let a = &*", stringify!($atomic_type), "::from_mut_slice(&mut some_ints);")] + /// std::thread::scope(|s| { + /// for i in 0..a.len() { + /// s.spawn(move |_| a[i].store(i as _, Ordering::Relaxed)); + /// } + /// }); + /// for (i, n) in some_ints.into_iter().enumerate() { + /// assert_eq!(i, n as usize); + /// } + /// ``` + #[inline] + #[$cfg_align] + #[unstable(feature = "atomic_from_mut", issue = "76314")] + pub fn from_mut_slice(v: &mut [$int_type]) -> &mut [Self] { + use crate::mem::align_of; + let [] = [(); align_of::() - align_of::<$int_type>()]; + // SAFETY: + // - the mutable reference guarantees unique ownership. + // - the alignment of `$int_type` and `Self` is the + // same, as promised by $cfg_align and verified above. + unsafe { &mut *(v as *mut [$int_type] as *mut [Self]) } + } + /// Consumes the atomic and returns the contained value. /// /// This is safe because passing `self` by value guarantees that no other threads are From 6dcf5d8fdeb341a3ec2ed00d2cb4449da35d8623 Mon Sep 17 00:00:00 2001 From: Ruby Lazuli Date: Fri, 11 Feb 2022 18:21:02 -0600 Subject: [PATCH 21/23] Lint against more useless `#[must_use]` attributes This expands the existing `#[must_use]` check in `unused_attributes` to lint against pretty much everything `#[must_use]` doesn't support. Fixes #93906. --- .../rustc_macros/src/session_diagnostic.rs | 1 - compiler/rustc_passes/src/check_attr.rs | 35 +- .../issue-43106-gating-of-builtin-attrs.rs | 9 +- ...issue-43106-gating-of-builtin-attrs.stderr | 346 ++++++++++-------- .../lint/unused/unused_attributes-must_use.rs | 125 +++++++ .../unused/unused_attributes-must_use.stderr | 175 +++++++++ 6 files changed, 527 insertions(+), 164 deletions(-) create mode 100644 src/test/ui/lint/unused/unused_attributes-must_use.rs create mode 100644 src/test/ui/lint/unused/unused_attributes-must_use.stderr diff --git a/compiler/rustc_macros/src/session_diagnostic.rs b/compiler/rustc_macros/src/session_diagnostic.rs index 80dcf99da6224..cb0654e0133c6 100644 --- a/compiler/rustc_macros/src/session_diagnostic.rs +++ b/compiler/rustc_macros/src/session_diagnostic.rs @@ -587,7 +587,6 @@ impl<'a> SessionDiagnosticDeriveBuilder<'a> { // next call to `it.next()` retrieves the next character. while let Some(c) = it.next() { if c == '{' && *it.peek().unwrap_or(&'\0') != '{' { - #[must_use] let mut eat_argument = || -> Option { let mut result = String::new(); // Format specifiers look like diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 479a08e43c01a..a87b253aae0aa 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1111,7 +1111,7 @@ impl CheckAttrVisitor<'_> { } /// Warns against some misuses of `#[must_use]` - fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, _target: Target) -> bool { + fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool { let node = self.tcx.hir().get(hir_id); if let Some(fn_node) = node.fn_kind() { if let rustc_hir::IsAsync::Async = fn_node.asyncness() { @@ -1131,6 +1131,39 @@ impl CheckAttrVisitor<'_> { } } + if !matches!( + target, + Target::Fn + | Target::Enum + | Target::Struct + | Target::Union + | Target::Method(_) + | Target::ForeignFn + // `impl Trait` in return position can trip + // `unused_must_use` if `Trait` is marked as + // `#[must_use]` + | Target::Trait + ) { + let article = match target { + Target::ExternCrate + | Target::OpaqueTy + | Target::Enum + | Target::Impl + | Target::Expression + | Target::Arm + | Target::AssocConst + | Target::AssocTy => "an", + _ => "a", + }; + + self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { + lint.build(&format!( + "`#[must_use]` has no effect when applied to {article} {target}" + )) + .emit(); + }); + } + // For now, its always valid true } diff --git a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs index ed382406efacf..b0e295178c8a5 100644 --- a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs +++ b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs @@ -71,6 +71,7 @@ //~^^ WARN this was previously accepted by the compiler // see issue-43106-gating-of-rustc_deprecated.rs #![must_use] +//~^ WARN `#[must_use]` has no effect // see issue-43106-gating-of-stable.rs // see issue-43106-gating-of-unstable.rs // see issue-43106-gating-of-deprecated.rs @@ -597,17 +598,17 @@ mod deprecated { #[deprecated] impl super::StructForDeprecated { } } -#[must_use] +#[must_use] //~ WARN `#[must_use]` has no effect mod must_use { - mod inner { #![must_use] } + mod inner { #![must_use] } //~ WARN `#[must_use]` has no effect #[must_use] fn f() { } #[must_use] struct S; - #[must_use] type T = S; + #[must_use] type T = S; //~ WARN `#[must_use]` has no effect - #[must_use] impl S { } + #[must_use] impl S { } //~ WARN `#[must_use]` has no effect } #[windows_subsystem = "windows"] diff --git a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index bd3e33320c384..2431957e5391d 100644 --- a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -29,151 +29,151 @@ LL | #![deny(x5100)] | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:105:8 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:106:8 | LL | #[warn(x5400)] | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:108:25 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:109:25 | LL | mod inner { #![warn(x5400)] } | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:111:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:112:12 | LL | #[warn(x5400)] fn f() { } | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:114:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:115:12 | LL | #[warn(x5400)] struct S; | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:117:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:118:12 | LL | #[warn(x5400)] type T = S; | ^^^^^ warning: unknown lint: `x5400` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:120:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:121:12 | LL | #[warn(x5400)] impl S { } | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:124:9 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:125:9 | LL | #[allow(x5300)] | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:127:26 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:128:26 | LL | mod inner { #![allow(x5300)] } | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:130:13 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:131:13 | LL | #[allow(x5300)] fn f() { } | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:133:13 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:134:13 | LL | #[allow(x5300)] struct S; | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:136:13 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:137:13 | LL | #[allow(x5300)] type T = S; | ^^^^^ warning: unknown lint: `x5300` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:139:13 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:140:13 | LL | #[allow(x5300)] impl S { } | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:143:10 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:144:10 | LL | #[forbid(x5200)] | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:146:27 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:147:27 | LL | mod inner { #![forbid(x5200)] } | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:149:14 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:150:14 | LL | #[forbid(x5200)] fn f() { } | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:152:14 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:153:14 | LL | #[forbid(x5200)] struct S; | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:155:14 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:156:14 | LL | #[forbid(x5200)] type T = S; | ^^^^^ warning: unknown lint: `x5200` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:158:14 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:159:14 | LL | #[forbid(x5200)] impl S { } | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:162:8 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:163:8 | LL | #[deny(x5100)] | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:165:25 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:166:25 | LL | mod inner { #![deny(x5100)] } | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:168:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:169:12 | LL | #[deny(x5100)] fn f() { } | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:171:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:172:12 | LL | #[deny(x5100)] struct S; | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:174:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:175:12 | LL | #[deny(x5100)] type T = S; | ^^^^^ warning: unknown lint: `x5100` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:177:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:178:12 | LL | #[deny(x5100)] impl S { } | ^^^^^ warning: `#[macro_escape]` is a deprecated synonym for `#[macro_use]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:400:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:401:17 | LL | mod inner { #![macro_escape] } | ^^^^^^^^^^^^^^^^ @@ -181,13 +181,13 @@ LL | mod inner { #![macro_escape] } = help: try an outer attribute: `#[macro_use]` warning: `#[macro_escape]` is a deprecated synonym for `#[macro_use]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:397:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:398:1 | LL | #[macro_escape] | ^^^^^^^^^^^^^^^ warning: use of deprecated attribute `crate_id`: no longer used. - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:84:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:85:1 | LL | #![crate_id = "10"] | ^^^^^^^^^^^^^^^^^^^ help: remove this attribute @@ -195,13 +195,13 @@ LL | #![crate_id = "10"] = note: `#[warn(deprecated)]` on by default warning: use of deprecated attribute `no_start`: no longer used. - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:94:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:95:1 | LL | #![no_start] | ^^^^^^^^^^^^ help: remove this attribute warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:198:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:199:1 | LL | #[macro_export] | ^^^^^^^^^^^^^^^ @@ -213,13 +213,13 @@ LL | #![warn(unused_attributes, unknown_lints)] | ^^^^^^^^^^^^^^^^^ warning: `#[automatically_derived]` only has an effect on items - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:266:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:267:1 | LL | #[automatically_derived] | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:284:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:285:1 | LL | #[no_mangle] | ^^^^^^^^^^^^ @@ -236,31 +236,31 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: `#[should_panic]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:324:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:325:1 | LL | #[should_panic] | ^^^^^^^^^^^^^^^ warning: `#[ignore]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:342:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:343:1 | LL | #[ignore] | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:377:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:378:1 | LL | #[reexport_test_harness_main = "2900"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:417:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:418:1 | LL | #[no_std] | ^^^^^^^^^ warning: attribute should be applied to a function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:453:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:454:1 | LL | #[cold] | ^^^^^^^ @@ -277,7 +277,7 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:482:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:483:1 | LL | #[link_name = "1900"] | ^^^^^^^^^^^^^^^^^^^^^ @@ -294,7 +294,7 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:521:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:522:1 | LL | #[link_section = "1800"] | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -311,7 +311,7 @@ LL | | } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:553:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:554:1 | LL | #[link()] | ^^^^^^^^^ @@ -327,50 +327,56 @@ LL | | } | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +warning: `#[must_use]` has no effect when applied to a module + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:601:1 + | +LL | #[must_use] + | ^^^^^^^^^^^ + warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:613:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:614:1 | LL | #[windows_subsystem = "windows"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:634:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:635:1 | LL | #[crate_name = "0900"] | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:653:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:654:1 | LL | #[crate_type = "0800"] | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:672:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:673:1 | LL | #[feature(x0600)] | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:692:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:693:1 | LL | #[no_main] | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:711:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:712:1 | LL | #[no_builtins] | ^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:730:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:731:1 | LL | #[recursion_limit="0200"] | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:749:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:750:1 | LL | #[type_length_limit="0100"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -425,110 +431,116 @@ LL | #![link_section = "1800"] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +warning: `#[must_use]` has no effect when applied to a module + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:73:1 + | +LL | #![must_use] + | ^^^^^^^^^^^^ + warning: `#[macro_use]` only has an effect on `extern crate` and modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:185:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:186:5 | LL | #[macro_use] fn f() { } | ^^^^^^^^^^^^ warning: `#[macro_use]` only has an effect on `extern crate` and modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:188:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:189:5 | LL | #[macro_use] struct S; | ^^^^^^^^^^^^ warning: `#[macro_use]` only has an effect on `extern crate` and modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:191:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:192:5 | LL | #[macro_use] type T = S; | ^^^^^^^^^^^^ warning: `#[macro_use]` only has an effect on `extern crate` and modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:194:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:195:5 | LL | #[macro_use] impl S { } | ^^^^^^^^^^^^ warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:201:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:202:17 | LL | mod inner { #![macro_export] } | ^^^^^^^^^^^^^^^^ warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:204:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:205:5 | LL | #[macro_export] fn f() { } | ^^^^^^^^^^^^^^^ warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:207:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:208:5 | LL | #[macro_export] struct S; | ^^^^^^^^^^^^^^^ warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:210:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:211:5 | LL | #[macro_export] type T = S; | ^^^^^^^^^^^^^^^ warning: `#[macro_export]` only has an effect on macro definitions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:213:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:214:5 | LL | #[macro_export] impl S { } | ^^^^^^^^^^^^^^^ warning: `#[path]` only has an effect on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:253:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:254:5 | LL | #[path = "3800"] fn f() { } | ^^^^^^^^^^^^^^^^ warning: `#[path]` only has an effect on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:256:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:257:5 | LL | #[path = "3800"] struct S; | ^^^^^^^^^^^^^^^^ warning: `#[path]` only has an effect on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:259:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:260:5 | LL | #[path = "3800"] type T = S; | ^^^^^^^^^^^^^^^^ warning: `#[path]` only has an effect on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:262:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:263:5 | LL | #[path = "3800"] impl S { } | ^^^^^^^^^^^^^^^^ warning: `#[automatically_derived]` only has an effect on items - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:269:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:270:17 | LL | mod inner { #![automatically_derived] } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: `#[automatically_derived]` only has an effect on items - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:272:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:273:5 | LL | #[automatically_derived] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: `#[automatically_derived]` only has an effect on items - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:275:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:276:5 | LL | #[automatically_derived] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: `#[automatically_derived]` only has an effect on items - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:278:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:279:5 | LL | #[automatically_derived] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^ warning: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:289:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:290:17 | LL | mod inner { #![no_mangle] } | ------------^^^^^^^^^^^^^-- not a free function, impl method or static @@ -536,7 +548,7 @@ LL | mod inner { #![no_mangle] } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:296:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:297:5 | LL | #[no_mangle] struct S; | ^^^^^^^^^^^^ --------- not a free function, impl method or static @@ -544,7 +556,7 @@ LL | #[no_mangle] struct S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:301:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:302:5 | LL | #[no_mangle] type T = S; | ^^^^^^^^^^^^ ----------- not a free function, impl method or static @@ -552,7 +564,7 @@ LL | #[no_mangle] type T = S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:306:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:307:5 | LL | #[no_mangle] impl S { } | ^^^^^^^^^^^^ ---------- not a free function, impl method or static @@ -560,7 +572,7 @@ LL | #[no_mangle] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:312:9 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:313:9 | LL | #[no_mangle] fn foo(); | ^^^^^^^^^^^^ --------- not a free function, impl method or static @@ -568,7 +580,7 @@ LL | #[no_mangle] fn foo(); = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a free function, impl method or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:317:9 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:318:9 | LL | #[no_mangle] fn bar() {} | ^^^^^^^^^^^^ ----------- not a free function, impl method or static @@ -576,163 +588,163 @@ LL | #[no_mangle] fn bar() {} = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: `#[should_panic]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:327:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:328:17 | LL | mod inner { #![should_panic] } | ^^^^^^^^^^^^^^^^ warning: `#[should_panic]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:332:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:333:5 | LL | #[should_panic] struct S; | ^^^^^^^^^^^^^^^ warning: `#[should_panic]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:335:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:336:5 | LL | #[should_panic] type T = S; | ^^^^^^^^^^^^^^^ warning: `#[should_panic]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:338:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:339:5 | LL | #[should_panic] impl S { } | ^^^^^^^^^^^^^^^ warning: `#[ignore]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:345:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:346:17 | LL | mod inner { #![ignore] } | ^^^^^^^^^^ warning: `#[ignore]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:350:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:351:5 | LL | #[ignore] struct S; | ^^^^^^^^^ warning: `#[ignore]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:353:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:354:5 | LL | #[ignore] type T = S; | ^^^^^^^^^ warning: `#[ignore]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:356:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:357:5 | LL | #[ignore] impl S { } | ^^^^^^^^^ warning: `#[no_implicit_prelude]` only has an effect on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:364:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:365:5 | LL | #[no_implicit_prelude] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: `#[no_implicit_prelude]` only has an effect on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:367:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:368:5 | LL | #[no_implicit_prelude] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: `#[no_implicit_prelude]` only has an effect on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:370:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:371:5 | LL | #[no_implicit_prelude] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: `#[no_implicit_prelude]` only has an effect on modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:373:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:374:5 | LL | #[no_implicit_prelude] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:380:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:381:17 | LL | mod inner { #![reexport_test_harness_main="2900"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:383:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:384:5 | LL | #[reexport_test_harness_main = "2900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:386:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:387:5 | LL | #[reexport_test_harness_main = "2900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:389:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:390:5 | LL | #[reexport_test_harness_main = "2900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:392:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:393:5 | LL | #[reexport_test_harness_main = "2900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: `#[macro_escape]` only has an effect on `extern crate` and modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:404:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:405:5 | LL | #[macro_escape] fn f() { } | ^^^^^^^^^^^^^^^ warning: `#[macro_escape]` only has an effect on `extern crate` and modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:407:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:408:5 | LL | #[macro_escape] struct S; | ^^^^^^^^^^^^^^^ warning: `#[macro_escape]` only has an effect on `extern crate` and modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:410:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:411:5 | LL | #[macro_escape] type T = S; | ^^^^^^^^^^^^^^^ warning: `#[macro_escape]` only has an effect on `extern crate` and modules - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:413:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:414:5 | LL | #[macro_escape] impl S { } | ^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:420:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:421:17 | LL | mod inner { #![no_std] } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:423:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:424:5 | LL | #[no_std] fn f() { } | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:426:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:427:5 | LL | #[no_std] struct S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:429:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:430:5 | LL | #[no_std] type T = S; | ^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:432:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:433:5 | LL | #[no_std] impl S { } | ^^^^^^^^^ warning: attribute should be applied to a function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:459:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:460:17 | LL | mod inner { #![cold] } | ------------^^^^^^^^-- not a function @@ -740,7 +752,7 @@ LL | mod inner { #![cold] } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:466:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:467:5 | LL | #[cold] struct S; | ^^^^^^^ --------- not a function @@ -748,7 +760,7 @@ LL | #[cold] struct S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:471:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:472:5 | LL | #[cold] type T = S; | ^^^^^^^ ----------- not a function @@ -756,7 +768,7 @@ LL | #[cold] type T = S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:476:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:477:5 | LL | #[cold] impl S { } | ^^^^^^^ ---------- not a function @@ -764,7 +776,7 @@ LL | #[cold] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:488:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:489:5 | LL | #[link_name = "1900"] | ^^^^^^^^^^^^^^^^^^^^^ @@ -774,13 +786,13 @@ LL | extern "C" { } | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! help: try `#[link(name = "1900")]` instead - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:488:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:489:5 | LL | #[link_name = "1900"] | ^^^^^^^^^^^^^^^^^^^^^ warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:495:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:496:17 | LL | mod inner { #![link_name="1900"] } | ------------^^^^^^^^^^^^^^^^^^^^-- not a foreign function or static @@ -788,7 +800,7 @@ LL | mod inner { #![link_name="1900"] } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:500:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:501:5 | LL | #[link_name = "1900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^ ---------- not a foreign function or static @@ -796,7 +808,7 @@ LL | #[link_name = "1900"] fn f() { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:505:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:506:5 | LL | #[link_name = "1900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^ --------- not a foreign function or static @@ -804,7 +816,7 @@ LL | #[link_name = "1900"] struct S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:510:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:511:5 | LL | #[link_name = "1900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^ ----------- not a foreign function or static @@ -812,7 +824,7 @@ LL | #[link_name = "1900"] type T = S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a foreign function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:515:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:516:5 | LL | #[link_name = "1900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^ ---------- not a foreign function or static @@ -820,7 +832,7 @@ LL | #[link_name = "1900"] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:527:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:528:17 | LL | mod inner { #![link_section="1800"] } | ------------^^^^^^^^^^^^^^^^^^^^^^^-- not a function or static @@ -828,7 +840,7 @@ LL | mod inner { #![link_section="1800"] } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:534:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:535:5 | LL | #[link_section = "1800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^ --------- not a function or static @@ -836,7 +848,7 @@ LL | #[link_section = "1800"] struct S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:539:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:540:5 | LL | #[link_section = "1800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^ ----------- not a function or static @@ -844,7 +856,7 @@ LL | #[link_section = "1800"] type T = S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to a function or static - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:544:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:545:5 | LL | #[link_section = "1800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^ ---------- not a function or static @@ -852,7 +864,7 @@ LL | #[link_section = "1800"] impl S { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:559:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:560:17 | LL | mod inner { #![link()] } | ------------^^^^^^^^^^-- not an `extern` block @@ -860,7 +872,7 @@ LL | mod inner { #![link()] } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:564:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:565:5 | LL | #[link()] fn f() { } | ^^^^^^^^^ ---------- not an `extern` block @@ -868,7 +880,7 @@ LL | #[link()] fn f() { } = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:569:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:570:5 | LL | #[link()] struct S; | ^^^^^^^^^ --------- not an `extern` block @@ -876,7 +888,7 @@ LL | #[link()] struct S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:574:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:575:5 | LL | #[link()] type T = S; | ^^^^^^^^^ ----------- not an `extern` block @@ -884,260 +896,278 @@ LL | #[link()] type T = S; = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! warning: attribute should be applied to an `extern` block - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:579:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:580:5 | LL | #[link()] impl S { } | ^^^^^^^^^ ---------- not an `extern` block | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +warning: `#[must_use]` has no effect when applied to a module + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:603:17 + | +LL | mod inner { #![must_use] } + | ^^^^^^^^^^^^ + +warning: `#[must_use]` has no effect when applied to a type alias + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:609:5 + | +LL | #[must_use] type T = S; + | ^^^^^^^^^^^ + +warning: `#[must_use]` has no effect when applied to an item + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:611:5 + | +LL | #[must_use] impl S { } + | ^^^^^^^^^^^ + warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:616:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:617:17 | LL | mod inner { #![windows_subsystem="windows"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:619:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:620:5 | LL | #[windows_subsystem = "windows"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:622:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:623:5 | LL | #[windows_subsystem = "windows"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:625:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:626:5 | LL | #[windows_subsystem = "windows"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:628:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:629:5 | LL | #[windows_subsystem = "windows"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:637:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:638:17 | LL | mod inner { #![crate_name="0900"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:640:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:641:5 | LL | #[crate_name = "0900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:643:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:644:5 | LL | #[crate_name = "0900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:646:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:647:5 | LL | #[crate_name = "0900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:649:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:650:5 | LL | #[crate_name = "0900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:656:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:657:17 | LL | mod inner { #![crate_type="0800"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:659:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:660:5 | LL | #[crate_type = "0800"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:662:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:663:5 | LL | #[crate_type = "0800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:665:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:666:5 | LL | #[crate_type = "0800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:668:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:669:5 | LL | #[crate_type = "0800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:675:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:676:17 | LL | mod inner { #![feature(x0600)] } | ^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:678:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:679:5 | LL | #[feature(x0600)] fn f() { } | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:681:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:682:5 | LL | #[feature(x0600)] struct S; | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:684:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:685:5 | LL | #[feature(x0600)] type T = S; | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:687:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:688:5 | LL | #[feature(x0600)] impl S { } | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:695:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:696:17 | LL | mod inner { #![no_main] } | ^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:698:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:699:5 | LL | #[no_main] fn f() { } | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:701:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:702:5 | LL | #[no_main] struct S; | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:704:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:705:5 | LL | #[no_main] type T = S; | ^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:707:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:708:5 | LL | #[no_main] impl S { } | ^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:714:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:715:17 | LL | mod inner { #![no_builtins] } | ^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:717:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:718:5 | LL | #[no_builtins] fn f() { } | ^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:720:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:721:5 | LL | #[no_builtins] struct S; | ^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:723:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:724:5 | LL | #[no_builtins] type T = S; | ^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:726:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:727:5 | LL | #[no_builtins] impl S { } | ^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:733:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:734:17 | LL | mod inner { #![recursion_limit="0200"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:736:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:737:5 | LL | #[recursion_limit="0200"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:739:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:740:5 | LL | #[recursion_limit="0200"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:742:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:743:5 | LL | #[recursion_limit="0200"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:745:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:746:5 | LL | #[recursion_limit="0200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:752:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:753:17 | LL | mod inner { #![type_length_limit="0100"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:755:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:756:5 | LL | #[type_length_limit="0100"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:758:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:759:5 | LL | #[type_length_limit="0100"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:761:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:762:5 | LL | #[type_length_limit="0100"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]` - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:764:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:765:5 | LL | #[type_length_limit="0100"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: the feature `rust1` has been stable since 1.0.0 and no longer requires an attribute to enable - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:90:12 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:91:12 | LL | #![feature(rust1)] | ^^^^^ | = note: `#[warn(stable_features)]` on by default -warning: 167 warnings emitted +warning: 172 warnings emitted diff --git a/src/test/ui/lint/unused/unused_attributes-must_use.rs b/src/test/ui/lint/unused/unused_attributes-must_use.rs new file mode 100644 index 0000000000000..1c4abb9491e00 --- /dev/null +++ b/src/test/ui/lint/unused/unused_attributes-must_use.rs @@ -0,0 +1,125 @@ +#![allow(dead_code, path_statements)] +#![deny(unused_attributes, unused_must_use)] +#![feature(asm_experimental_arch, stmt_expr_attributes, trait_alias)] + +#[must_use] //~ ERROR `#[must_use]` has no effect +extern crate std as std2; + +#[must_use] //~ ERROR `#[must_use]` has no effect +mod test_mod {} + +#[must_use] //~ ERROR `#[must_use]` has no effect +use std::arch::global_asm; + +#[must_use] //~ ERROR `#[must_use]` has no effect +const CONST: usize = 4; +#[must_use] //~ ERROR `#[must_use]` has no effect +#[no_mangle] +static STATIC: usize = 4; + +#[must_use] +struct X; + +#[must_use] +enum Y { + Z, +} + +#[must_use] +union U { + unit: (), +} + +#[must_use] //~ ERROR `#[must_use]` has no effect +impl U { + #[must_use] + fn method() -> i32 { + 4 + } +} + +#[must_use] +#[no_mangle] +fn foo() -> i64 { + 4 +} + +#[must_use] //~ ERROR `#[must_use]` has no effect +extern "Rust" { + #[link_name = "STATIC"] + #[must_use] //~ ERROR `#[must_use]` has no effect + static FOREIGN_STATIC: usize; + + #[link_name = "foo"] + #[must_use] + fn foreign_foo() -> i64; +} + +#[must_use] //~ ERROR unused attribute +global_asm!(""); + +#[must_use] //~ ERROR `#[must_use]` has no effect +type UseMe = (); + +fn qux<#[must_use] T>(_: T) {} //~ ERROR `#[must_use]` has no effect + +#[must_use] +trait Use { + #[must_use] //~ ERROR `#[must_use]` has no effect + const ASSOC_CONST: usize = 4; + #[must_use] //~ ERROR `#[must_use]` has no effect + type AssocTy; + + #[must_use] + fn get_four(&self) -> usize { + 4 + } +} + +#[must_use] //~ ERROR `#[must_use]` has no effect +impl Use for () { + type AssocTy = (); +} + +#[must_use] //~ ERROR `#[must_use]` has no effect +trait Alias = Use; + +#[must_use] //~ ERROR `#[must_use]` has no effect +macro_rules! cool_macro { + () => { + 4 + }; +} + +fn main() { + #[must_use] //~ ERROR `#[must_use]` has no effect + let x = || {}; + x(); + + let x = #[must_use] //~ ERROR `#[must_use]` has no effect + || {}; + x(); + + X; //~ ERROR that must be used + Y::Z; //~ ERROR that must be used + U { unit: () }; //~ ERROR that must be used + U::method(); //~ ERROR that must be used + foo(); //~ ERROR that must be used + + unsafe { + foreign_foo(); //~ ERROR that must be used + }; + + CONST; + STATIC; + unsafe { FOREIGN_STATIC }; + cool_macro!(); + qux(4); + ().get_four(); //~ ERROR that must be used + + match Some(4) { + #[must_use] //~ ERROR `#[must_use]` has no effect + Some(res) => res, + None => 0, + }; +} diff --git a/src/test/ui/lint/unused/unused_attributes-must_use.stderr b/src/test/ui/lint/unused/unused_attributes-must_use.stderr new file mode 100644 index 0000000000000..27269580e52e2 --- /dev/null +++ b/src/test/ui/lint/unused/unused_attributes-must_use.stderr @@ -0,0 +1,175 @@ +error: unused attribute `must_use` + --> $DIR/unused_attributes-must_use.rs:58:1 + | +LL | #[must_use] + | ^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused_attributes-must_use.rs:2:9 + | +LL | #![deny(unused_attributes, unused_must_use)] + | ^^^^^^^^^^^^^^^^^ +note: the built-in attribute `must_use` will be ignored, since it's applied to the macro invocation `global_asm` + --> $DIR/unused_attributes-must_use.rs:59:1 + | +LL | global_asm!(""); + | ^^^^^^^^^^ + +error: `#[must_use]` has no effect when applied to an extern crate + --> $DIR/unused_attributes-must_use.rs:5:1 + | +LL | #[must_use] + | ^^^^^^^^^^^ + +error: `#[must_use]` has no effect when applied to a module + --> $DIR/unused_attributes-must_use.rs:8:1 + | +LL | #[must_use] + | ^^^^^^^^^^^ + +error: `#[must_use]` has no effect when applied to a use + --> $DIR/unused_attributes-must_use.rs:11:1 + | +LL | #[must_use] + | ^^^^^^^^^^^ + +error: `#[must_use]` has no effect when applied to a constant item + --> $DIR/unused_attributes-must_use.rs:14:1 + | +LL | #[must_use] + | ^^^^^^^^^^^ + +error: `#[must_use]` has no effect when applied to a static item + --> $DIR/unused_attributes-must_use.rs:16:1 + | +LL | #[must_use] + | ^^^^^^^^^^^ + +error: `#[must_use]` has no effect when applied to an item + --> $DIR/unused_attributes-must_use.rs:33:1 + | +LL | #[must_use] + | ^^^^^^^^^^^ + +error: `#[must_use]` has no effect when applied to a foreign module + --> $DIR/unused_attributes-must_use.rs:47:1 + | +LL | #[must_use] + | ^^^^^^^^^^^ + +error: `#[must_use]` has no effect when applied to a type alias + --> $DIR/unused_attributes-must_use.rs:61:1 + | +LL | #[must_use] + | ^^^^^^^^^^^ + +error: `#[must_use]` has no effect when applied to a type parameter + --> $DIR/unused_attributes-must_use.rs:64:8 + | +LL | fn qux<#[must_use] T>(_: T) {} + | ^^^^^^^^^^^ + +error: `#[must_use]` has no effect when applied to an item + --> $DIR/unused_attributes-must_use.rs:79:1 + | +LL | #[must_use] + | ^^^^^^^^^^^ + +error: `#[must_use]` has no effect when applied to a trait alias + --> $DIR/unused_attributes-must_use.rs:84:1 + | +LL | #[must_use] + | ^^^^^^^^^^^ + +error: `#[must_use]` has no effect when applied to a macro def + --> $DIR/unused_attributes-must_use.rs:87:1 + | +LL | #[must_use] + | ^^^^^^^^^^^ + +error: `#[must_use]` has no effect when applied to a statement + --> $DIR/unused_attributes-must_use.rs:95:5 + | +LL | #[must_use] + | ^^^^^^^^^^^ + +error: `#[must_use]` has no effect when applied to a closure + --> $DIR/unused_attributes-must_use.rs:99:13 + | +LL | let x = #[must_use] + | ^^^^^^^^^^^ + +error: `#[must_use]` has no effect when applied to an match arm + --> $DIR/unused_attributes-must_use.rs:121:9 + | +LL | #[must_use] + | ^^^^^^^^^^^ + +error: `#[must_use]` has no effect when applied to an associated const + --> $DIR/unused_attributes-must_use.rs:68:5 + | +LL | #[must_use] + | ^^^^^^^^^^^ + +error: `#[must_use]` has no effect when applied to an associated type + --> $DIR/unused_attributes-must_use.rs:70:5 + | +LL | #[must_use] + | ^^^^^^^^^^^ + +error: `#[must_use]` has no effect when applied to a foreign static item + --> $DIR/unused_attributes-must_use.rs:50:5 + | +LL | #[must_use] + | ^^^^^^^^^^^ + +error: unused `X` that must be used + --> $DIR/unused_attributes-must_use.rs:103:5 + | +LL | X; + | ^^ + | +note: the lint level is defined here + --> $DIR/unused_attributes-must_use.rs:2:28 + | +LL | #![deny(unused_attributes, unused_must_use)] + | ^^^^^^^^^^^^^^^ + +error: unused `Y` that must be used + --> $DIR/unused_attributes-must_use.rs:104:5 + | +LL | Y::Z; + | ^^^^^ + +error: unused `U` that must be used + --> $DIR/unused_attributes-must_use.rs:105:5 + | +LL | U { unit: () }; + | ^^^^^^^^^^^^^^^ + +error: unused return value of `U::method` that must be used + --> $DIR/unused_attributes-must_use.rs:106:5 + | +LL | U::method(); + | ^^^^^^^^^^^^ + +error: unused return value of `foo` that must be used + --> $DIR/unused_attributes-must_use.rs:107:5 + | +LL | foo(); + | ^^^^^^ + +error: unused return value of `foreign_foo` that must be used + --> $DIR/unused_attributes-must_use.rs:110:9 + | +LL | foreign_foo(); + | ^^^^^^^^^^^^^^ + +error: unused return value of `Use::get_four` that must be used + --> $DIR/unused_attributes-must_use.rs:118:5 + | +LL | ().get_four(); + | ^^^^^^^^^^^^^^ + +error: aborting due to 26 previous errors + From 5ce3f5664130eaf24d187d04dcd51c4577336ab5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 5 Dec 2021 04:51:36 +0000 Subject: [PATCH 22/23] Make deref suggestion better --- compiler/rustc_typeck/src/check/demand.rs | 150 +++++++++++------- .../src/check/fn_ctxt/suggestions.rs | 4 +- src/test/ui/parser/expr-as-stmt-2.stderr | 5 + src/test/ui/parser/expr-as-stmt.stderr | 5 + .../disallowed-positions.stderr | 12 ++ src/test/ui/typeck/deref-multi.rs | 26 +++ src/test/ui/typeck/deref-multi.stderr | 72 +++++++++ 7 files changed, 214 insertions(+), 60 deletions(-) create mode 100644 src/test/ui/typeck/deref-multi.rs create mode 100644 src/test/ui/typeck/deref-multi.stderr diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index 80096b90f9530..47fc4b6683368 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -566,7 +566,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, - ) -> Option<(Span, &'static str, String, Applicability, bool /* verbose */)> { + ) -> Option<(Span, String, String, Applicability, bool /* verbose */)> { let sess = self.sess(); let sp = expr.span; @@ -593,7 +593,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let pos = sp.lo() + BytePos(1); return Some(( sp.with_hi(pos), - "consider removing the leading `b`", + "consider removing the leading `b`".to_string(), String::new(), Applicability::MachineApplicable, true, @@ -608,7 +608,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if replace_prefix(&src, "\"", "b\"").is_some() { return Some(( sp.shrink_to_lo(), - "consider adding a leading `b`", + "consider adding a leading `b`".to_string(), "b".to_string(), Applicability::MachineApplicable, true, @@ -669,7 +669,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(sugg) = self.can_use_as_ref(expr) { return Some(( sugg.0, - sugg.1, + sugg.1.to_string(), sugg.2, Applicability::MachineApplicable, false, @@ -697,7 +697,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Some(( left_expr.span.shrink_to_lo(), "consider dereferencing here to assign to the mutable \ - borrowed piece of memory", + borrowed piece of memory" + .to_string(), "*".to_string(), Applicability::MachineApplicable, true, @@ -709,14 +710,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Some(match mutability { hir::Mutability::Mut => ( sp, - "consider mutably borrowing here", + "consider mutably borrowing here".to_string(), format!("{}&mut {}", prefix, sugg_expr), Applicability::MachineApplicable, false, ), hir::Mutability::Not => ( sp, - "consider borrowing here", + "consider borrowing here".to_string(), format!("{}&{}", prefix, sugg_expr), Applicability::MachineApplicable, false, @@ -745,7 +746,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if sm.span_to_snippet(call_span).is_ok() { return Some(( sp.with_hi(call_span.lo()), - "consider removing the borrow", + "consider removing the borrow".to_string(), String::new(), Applicability::MachineApplicable, true, @@ -758,7 +759,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if sm.span_to_snippet(expr.span).is_ok() { return Some(( sp.with_hi(expr.span.lo()), - "consider removing the borrow", + "consider removing the borrow".to_string(), String::new(), Applicability::MachineApplicable, true, @@ -824,7 +825,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } { return Some(( span, - "consider dereferencing", + "consider dereferencing".to_string(), src, applicability, true, @@ -835,60 +836,93 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } _ if sp == expr.span => { - if let Some(steps) = self.deref_steps(checked_ty, expected) { - let expr = expr.peel_blocks(); + if let Some(mut steps) = self.deref_steps(checked_ty, expected) { + let mut expr = expr.peel_blocks(); + let mut prefix_span = expr.span.shrink_to_lo(); + let mut remove = String::new(); - if steps == 1 { + // Try peeling off any existing `&` and `&mut` to reach our target type + while steps > 0 { if let hir::ExprKind::AddrOf(_, mutbl, inner) = expr.kind { // If the expression has `&`, removing it would fix the error - let prefix_span = expr.span.with_hi(inner.span.lo()); - let message = match mutbl { - hir::Mutability::Not => "consider removing the `&`", - hir::Mutability::Mut => "consider removing the `&mut`", + prefix_span = prefix_span.with_hi(inner.span.lo()); + expr = inner; + remove += match mutbl { + hir::Mutability::Not => "&", + hir::Mutability::Mut => "&mut ", }; - let suggestion = String::new(); - return Some(( - prefix_span, - message, - suggestion, - Applicability::MachineApplicable, - false, - )); + steps -= 1; + } else { + break; } - - // For this suggestion to make sense, the type would need to be `Copy`, - // or we have to be moving out of a `Box` - if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp) - || checked_ty.is_box() - { - let message = if checked_ty.is_box() { - "consider unboxing the value" - } else if checked_ty.is_region_ptr() { - "consider dereferencing the borrow" - } else { - "consider dereferencing the type" - }; - let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) { - Some(ident) => format!("{}: ", ident), - None => String::new(), - }; - let (span, suggestion) = if self.is_else_if_block(expr) { - // Don't suggest nonsense like `else *if` - return None; - } else if let Some(expr) = self.maybe_get_block_expr(expr) { - // prefix should be empty here.. - (expr.span.shrink_to_lo(), "*".to_string()) + } + // If we've reached our target type with just removing `&`, then just print now. + if steps == 0 { + return Some(( + prefix_span, + format!("consider removing the `{}`", remove.trim()), + String::new(), + // Do not remove `&&` to get to bool, because it might be something like + // { a } && b, which we have a separate fixup suggestion that is more + // likely correct... + if remove.trim() == "&&" && expected == self.tcx.types.bool { + Applicability::MaybeIncorrect } else { - (expr.span.shrink_to_lo(), format!("{}*", prefix)) - }; - return Some(( - span, - message, - suggestion, - Applicability::MachineApplicable, - true, - )); - } + Applicability::MachineApplicable + }, + true, + )); + } + + // For this suggestion to make sense, the type would need to be `Copy`, + // or we have to be moving out of a `Box` + if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp) + // FIXME(compiler-errors): We can actually do this if the checked_ty is + // `steps` layers of boxes, not just one, but this is easier and most likely. + || (checked_ty.is_box() && steps == 1) + { + let deref_kind = if checked_ty.is_box() { + "unboxing the value" + } else if checked_ty.is_region_ptr() { + "dereferencing the borrow" + } else { + "dereferencing the type" + }; + + // Suggest removing `&` if we have removed any, otherwise suggest just + // dereferencing the remaining number of steps. + let message = if remove.is_empty() { + format!("consider {}", deref_kind) + } else { + format!( + "consider removing the `{}` and {} instead", + remove.trim(), + deref_kind + ) + }; + + let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) { + Some(ident) => format!("{}: ", ident), + None => String::new(), + }; + + let (span, suggestion) = if self.is_else_if_block(expr) { + // Don't suggest nonsense like `else *if` + return None; + } else if let Some(expr) = self.maybe_get_block_expr(expr) { + // prefix should be empty here.. + (expr.span.shrink_to_lo(), "*".to_string()) + } else { + (prefix_span, format!("{}{}", prefix, "*".repeat(steps))) + }; + + return Some(( + span, + message, + suggestion, + Applicability::MachineApplicable, + true, + )); } } } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 8cad4fc707ea3..523602d5b1888 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -218,9 +218,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_ref(expr, found, expected) { if verbose { - err.span_suggestion_verbose(sp, msg, suggestion, applicability); + err.span_suggestion_verbose(sp, &msg, suggestion, applicability); } else { - err.span_suggestion(sp, msg, suggestion, applicability); + err.span_suggestion(sp, &msg, suggestion, applicability); } } else if let (ty::FnDef(def_id, ..), true) = (&found.kind(), self.suggest_fn_call(err, expr, expected, found)) diff --git a/src/test/ui/parser/expr-as-stmt-2.stderr b/src/test/ui/parser/expr-as-stmt-2.stderr index 2b6314c38ceb6..b7516babc1330 100644 --- a/src/test/ui/parser/expr-as-stmt-2.stderr +++ b/src/test/ui/parser/expr-as-stmt-2.stderr @@ -36,6 +36,11 @@ LL | / && LL | | if let Some(y) = a { true } else { false } | |______________________________________________^ expected `bool`, found `&&bool` | +help: consider removing the `&&` + | +LL - && +LL + if let Some(y) = a { true } else { false } + | help: parentheses are required to parse this as an expression | LL | (if let Some(x) = a { true } else { false }) diff --git a/src/test/ui/parser/expr-as-stmt.stderr b/src/test/ui/parser/expr-as-stmt.stderr index df0e4dcb16e2c..e63da52c8fe1d 100644 --- a/src/test/ui/parser/expr-as-stmt.stderr +++ b/src/test/ui/parser/expr-as-stmt.stderr @@ -170,6 +170,11 @@ LL | fn revenge_from_mars() -> bool { LL | { true } && { true } | ^^^^^^^^^^^ expected `bool`, found `&&bool` | +help: consider removing the `&&` + | +LL - { true } && { true } +LL + { true } { true } + | help: parentheses are required to parse this as an expression | LL | ({ true }) && { true } diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr index 4c830554d435c..897de54a7bf8c 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr @@ -676,6 +676,12 @@ error[E0308]: mismatched types | LL | if let Range { start: true, end } = t..&&false {} | ^^^^^^^ expected `bool`, found `&&bool` + | +help: consider removing the `&&` + | +LL - if let Range { start: true, end } = t..&&false {} +LL + if let Range { start: true, end } = t..false {} + | error[E0308]: mismatched types --> $DIR/disallowed-positions.rs:83:8 @@ -866,6 +872,12 @@ error[E0308]: mismatched types | LL | while let Range { start: true, end } = t..&&false {} | ^^^^^^^ expected `bool`, found `&&bool` + | +help: consider removing the `&&` + | +LL - while let Range { start: true, end } = t..&&false {} +LL + while let Range { start: true, end } = t..false {} + | error[E0308]: mismatched types --> $DIR/disallowed-positions.rs:147:11 diff --git a/src/test/ui/typeck/deref-multi.rs b/src/test/ui/typeck/deref-multi.rs new file mode 100644 index 0000000000000..3dc4771fefb07 --- /dev/null +++ b/src/test/ui/typeck/deref-multi.rs @@ -0,0 +1,26 @@ +fn a(x: &&i32) -> i32 { + x + //~^ ERROR mismatched types +} + +fn a2(x: &&&&&i32) -> i32 { + x + //~^ ERROR mismatched types +} + +fn b(x: &i32) -> i32 { + &x + //~^ ERROR mismatched types +} + +fn c(x: Box) -> i32 { + &x + //~^ ERROR mismatched types +} + +fn d(x: std::sync::Mutex<&i32>) -> i32 { + x.lock().unwrap() + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/src/test/ui/typeck/deref-multi.stderr b/src/test/ui/typeck/deref-multi.stderr new file mode 100644 index 0000000000000..bd6575c73d244 --- /dev/null +++ b/src/test/ui/typeck/deref-multi.stderr @@ -0,0 +1,72 @@ +error[E0308]: mismatched types + --> $DIR/deref-multi.rs:2:5 + | +LL | fn a(x: &&i32) -> i32 { + | --- expected `i32` because of return type +LL | x + | ^ expected `i32`, found `&&i32` + | +help: consider dereferencing the borrow + | +LL | **x + | ++ + +error[E0308]: mismatched types + --> $DIR/deref-multi.rs:7:5 + | +LL | fn a2(x: &&&&&i32) -> i32 { + | --- expected `i32` because of return type +LL | x + | ^ expected `i32`, found `&&&&&i32` + | +help: consider dereferencing the borrow + | +LL | *****x + | +++++ + +error[E0308]: mismatched types + --> $DIR/deref-multi.rs:12:5 + | +LL | fn b(x: &i32) -> i32 { + | --- expected `i32` because of return type +LL | &x + | ^^ expected `i32`, found `&&i32` + | +help: consider removing the `&` and dereferencing the borrow instead + | +LL | *x + | ~ + +error[E0308]: mismatched types + --> $DIR/deref-multi.rs:17:5 + | +LL | fn c(x: Box) -> i32 { + | --- expected `i32` because of return type +LL | &x + | ^^ expected `i32`, found `&Box` + | + = note: expected type `i32` + found reference `&Box` +help: consider removing the `&` and dereferencing the borrow instead + | +LL | *x + | ~ + +error[E0308]: mismatched types + --> $DIR/deref-multi.rs:22:5 + | +LL | fn d(x: std::sync::Mutex<&i32>) -> i32 { + | --- expected `i32` because of return type +LL | x.lock().unwrap() + | ^^^^^^^^^^^^^^^^^ expected `i32`, found struct `MutexGuard` + | + = note: expected type `i32` + found struct `MutexGuard<'_, &i32>` +help: consider dereferencing the type + | +LL | **x.lock().unwrap() + | ++ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0308`. From d956c8b816c1bcc38053d6dfd79e34cd12b0f4b1 Mon Sep 17 00:00:00 2001 From: Caio Date: Mon, 28 Feb 2022 15:52:36 -0300 Subject: [PATCH 23/23] 5 - Make more use of let_chains --- compiler/rustc_passes/src/check_attr.rs | 30 +++++++------- compiler/rustc_passes/src/dead.rs | 55 ++++++++++++------------- compiler/rustc_passes/src/lib.rs | 3 +- compiler/rustc_passes/src/liveness.rs | 11 +++-- compiler/rustc_passes/src/reachable.rs | 32 +++++++------- 5 files changed, 63 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 6e4907fe518ae..6c296b5909277 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -1113,22 +1113,20 @@ impl CheckAttrVisitor<'_> { /// Warns against some misuses of `#[must_use]` fn check_must_use(&self, hir_id: HirId, attr: &Attribute, span: Span, _target: Target) -> bool { let node = self.tcx.hir().get(hir_id); - if let Some(fn_node) = node.fn_kind() { - if let rustc_hir::IsAsync::Async = fn_node.asyncness() { - self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { - lint.build( - "`must_use` attribute on `async` functions \ - applies to the anonymous `Future` returned by the \ - function, not the value within", - ) - .span_label( - span, - "this attribute does nothing, the `Future`s \ - returned by async functions are already `must_use`", - ) - .emit(); - }); - } + if let Some(kind) = node.fn_kind() && let rustc_hir::IsAsync::Async = kind.asyncness() { + self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| { + lint.build( + "`must_use` attribute on `async` functions \ + applies to the anonymous `Future` returned by the \ + function, not the value within", + ) + .span_label( + span, + "this attribute does nothing, the `Future`s \ + returned by async functions are already `must_use`", + ) + .emit(); + }); } // For now, its always valid diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index e52fbc8ab92d6..e438b521a952b 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -683,34 +683,33 @@ impl<'tcx> DeadVisitor<'tcx> { let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id()); let mut err = lint.build(&format!("{} is never {}: `{}`", descr, participle, name)); let hir = self.tcx.hir(); - if let Some(encl_scope) = hir.get_enclosing_scope(id) { - if let Some(encl_def_id) = hir.opt_local_def_id(encl_scope) { - if let Some(ign_traits) = self.ignored_derived_traits.get(&encl_def_id) { - let traits_str = ign_traits - .iter() - .map(|(trait_id, _)| format!("`{}`", self.tcx.item_name(*trait_id))) - .collect::>() - .join(" and "); - let plural_s = pluralize!(ign_traits.len()); - let article = if ign_traits.len() > 1 { "" } else { "a " }; - let is_are = if ign_traits.len() > 1 { "these are" } else { "this is" }; - let msg = format!( - "`{}` has {}derived impl{} for the trait{} {}, but {} \ - intentionally ignored during dead code analysis", - self.tcx.item_name(encl_def_id.to_def_id()), - article, - plural_s, - plural_s, - traits_str, - is_are - ); - let multispan = ign_traits - .iter() - .map(|(_, impl_id)| self.tcx.def_span(*impl_id)) - .collect::>(); - err.span_note(multispan, &msg); - } - } + if let Some(encl_scope) = hir.get_enclosing_scope(id) + && let Some(encl_def_id) = hir.opt_local_def_id(encl_scope) + && let Some(ign_traits) = self.ignored_derived_traits.get(&encl_def_id) + { + let traits_str = ign_traits + .iter() + .map(|(trait_id, _)| format!("`{}`", self.tcx.item_name(*trait_id))) + .collect::>() + .join(" and "); + let plural_s = pluralize!(ign_traits.len()); + let article = if ign_traits.len() > 1 { "" } else { "a " }; + let is_are = if ign_traits.len() > 1 { "these are" } else { "this is" }; + let msg = format!( + "`{}` has {}derived impl{} for the trait{} {}, but {} \ + intentionally ignored during dead code analysis", + self.tcx.item_name(encl_def_id.to_def_id()), + article, + plural_s, + plural_s, + traits_str, + is_are + ); + let multispan = ign_traits + .iter() + .map(|(_, impl_id)| self.tcx.def_span(*impl_id)) + .collect::>(); + err.span_note(multispan, &msg); } err.emit(); }); diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index 71381f1d86931..71d49d8b7ea9c 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -4,16 +4,17 @@ //! //! This API is completely unstable and subject to change. +#![allow(rustc::potential_query_instability)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![feature(crate_visibility_modifier)] #![feature(iter_intersperse)] #![feature(let_else)] +#![feature(let_chains)] #![feature(map_try_insert)] #![feature(min_specialization)] #![feature(nll)] #![feature(try_blocks)] #![recursion_limit = "256"] -#![allow(rustc::potential_query_instability)] #[macro_use] extern crate rustc_middle; diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index a959089ebb318..ea99a90e937c3 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -332,12 +332,11 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { let def_id = local_def_id.to_def_id(); // Don't run unused pass for #[derive()] - if let Some(parent) = self.tcx.parent(def_id) { - if let DefKind::Impl = self.tcx.def_kind(parent.expect_local()) { - if self.tcx.has_attr(parent, sym::automatically_derived) { - return; - } - } + if let Some(parent) = self.tcx.parent(def_id) + && let DefKind::Impl = self.tcx.def_kind(parent.expect_local()) + && self.tcx.has_attr(parent, sym::automatically_derived) + { + return; } // Don't run unused pass for #[naked] diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index adbfb4fcf01ca..b520e5d04eab9 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -94,24 +94,22 @@ impl<'tcx> Visitor<'tcx> for ReachableContext<'tcx> { _ => None, }; - if let Some(res) = res { - if let Some(def_id) = res.opt_def_id().and_then(|def_id| def_id.as_local()) { - if self.def_id_represents_local_inlined_item(def_id.to_def_id()) { - self.worklist.push(def_id); - } else { - match res { - // If this path leads to a constant, then we need to - // recurse into the constant to continue finding - // items that are reachable. - Res::Def(DefKind::Const | DefKind::AssocConst, _) => { - self.worklist.push(def_id); - } + if let Some(res) = res && let Some(def_id) = res.opt_def_id().and_then(|el| el.as_local()) { + if self.def_id_represents_local_inlined_item(def_id.to_def_id()) { + self.worklist.push(def_id); + } else { + match res { + // If this path leads to a constant, then we need to + // recurse into the constant to continue finding + // items that are reachable. + Res::Def(DefKind::Const | DefKind::AssocConst, _) => { + self.worklist.push(def_id); + } - // If this wasn't a static, then the destination is - // surely reachable. - _ => { - self.reachable_symbols.insert(def_id); - } + // If this wasn't a static, then the destination is + // surely reachable. + _ => { + self.reachable_symbols.insert(def_id); } } }