From bae0da83612257185691faf94fc39457b4ed14e6 Mon Sep 17 00:00:00 2001 From: oxalica Date: Sun, 12 Dec 2021 17:55:41 +0800 Subject: [PATCH 01/12] Implement data and vtable getters for `RawWaker` --- library/core/src/task/wake.rs | 24 ++++++++++++++++++++++++ library/core/tests/lib.rs | 2 ++ library/core/tests/waker.rs | 22 ++++++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 library/core/tests/waker.rs diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 6cba781c2ed8f..27af227a1f27f 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -43,6 +43,22 @@ impl RawWaker { pub const fn new(data: *const (), vtable: &'static RawWakerVTable) -> RawWaker { RawWaker { data, vtable } } + + /// Get the `data` pointer used to create this `RawWaker`. + #[inline] + #[must_use] + #[unstable(feature = "waker_getters", issue = "87021")] + pub fn data(&self) -> *const () { + self.data + } + + /// Get the `vtable` pointer used to create this `RawWaker`. + #[inline] + #[must_use] + #[unstable(feature = "waker_getters", issue = "87021")] + pub fn vtable(&self) -> &'static RawWakerVTable { + self.vtable + } } /// A virtual function pointer table (vtable) that specifies the behavior @@ -260,6 +276,14 @@ impl Waker { pub unsafe fn from_raw(waker: RawWaker) -> Waker { Waker { waker } } + + /// Get a reference to the underlying [`RawWaker`]. + #[inline] + #[must_use] + #[unstable(feature = "waker_getters", issue = "87021")] + pub fn as_raw(&self) -> &RawWaker { + &self.waker + } } #[stable(feature = "futures_api", since = "1.36.0")] diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 012e6e5b57ad0..40d4fa60d0bc7 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -81,6 +81,7 @@ #![feature(unzip_option)] #![feature(const_array_from_ref)] #![feature(const_slice_from_ref)] +#![feature(waker_getters)] #![deny(unsafe_op_in_unsafe_fn)] extern crate test; @@ -121,3 +122,4 @@ mod task; mod time; mod tuple; mod unicode; +mod waker; diff --git a/library/core/tests/waker.rs b/library/core/tests/waker.rs new file mode 100644 index 0000000000000..6602ab36ba714 --- /dev/null +++ b/library/core/tests/waker.rs @@ -0,0 +1,22 @@ +use std::ptr; +use std::task::{RawWaker, RawWakerVTable, Waker}; + +#[test] +fn test_waker_getters() { + let raw_waker = RawWaker::new(42usize as *mut (), &WAKER_VTABLE); + assert_eq!(raw_waker.data() as usize, 42); + assert!(ptr::eq(raw_waker.vtable(), &WAKER_VTABLE)); + + let waker = unsafe { Waker::from_raw(raw_waker) }; + let waker2 = waker.clone(); + let raw_waker2 = waker2.as_raw(); + assert_eq!(raw_waker2.data() as usize, 43); + assert!(ptr::eq(raw_waker2.vtable(), &WAKER_VTABLE)); +} + +static WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new( + |data| RawWaker::new((data as usize + 1) as *mut (), &WAKER_VTABLE), + |_| {}, + |_| {}, + |_| {}, +); From d9b98f9c2326e9916c678f252233bab5a14d0e47 Mon Sep 17 00:00:00 2001 From: lzh Date: Fri, 17 Dec 2021 11:48:44 +0800 Subject: [PATCH 02/12] Eliminate duplicate codes of is_single_fp_element --- compiler/rustc_target/src/abi/call/s390x.rs | 22 ++------------------- compiler/rustc_target/src/abi/call/x86.rs | 22 ++------------------- compiler/rustc_target/src/abi/mod.rs | 18 +++++++++++++++++ 3 files changed, 22 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_target/src/abi/call/s390x.rs b/compiler/rustc_target/src/abi/call/s390x.rs index 38aaee64a4d6b..13706e8c21725 100644 --- a/compiler/rustc_target/src/abi/call/s390x.rs +++ b/compiler/rustc_target/src/abi/call/s390x.rs @@ -2,7 +2,7 @@ // for a pre-z13 machine or using -mno-vx. use crate::abi::call::{ArgAbi, FnAbi, Reg}; -use crate::abi::{self, HasDataLayout, TyAbiInterface, TyAndLayout}; +use crate::abi::{HasDataLayout, TyAbiInterface}; fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { if !ret.layout.is_aggregate() && ret.layout.size.bits() <= 64 { @@ -12,24 +12,6 @@ fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { } } -fn is_single_fp_element<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool -where - Ty: TyAbiInterface<'a, C>, - C: HasDataLayout, -{ - match layout.abi { - abi::Abi::Scalar(scalar) => scalar.value.is_float(), - abi::Abi::Aggregate { .. } => { - if layout.fields.count() == 1 && layout.fields.offset(0).bytes() == 0 { - is_single_fp_element(cx, layout.field(cx, 0)) - } else { - false - } - } - _ => false, - } -} - fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) where Ty: TyAbiInterface<'a, C> + Copy, @@ -40,7 +22,7 @@ where return; } - if is_single_fp_element(cx, arg.layout) { + if arg.layout.is_single_fp_element(cx) { match arg.layout.size.bytes() { 4 => arg.cast_to(Reg::f32()), 8 => arg.cast_to(Reg::f64()), diff --git a/compiler/rustc_target/src/abi/call/x86.rs b/compiler/rustc_target/src/abi/call/x86.rs index 28064d85bf171..d169087dfbdab 100644 --- a/compiler/rustc_target/src/abi/call/x86.rs +++ b/compiler/rustc_target/src/abi/call/x86.rs @@ -1,5 +1,5 @@ use crate::abi::call::{ArgAttribute, FnAbi, PassMode, Reg, RegKind}; -use crate::abi::{self, HasDataLayout, TyAbiInterface, TyAndLayout}; +use crate::abi::{HasDataLayout, TyAbiInterface}; use crate::spec::HasTargetSpec; #[derive(PartialEq)] @@ -8,24 +8,6 @@ pub enum Flavor { Fastcall, } -fn is_single_fp_element<'a, Ty, C>(cx: &C, layout: TyAndLayout<'a, Ty>) -> bool -where - Ty: TyAbiInterface<'a, C> + Copy, - C: HasDataLayout, -{ - match layout.abi { - abi::Abi::Scalar(scalar) => scalar.value.is_float(), - abi::Abi::Aggregate { .. } => { - if layout.fields.count() == 1 && layout.fields.offset(0).bytes() == 0 { - is_single_fp_element(cx, layout.field(cx, 0)) - } else { - false - } - } - _ => false, - } -} - pub fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>, flavor: Flavor) where Ty: TyAbiInterface<'a, C> + Copy, @@ -44,7 +26,7 @@ where if t.abi_return_struct_as_int { // According to Clang, everyone but MSVC returns single-element // float aggregates directly in a floating-point register. - if !t.is_like_msvc && is_single_fp_element(cx, fn_abi.ret.layout) { + if !t.is_like_msvc && fn_abi.ret.layout.is_single_fp_element(cx) { match fn_abi.ret.layout.size.bytes() { 4 => fn_abi.ret.cast_to(Reg::f32()), 8 => fn_abi.ret.cast_to(Reg::f64()), diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs index a57ad8f2bbd1b..7f1fd28b30df8 100644 --- a/compiler/rustc_target/src/abi/mod.rs +++ b/compiler/rustc_target/src/abi/mod.rs @@ -1276,6 +1276,24 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { { Ty::ty_and_layout_pointee_info_at(self, cx, offset) } + + pub fn is_single_fp_element(self, cx: &C) -> bool + where + Ty: TyAbiInterface<'a, C>, + C: HasDataLayout, + { + match self.abi { + Abi::Scalar(scalar) => scalar.value.is_float(), + Abi::Aggregate { .. } => { + if self.fields.count() == 1 && self.fields.offset(0).bytes() == 0 { + self.field(cx, 0).is_single_fp_element(cx) + } else { + false + } + } + _ => false, + } + } } impl<'a, Ty> TyAndLayout<'a, Ty> { From f8ee57be2c585c3fd8931eeb88994fbfcaa0f083 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Wed, 19 Jan 2022 13:43:27 -0600 Subject: [PATCH 03/12] `impl Display for io::ErrorKind` This avoids having to convert from `ErrorKind` to `Error` just to print the error message. --- library/std/src/io/error.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 210a9ec718315..074d693b83155 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -361,13 +361,29 @@ impl ErrorKind { } } +#[stable(feature = "io_errorkind_display", since = "1.60.0")] +impl fmt::Display for ErrorKind { + /// Shows a human-readable description of the `ErrorKind`. + /// + /// This is similar to `impl Display for Error`, but doesn't require first converting to Error. + /// + /// # Examples + /// ``` + /// use std::io::ErrorKind; + /// assert_eq!("entity not found", ErrorKind::NotFound.to_string()); + /// ``` + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str(self.as_str()) + } +} + /// Intended for use for errors not exposed to the user, where allocating onto /// the heap (for normal construction via Error::new) is too costly. #[stable(feature = "io_error_from_errorkind", since = "1.14.0")] impl From for Error { /// Converts an [`ErrorKind`] into an [`Error`]. /// - /// This conversion allocates a new error with a simple representation of error kind. + /// This conversion creates a new error with a simple representation of error kind. /// /// # Examples /// From 2bae7302d9e3b5f6ab1bc9abfad502c00e5ec53f Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 27 Jan 2022 10:49:32 +0100 Subject: [PATCH 04/12] update `FutureIncompatibilityReason` --- compiler/rustc_lint_defs/src/builtin.rs | 8 +++++ compiler/rustc_lint_defs/src/lib.rs | 5 +++ compiler/rustc_middle/src/lint.rs | 47 ++++++++++++------------- 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 4096815c6a426..9b3b7d74b63fa 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -1793,6 +1793,10 @@ declare_lint! { Warn, "detects name collision with an existing but unstable method", @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::Custom( + "once this associated item is added to the standard library, \ + the ambiguity may cause an error or change in behavior!" + ), reference: "issue #48919 ", // Note: this item represents future incompatibility of all unstable functions in the // standard library, and thus should never be removed or changed to an error. @@ -2335,6 +2339,10 @@ declare_lint! { Warn, "reservation of a two-phased borrow conflicts with other shared borrows", @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::Custom( + "this borrowing pattern was not meant to be accepted, \ + and may become a hard error in the future" + ), reference: "issue #59159 ", }; } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 97f6df51f88dd..d6c7d025150f4 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -163,12 +163,17 @@ pub enum FutureIncompatibilityReason { /// This will be an error in a future release, and /// Cargo should create a report even for dependencies FutureReleaseErrorReportNow, + /// Code that changes meaning in some way in a + /// future release. + FutureReleaseSemanticsChange, /// Previously accepted code that will become an /// error in the provided edition EditionError(Edition), /// Code that changes meaning in some way in /// the provided edition EditionSemanticsChange(Edition), + /// A custom reason. + Custom(&'static str), } impl FutureIncompatibilityReason { diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index eef10356ed25e..661811140b84e 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -221,7 +221,6 @@ pub fn struct_lint_level<'s, 'd>( decorate: Box FnOnce(LintDiagnosticBuilder<'b>) + 'd>, ) { // Check for future incompatibility lints and issue a stronger warning. - let lint_id = LintId::of(lint); let future_incompatible = lint.future_incompatible; let has_future_breakage = future_incompatible.map_or( @@ -345,31 +344,29 @@ pub fn struct_lint_level<'s, 'd>( err.code(DiagnosticId::Lint { name, has_future_breakage, is_force_warn }); if let Some(future_incompatible) = future_incompatible { - let explanation = if lint_id == LintId::of(builtin::UNSTABLE_NAME_COLLISIONS) { - "once this associated item is added to the standard library, the ambiguity may \ - cause an error or change in behavior!" - .to_owned() - } else if lint_id == LintId::of(builtin::MUTABLE_BORROW_RESERVATION_CONFLICT) { - "this borrowing pattern was not meant to be accepted, and may become a hard error \ - in the future" - .to_owned() - } else if let FutureIncompatibilityReason::EditionError(edition) = - future_incompatible.reason - { - let current_edition = sess.edition(); - format!( - "this is accepted in the current edition (Rust {}) but is a hard error in Rust {}!", - current_edition, edition - ) - } else if let FutureIncompatibilityReason::EditionSemanticsChange(edition) = - future_incompatible.reason - { - format!("this changes meaning in Rust {}", edition) - } else { - "this was previously accepted by the compiler but is being phased out; \ - it will become a hard error in a future release!" - .to_owned() + let explanation = match future_incompatible.reason { + FutureIncompatibilityReason::FutureReleaseError + | FutureIncompatibilityReason::FutureReleaseErrorReportNow => { + "this was previously accepted by the compiler but is being phased out; \ + it will become a hard error in a future release!" + .to_owned() + } + FutureIncompatibilityReason::FutureReleaseSemanticsChange => { + "this will change its meaning in a future release!".to_owned() + } + FutureIncompatibilityReason::EditionError(edition) => { + let current_edition = sess.edition(); + format!( + "this is accepted in the current edition (Rust {}) but is a hard error in Rust {}!", + current_edition, edition + ) + } + FutureIncompatibilityReason::EditionSemanticsChange(edition) => { + format!("this changes meaning in Rust {}", edition) + } + FutureIncompatibilityReason::Custom(reason) => reason.to_owned(), }; + if future_incompatible.explain_reason { err.warn(&explanation); } From 2e9ee90584f23b3f85883ebdacf200ee0dd875f3 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 27 Jan 2022 10:49:52 +0100 Subject: [PATCH 05/12] implement lint for suspicious auto trait impls --- compiler/rustc_index/src/bit_set.rs | 6 + compiler/rustc_lint_defs/src/builtin.rs | 35 +++ compiler/rustc_middle/src/ty/trait_def.rs | 17 ++ compiler/rustc_typeck/src/coherence/orphan.rs | 213 +++++++++++++++++- .../ui/auto-traits/suspicious-impls-lint.rs | 34 +++ .../auto-traits/suspicious-impls-lint.stderr | 52 +++++ ...efault-trait-impl-cross-crate-coherence.rs | 1 + ...lt-trait-impl-cross-crate-coherence.stderr | 8 +- 8 files changed, 359 insertions(+), 7 deletions(-) create mode 100644 src/test/ui/auto-traits/suspicious-impls-lint.rs create mode 100644 src/test/ui/auto-traits/suspicious-impls-lint.stderr diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 5aa213cb70134..cf86c450a5bcc 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -938,6 +938,12 @@ pub struct GrowableBitSet { bit_set: BitSet, } +impl Default for GrowableBitSet { + fn default() -> Self { + GrowableBitSet::new_empty() + } +} + impl GrowableBitSet { /// Ensure that the set can hold at least `min_domain_size` elements. pub fn ensure(&mut self, min_domain_size: usize) { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 9b3b7d74b63fa..d44a40fda85de 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3050,6 +3050,7 @@ declare_lint_pass! { DEREF_INTO_DYN_SUPERTRAIT, DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, DUPLICATE_MACRO_ATTRIBUTES, + SUSPICIOUS_AUTO_TRAIT_IMPLS, ] } @@ -3626,3 +3627,37 @@ declare_lint! { Warn, "duplicated attribute" } + +declare_lint! { + /// The `suspicious_auto_trait_impls` lint checks for potentially incorrect + /// implementations of auto traits. + /// + /// ### Example + /// + /// ```rust + /// struct Foo(T); + /// + /// unsafe impl Send for Foo<*const T> {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// A type can implement auto traits, e.g. `Send`, `Sync` and `Unpin`, + /// in two different ways: either by writing an explicit impl or if + /// all fields of the type implement that auto trait. + /// + /// The compiler disables the automatic implementation if an explicit one + /// exists for given type constructor. The exact rules governing this + /// are currently unsound and quite subtle and and will be modified in the future. + /// This change will cause the automatic implementation to be disabled in more + /// cases, potentially breaking some code. + pub SUSPICIOUS_AUTO_TRAIT_IMPLS, + Warn, + "the rules governing auto traits will change in the future", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseSemanticsChange, + reference: "issue #93367 ", + }; +} diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 9f8053d4a4eac..9e32c0162e617 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -144,6 +144,23 @@ impl<'tcx> TyCtxt<'tcx> { }); } + pub fn non_blanket_impls_for_ty( + self, + def_id: DefId, + self_ty: Ty<'tcx>, + ) -> impl Iterator + 'tcx { + let impls = self.trait_impls_of(def_id); + if let Some(simp) = + fast_reject::simplify_type(self, self_ty, SimplifyParams::No, StripReferences::No) + { + if let Some(impls) = impls.non_blanket_impls.get(&simp) { + return impls.iter().copied(); + } + } + + [].iter().copied() + } + /// Applies function to every impl that could possibly match the self type `self_ty` and returns /// the first non-none value. pub fn find_map_relevant_impl Option>( diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs index e954b4cf512c5..777bd640669ce 100644 --- a/compiler/rustc_typeck/src/coherence/orphan.rs +++ b/compiler/rustc_typeck/src/coherence/orphan.rs @@ -1,24 +1,33 @@ //! Orphan checker: every impl either implements a trait defined in this //! crate or pertains to a type defined in this crate. +use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_errors::ErrorReported; use rustc_hir as hir; +use rustc_index::bit_set::GrowableBitSet; use rustc_infer::infer::TyCtxtInferExt; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_span::def_id::LocalDefId; +use rustc_middle::ty::subst::{GenericArg, InternalSubsts}; +use rustc_middle::ty::{self, ImplPolarity, Ty, TyCtxt, TypeFoldable, TypeVisitor}; +use rustc_session::lint; +use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::Span; use rustc_trait_selection::traits; +use std::ops::ControlFlow; pub(super) fn orphan_check_crate(tcx: TyCtxt<'_>, (): ()) -> &[LocalDefId] { let mut errors = Vec::new(); - for (_trait, impls_of_trait) in tcx.all_local_trait_impls(()) { + for (&trait_def_id, impls_of_trait) in tcx.all_local_trait_impls(()) { for &impl_of_trait in impls_of_trait { match orphan_check_impl(tcx, impl_of_trait) { Ok(()) => {} Err(ErrorReported) => errors.push(impl_of_trait), } } + + if tcx.trait_is_auto(trait_def_id) { + lint_auto_trait_impls(tcx, trait_def_id, impls_of_trait); + } } tcx.arena.alloc_slice(&errors) } @@ -265,3 +274,201 @@ fn emit_orphan_check_error<'tcx>( Err(ErrorReported) } + +#[derive(Default)] +struct AreUniqueParamsVisitor { + seen: GrowableBitSet, +} + +#[derive(Copy, Clone)] +enum NotUniqueParam<'tcx> { + DuplicateParam(GenericArg<'tcx>), + NotParam(GenericArg<'tcx>), +} + +impl<'tcx> TypeVisitor<'tcx> for AreUniqueParamsVisitor { + type BreakTy = NotUniqueParam<'tcx>; + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + match t.kind() { + ty::Param(p) => { + if self.seen.insert(p.index) { + ControlFlow::CONTINUE + } else { + ControlFlow::Break(NotUniqueParam::DuplicateParam(t.into())) + } + } + _ => ControlFlow::Break(NotUniqueParam::NotParam(t.into())), + } + } + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow { + match r { + ty::ReEarlyBound(p) => { + if self.seen.insert(p.index) { + ControlFlow::CONTINUE + } else { + ControlFlow::Break(NotUniqueParam::DuplicateParam(r.into())) + } + } + _ => ControlFlow::Break(NotUniqueParam::NotParam(r.into())), + } + } + fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow { + match c.val { + ty::ConstKind::Param(p) => { + if self.seen.insert(p.index) { + ControlFlow::CONTINUE + } else { + ControlFlow::Break(NotUniqueParam::DuplicateParam(c.into())) + } + } + _ => ControlFlow::Break(NotUniqueParam::NotParam(c.into())), + } + } +} + +/// Lint impls of auto traits if they are likely to have +/// unsound or surprising effects on auto impls. +fn lint_auto_trait_impls(tcx: TyCtxt<'_>, trait_def_id: DefId, impls: &[LocalDefId]) { + let mut non_covering_impls = Vec::new(); + for &impl_def_id in impls { + let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); + if trait_ref.references_error() { + return; + } + + if tcx.impl_polarity(impl_def_id) != ImplPolarity::Positive { + return; + } + + assert_eq!(trait_ref.substs.len(), 1); + let self_ty = trait_ref.self_ty(); + let (self_type_did, substs) = match self_ty.kind() { + ty::Adt(def, substs) => (def.did, substs), + _ => { + // FIXME: should also lint for stuff like `&i32` but + // considering that auto traits are unstable, that + // isn't too important for now as this only affects + // crates using `nightly`, and std. + continue; + } + }; + + // Impls which completely cover a given root type are fine as they + // disable auto impls entirely. So only lint if the substs + // are not a permutation of the identity substs. + match substs.visit_with(&mut AreUniqueParamsVisitor::default()) { + ControlFlow::Continue(()) => {} // ok + ControlFlow::Break(arg) => { + // Ideally: + // + // - compute the requirements for the auto impl candidate + // - check whether these are implied by the non covering impls + // - if not, emit the lint + // + // What we do here is a bit simpler: + // + // - badly check if an auto impl candidate definitely does not apply + // for the given simplified type + // - if so, do not lint + if fast_reject_auto_impl(tcx, trait_def_id, self_ty) { + // ok + } else { + non_covering_impls.push((impl_def_id, self_type_did, arg)); + } + } + } + } + + for &(impl_def_id, self_type_did, arg) in &non_covering_impls { + tcx.struct_span_lint_hir( + lint::builtin::SUSPICIOUS_AUTO_TRAIT_IMPLS, + tcx.hir().local_def_id_to_hir_id(impl_def_id), + tcx.def_span(impl_def_id), + |err| { + let mut err = err.build(&format!( + "cross-crate traits with a default impl, like `{}`, \ + should not be specialized", + tcx.def_path_str(trait_def_id), + )); + let item_span = tcx.def_span(self_type_did); + let self_descr = tcx.def_kind(self_type_did).descr(self_type_did); + err.span_note( + item_span, + &format!( + "try using the same sequence of generic parameters as the {} definition", + self_descr, + ), + ); + match arg { + NotUniqueParam::DuplicateParam(arg) => { + err.note(&format!("`{}` is mentioned multiple times", arg)); + } + NotUniqueParam::NotParam(arg) => { + err.note(&format!("`{}` is not a generic parameter", arg)); + } + } + err.emit(); + }, + ); + } +} + +fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty: Ty<'tcx>) -> bool { + struct DisableAutoTraitVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + trait_def_id: DefId, + self_ty_root: Ty<'tcx>, + seen: FxHashSet, + } + + impl<'tcx> TypeVisitor<'tcx> for DisableAutoTraitVisitor<'tcx> { + type BreakTy = (); + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow { + let tcx = self.tcx; + if t != self.self_ty_root { + for impl_def_id in tcx.non_blanket_impls_for_ty(self.trait_def_id, t) { + match tcx.impl_polarity(impl_def_id) { + ImplPolarity::Negative => return ControlFlow::BREAK, + ImplPolarity::Reservation => {} + // FIXME(@lcnr): That's probably not good enough, idk + // + // We might just want to take the rustdoc code and somehow avoid + // explicit impls for `Self`. + ImplPolarity::Positive => return ControlFlow::CONTINUE, + } + } + } + + match t.kind() { + ty::Adt(def, substs) => { + // @lcnr: This is the only place where cycles can happen. We avoid this + // by only visiting each `DefId` once. + // + // This will be is incorrect in subtle cases, but I don't care :) + if self.seen.insert(def.did) { + for ty in def.all_fields().map(|field| field.ty(tcx, substs)) { + ty.visit_with(self)?; + } + } + + ControlFlow::CONTINUE + } + _ => t.super_visit_with(self), + } + } + } + + let self_ty_root = match self_ty.kind() { + ty::Adt(def, _) => tcx.mk_adt(def, InternalSubsts::identity_for_item(tcx, def.did)), + _ => unimplemented!("unexpected self ty {:?}", self_ty), + }; + + self_ty_root + .visit_with(&mut DisableAutoTraitVisitor { + tcx, + self_ty_root, + trait_def_id, + seen: FxHashSet::default(), + }) + .is_break() +} diff --git a/src/test/ui/auto-traits/suspicious-impls-lint.rs b/src/test/ui/auto-traits/suspicious-impls-lint.rs new file mode 100644 index 0000000000000..1026a35a455ac --- /dev/null +++ b/src/test/ui/auto-traits/suspicious-impls-lint.rs @@ -0,0 +1,34 @@ +#![deny(suspicious_auto_trait_impls)] + +struct MayImplementSendOk(T); +unsafe impl Send for MayImplementSendOk {} // ok + +struct MayImplementSendErr(T); +unsafe impl Send for MayImplementSendErr<&T> {} +//~^ ERROR +//~| WARNING this will change its meaning + +struct ContainsNonSendDirect(*const T); +unsafe impl Send for ContainsNonSendDirect<&T> {} // ok + +struct ContainsPtr(*const T); +struct ContainsIndirectNonSend(ContainsPtr); +unsafe impl Send for ContainsIndirectNonSend<&T> {} // ok + +struct ContainsVec(Vec); +unsafe impl Send for ContainsVec {} +//~^ ERROR +//~| WARNING this will change its meaning + +struct TwoParams(T, U); +unsafe impl Send for TwoParams {} // ok + +struct TwoParamsFlipped(T, U); +unsafe impl Send for TwoParamsFlipped {} // ok + +struct TwoParamsSame(T, U); +unsafe impl Send for TwoParamsSame {} +//~^ ERROR +//~| WARNING this will change its meaning + +fn main() {} diff --git a/src/test/ui/auto-traits/suspicious-impls-lint.stderr b/src/test/ui/auto-traits/suspicious-impls-lint.stderr new file mode 100644 index 0000000000000..f91aa862271d3 --- /dev/null +++ b/src/test/ui/auto-traits/suspicious-impls-lint.stderr @@ -0,0 +1,52 @@ +error: cross-crate traits with a default impl, like `Send`, should not be specialized + --> $DIR/suspicious-impls-lint.rs:7:1 + | +LL | unsafe impl Send for MayImplementSendErr<&T> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/suspicious-impls-lint.rs:1:9 + | +LL | #![deny(suspicious_auto_trait_impls)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = warning: this will change its meaning in a future release! + = note: for more information, see issue #93367 +note: try using the same sequence of generic parameters as the struct definition + --> $DIR/suspicious-impls-lint.rs:6:1 + | +LL | struct MayImplementSendErr(T); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `&T` is not a generic parameter + +error: cross-crate traits with a default impl, like `Send`, should not be specialized + --> $DIR/suspicious-impls-lint.rs:19:1 + | +LL | unsafe impl Send for ContainsVec {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #93367 +note: try using the same sequence of generic parameters as the struct definition + --> $DIR/suspicious-impls-lint.rs:18:1 + | +LL | struct ContainsVec(Vec); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `i32` is not a generic parameter + +error: cross-crate traits with a default impl, like `Send`, should not be specialized + --> $DIR/suspicious-impls-lint.rs:30:1 + | +LL | unsafe impl Send for TwoParamsSame {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #93367 +note: try using the same sequence of generic parameters as the struct definition + --> $DIR/suspicious-impls-lint.rs:29:1 + | +LL | struct TwoParamsSame(T, U); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `T` is mentioned multiple times + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.rs b/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.rs index 772ac322032ec..cc75cd4909a23 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.rs +++ b/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.rs @@ -1,4 +1,5 @@ // aux-build:tdticc_coherence_lib.rs +#![allow(suspicious_auto_trait_impls)] // Test that we do not consider associated types to be sendable without // some applicable trait bound (and we don't ICE). diff --git a/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr b/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr index 90ab5be016c75..cf5c15df7051c 100644 --- a/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr +++ b/src/test/ui/typeck/typeck-default-trait-impl-cross-crate-coherence.stderr @@ -1,5 +1,5 @@ error[E0117]: only traits defined in the current crate can be implemented for arbitrary types - --> $DIR/typeck-default-trait-impl-cross-crate-coherence.rs:13:1 + --> $DIR/typeck-default-trait-impl-cross-crate-coherence.rs:14:1 | LL | impl DefaultedTrait for (A,) { } | ^^^^^^^^^^^^^^^^^^^^^^^^---- @@ -10,7 +10,7 @@ LL | impl DefaultedTrait for (A,) { } = note: define and implement a trait or new type instead error[E0117]: only traits defined in the current crate can be implemented for arbitrary types - --> $DIR/typeck-default-trait-impl-cross-crate-coherence.rs:16:1 + --> $DIR/typeck-default-trait-impl-cross-crate-coherence.rs:17:1 | LL | impl !DefaultedTrait for (B,) { } | ^^^^^^^^^^^^^^^^^^^^^^^^^---- @@ -21,13 +21,13 @@ LL | impl !DefaultedTrait for (B,) { } = note: define and implement a trait or new type instead error[E0321]: cross-crate traits with a default impl, like `DefaultedTrait`, can only be implemented for a struct/enum type defined in the current crate - --> $DIR/typeck-default-trait-impl-cross-crate-coherence.rs:20:1 + --> $DIR/typeck-default-trait-impl-cross-crate-coherence.rs:21:1 | LL | impl DefaultedTrait for Box { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait for type in another crate error[E0117]: only traits defined in the current crate can be implemented for arbitrary types - --> $DIR/typeck-default-trait-impl-cross-crate-coherence.rs:21:1 + --> $DIR/typeck-default-trait-impl-cross-crate-coherence.rs:22:1 | LL | impl DefaultedTrait for lib::Something { } | ^^^^^^^^^^^^^^^^^^^^^^^^----------------- From 7f2477810271d0e6e0e7025f0629a0213db90795 Mon Sep 17 00:00:00 2001 From: 5225225 <5225225@mailbox.org> Date: Mon, 17 Jan 2022 22:13:15 +0000 Subject: [PATCH 06/12] Suggest making base prefix lowercase if parsing fails --- compiler/rustc_parse/src/parser/expr.rs | 26 +++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 693dd0051dad1..09b88e9e07b6a 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1700,6 +1700,19 @@ impl<'a> Parser<'a> { s.len() > 1 && s.starts_with(first_chars) && s[1..].chars().all(|c| c.is_ascii_digit()) } + // Try to lowercase the prefix if it's a valid base prefix. + fn fix_base_capitalisation(s: &str) -> Option { + if let Some(stripped) = s.strip_prefix("B") { + Some(format!("0b{stripped}")) + } else if let Some(stripped) = s.strip_prefix("O") { + Some(format!("0o{stripped}")) + } else if let Some(stripped) = s.strip_prefix("X") { + Some(format!("0x{stripped}")) + } else { + None + } + } + let token::Lit { kind, suffix, .. } = lit; match err { // `NotLiteral` is not an error by itself, so we don't report @@ -1724,6 +1737,19 @@ impl<'a> Parser<'a> { self.struct_span_err(span, &msg) .help("valid widths are 8, 16, 32, 64 and 128") .emit(); + } else if let Some(fixed) = fix_base_capitalisation(suf) { + let msg = format!("invalid suffix `{}` for number literal", suf); + + self.struct_span_err(span, &msg) + .span_label(span, format!("invalid suffix `{}`", suf)) + .help("base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase") + .span_suggestion( + span, + "try making the prefix lowercase", + fixed, + Applicability::MaybeIncorrect, + ) + .emit(); } else { let msg = format!("invalid suffix `{}` for number literal", suf); self.struct_span_err(span, &msg) From 1a77d6227cccdd66367fd1e9cd736c03ddda64ec Mon Sep 17 00:00:00 2001 From: Tomoaki Kawada Date: Mon, 31 Jan 2022 17:39:38 +0900 Subject: [PATCH 07/12] kmc-solid: Increase the default stack size --- library/std/src/sys/itron/thread.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/itron/thread.rs b/library/std/src/sys/itron/thread.rs index ebcc9ab26e088..0d8a3ac716360 100644 --- a/library/std/src/sys/itron/thread.rs +++ b/library/std/src/sys/itron/thread.rs @@ -77,7 +77,8 @@ const LIFECYCLE_DETACHED_OR_JOINED: usize = usize::MAX; const LIFECYCLE_EXITED_OR_FINISHED_OR_JOIN_FINALIZE: usize = usize::MAX; // there's no single value for `JOINING` -pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * crate::mem::size_of::(); +// 64KiB for 32-bit ISAs, 128KiB for 64-bit ISAs. +pub const DEFAULT_MIN_STACK_SIZE: usize = 0x4000 * crate::mem::size_of::(); impl Thread { /// # Safety From 0b8f3729fb13e6262f1fa5055ec38d7033f95cd0 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Sat, 29 Jan 2022 14:36:35 +0100 Subject: [PATCH 08/12] Remove two unnecessary transmutes from opaque Encoder and Decoder --- compiler/rustc_serialize/src/opaque.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs index c272c687a7e98..7a05d2b762a47 100644 --- a/compiler/rustc_serialize/src/opaque.rs +++ b/compiler/rustc_serialize/src/opaque.rs @@ -130,8 +130,7 @@ impl serialize::Encoder for Encoder { #[inline] fn emit_i8(&mut self, v: i8) -> EncodeResult { - let as_u8: u8 = unsafe { std::mem::transmute(v) }; - self.emit_u8(as_u8) + self.emit_u8(v as u8) } #[inline] @@ -629,9 +628,9 @@ impl<'a> serialize::Decoder for Decoder<'a> { #[inline] fn read_i8(&mut self) -> i8 { - let as_u8 = self.data[self.position]; + let value = self.data[self.position]; self.position += 1; - unsafe { ::std::mem::transmute(as_u8) } + value as i8 } #[inline] From ec3b711a4bcc8cb3af34b2cdf1ad110eef5981f9 Mon Sep 17 00:00:00 2001 From: 5225225 <5225225@mailbox.org> Date: Thu, 27 Jan 2022 22:22:33 +0000 Subject: [PATCH 09/12] Write UI tests, tweak message --- compiler/rustc_parse/src/parser/expr.rs | 5 +- .../ui/numeric/uppercase-base-prefix.fixed | 77 +++++++++++++++ src/test/ui/numeric/uppercase-base-prefix.rs | 77 +++++++++++++++ .../ui/numeric/uppercase-base-prefix.stderr | 98 +++++++++++++++++++ 4 files changed, 254 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/numeric/uppercase-base-prefix.fixed create mode 100644 src/test/ui/numeric/uppercase-base-prefix.rs create mode 100644 src/test/ui/numeric/uppercase-base-prefix.stderr diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 09b88e9e07b6a..0115d498a7fb8 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1738,11 +1738,10 @@ impl<'a> Parser<'a> { .help("valid widths are 8, 16, 32, 64 and 128") .emit(); } else if let Some(fixed) = fix_base_capitalisation(suf) { - let msg = format!("invalid suffix `{}` for number literal", suf); + let msg = "invalid base prefix for number literal"; self.struct_span_err(span, &msg) - .span_label(span, format!("invalid suffix `{}`", suf)) - .help("base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase") + .note("base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase") .span_suggestion( span, "try making the prefix lowercase", diff --git a/src/test/ui/numeric/uppercase-base-prefix.fixed b/src/test/ui/numeric/uppercase-base-prefix.fixed new file mode 100644 index 0000000000000..1b1c837ec5040 --- /dev/null +++ b/src/test/ui/numeric/uppercase-base-prefix.fixed @@ -0,0 +1,77 @@ +// run-rustfix +// Checks that integers with an uppercase base prefix (0B, 0X, 0O) have a nice error +#![allow(unused_variables)] + +fn main() { + let a = 0xABCDEF; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0xABCDEF + + let b = 0o755; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0o755 + + let c = 0b10101010; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0b10101010 + + let d = 0xABC_DEF; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0xABC_DEF + + let e = 0o7_55; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0o7_55 + + let f = 0b1010_1010; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0b1010_1010 + + let g = 0xABC_DEF_u64; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0xABC_DEF_u64 + + let h = 0o7_55_u32; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0o7_55_u32 + + let i = 0b1010_1010_u8; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0b1010_1010_u8 + // + let j = 0xABCDEFu64; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0xABCDEFu64 + + let k = 0o755u32; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0o755u32 + + let l = 0b10101010u8; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0b10101010u8 +} diff --git a/src/test/ui/numeric/uppercase-base-prefix.rs b/src/test/ui/numeric/uppercase-base-prefix.rs new file mode 100644 index 0000000000000..233d553da6585 --- /dev/null +++ b/src/test/ui/numeric/uppercase-base-prefix.rs @@ -0,0 +1,77 @@ +// run-rustfix +// Checks that integers with an uppercase base prefix (0B, 0X, 0O) have a nice error +#![allow(unused_variables)] + +fn main() { + let a = 0XABCDEF; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0xABCDEF + + let b = 0O755; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0o755 + + let c = 0B10101010; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0b10101010 + + let d = 0XABC_DEF; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0xABC_DEF + + let e = 0O7_55; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0o7_55 + + let f = 0B1010_1010; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0b1010_1010 + + let g = 0XABC_DEF_u64; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0xABC_DEF_u64 + + let h = 0O7_55_u32; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0o7_55_u32 + + let i = 0B1010_1010_u8; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0b1010_1010_u8 + // + let j = 0XABCDEFu64; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0xABCDEFu64 + + let k = 0O755u32; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0o755u32 + + let l = 0B10101010u8; + //~^ ERROR invalid base prefix for number literal + //~| NOTE base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + //~| HELP try making the prefix lowercase + //~| SUGGESTION 0b10101010u8 +} diff --git a/src/test/ui/numeric/uppercase-base-prefix.stderr b/src/test/ui/numeric/uppercase-base-prefix.stderr new file mode 100644 index 0000000000000..4ba8d5224b3e6 --- /dev/null +++ b/src/test/ui/numeric/uppercase-base-prefix.stderr @@ -0,0 +1,98 @@ +error: invalid base prefix for number literal + --> $DIR/uppercase-base-prefix.rs:6:13 + | +LL | let a = 0XABCDEF; + | ^^^^^^^^ help: try making the prefix lowercase (notice the capitalization): `0xABCDEF` + | + = note: base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + +error: invalid base prefix for number literal + --> $DIR/uppercase-base-prefix.rs:12:13 + | +LL | let b = 0O755; + | ^^^^^ help: try making the prefix lowercase (notice the capitalization): `0o755` + | + = note: base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + +error: invalid base prefix for number literal + --> $DIR/uppercase-base-prefix.rs:18:13 + | +LL | let c = 0B10101010; + | ^^^^^^^^^^ help: try making the prefix lowercase: `0b10101010` + | + = note: base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + +error: invalid base prefix for number literal + --> $DIR/uppercase-base-prefix.rs:24:13 + | +LL | let d = 0XABC_DEF; + | ^^^^^^^^^ help: try making the prefix lowercase (notice the capitalization): `0xABC_DEF` + | + = note: base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + +error: invalid base prefix for number literal + --> $DIR/uppercase-base-prefix.rs:30:13 + | +LL | let e = 0O7_55; + | ^^^^^^ help: try making the prefix lowercase (notice the capitalization): `0o7_55` + | + = note: base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + +error: invalid base prefix for number literal + --> $DIR/uppercase-base-prefix.rs:36:13 + | +LL | let f = 0B1010_1010; + | ^^^^^^^^^^^ help: try making the prefix lowercase: `0b1010_1010` + | + = note: base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + +error: invalid base prefix for number literal + --> $DIR/uppercase-base-prefix.rs:42:13 + | +LL | let g = 0XABC_DEF_u64; + | ^^^^^^^^^^^^^ help: try making the prefix lowercase (notice the capitalization): `0xABC_DEF_u64` + | + = note: base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + +error: invalid base prefix for number literal + --> $DIR/uppercase-base-prefix.rs:48:13 + | +LL | let h = 0O7_55_u32; + | ^^^^^^^^^^ help: try making the prefix lowercase (notice the capitalization): `0o7_55_u32` + | + = note: base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + +error: invalid base prefix for number literal + --> $DIR/uppercase-base-prefix.rs:54:13 + | +LL | let i = 0B1010_1010_u8; + | ^^^^^^^^^^^^^^ help: try making the prefix lowercase: `0b1010_1010_u8` + | + = note: base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + +error: invalid base prefix for number literal + --> $DIR/uppercase-base-prefix.rs:60:13 + | +LL | let j = 0XABCDEFu64; + | ^^^^^^^^^^^ help: try making the prefix lowercase (notice the capitalization): `0xABCDEFu64` + | + = note: base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + +error: invalid base prefix for number literal + --> $DIR/uppercase-base-prefix.rs:66:13 + | +LL | let k = 0O755u32; + | ^^^^^^^^ help: try making the prefix lowercase (notice the capitalization): `0o755u32` + | + = note: base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + +error: invalid base prefix for number literal + --> $DIR/uppercase-base-prefix.rs:72:13 + | +LL | let l = 0B10101010u8; + | ^^^^^^^^^^^^ help: try making the prefix lowercase: `0b10101010u8` + | + = note: base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase + +error: aborting due to 12 previous errors + From c15ef58f4f67ee9ae02773642145650495f9f413 Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Mon, 29 Nov 2021 01:50:12 +0100 Subject: [PATCH 10/12] Fix suggestion to slice if scrutinee is a `Result` or `Option` --- compiler/rustc_typeck/src/check/pat.rs | 38 +++++++++++++++++++++----- src/test/ui/typeck/issue-91328.fixed | 27 ++++++++++++++++++ src/test/ui/typeck/issue-91328.rs | 27 ++++++++++++++++++ src/test/ui/typeck/issue-91328.stderr | 21 ++++++++++++++ 4 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 src/test/ui/typeck/issue-91328.fixed create mode 100644 src/test/ui/typeck/issue-91328.rs create mode 100644 src/test/ui/typeck/issue-91328.stderr diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 17b97d4cad1d4..9c2182a87ff1c 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -15,7 +15,7 @@ use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_span::hygiene::DesugaringKind; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::source_map::{Span, Spanned}; -use rustc_span::symbol::Ident; +use rustc_span::symbol::{sym, Ident}; use rustc_span::{BytePos, MultiSpan, DUMMY_SP}; use rustc_trait_selection::autoderef::Autoderef; use rustc_trait_selection::traits::{ObligationCause, Pattern}; @@ -2033,12 +2033,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { if let (Some(span), true) = (ti.span, ti.origin_expr) { if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - err.span_suggestion( - span, - "consider slicing here", - format!("{}[..]", snippet), - Applicability::MachineApplicable, - ); + let applicability = match self.resolve_vars_if_possible(ti.expected).kind() { + ty::Adt(adt_def, _) + if self.tcx.is_diagnostic_item(sym::Option, adt_def.did) + || self.tcx.is_diagnostic_item(sym::Result, adt_def.did) => + { + // Slicing won't work here, but `.as_deref()` might (issue #91328). + err.span_suggestion( + span, + "consider using `as_deref` here", + format!("{}.as_deref()", snippet), + Applicability::MaybeIncorrect, + ); + None + } + ty::Adt(adt_def, _) + if self.tcx.is_diagnostic_item(sym::Vec, adt_def.did) => + { + Some(Applicability::MachineApplicable) + } + _ => Some(Applicability::MaybeIncorrect), + }; + + if let Some(applicability) = applicability { + err.span_suggestion( + span, + "consider slicing here", + format!("{}[..]", snippet), + applicability, + ); + } } } } diff --git a/src/test/ui/typeck/issue-91328.fixed b/src/test/ui/typeck/issue-91328.fixed new file mode 100644 index 0000000000000..48dc26718da31 --- /dev/null +++ b/src/test/ui/typeck/issue-91328.fixed @@ -0,0 +1,27 @@ +// Regression test for issue #91328. + +// run-rustfix + +#![allow(dead_code)] + +fn foo(r: Result, i32>) -> i32 { + match r.as_deref() { + //~^ HELP: consider using `as_deref` here + Ok([a, b]) => a + b, + //~^ ERROR: expected an array or slice + //~| NOTE: pattern cannot match with input type + _ => 42, + } +} + +fn bar(o: Option>) -> i32 { + match o.as_deref() { + //~^ HELP: consider using `as_deref` here + Some([a, b]) => a + b, + //~^ ERROR: expected an array or slice + //~| NOTE: pattern cannot match with input type + _ => 42, + } +} + +fn main() {} diff --git a/src/test/ui/typeck/issue-91328.rs b/src/test/ui/typeck/issue-91328.rs new file mode 100644 index 0000000000000..de5deb9534902 --- /dev/null +++ b/src/test/ui/typeck/issue-91328.rs @@ -0,0 +1,27 @@ +// Regression test for issue #91328. + +// run-rustfix + +#![allow(dead_code)] + +fn foo(r: Result, i32>) -> i32 { + match r { + //~^ HELP: consider using `as_deref` here + Ok([a, b]) => a + b, + //~^ ERROR: expected an array or slice + //~| NOTE: pattern cannot match with input type + _ => 42, + } +} + +fn bar(o: Option>) -> i32 { + match o { + //~^ HELP: consider using `as_deref` here + Some([a, b]) => a + b, + //~^ ERROR: expected an array or slice + //~| NOTE: pattern cannot match with input type + _ => 42, + } +} + +fn main() {} diff --git a/src/test/ui/typeck/issue-91328.stderr b/src/test/ui/typeck/issue-91328.stderr new file mode 100644 index 0000000000000..4a9ddb2b92717 --- /dev/null +++ b/src/test/ui/typeck/issue-91328.stderr @@ -0,0 +1,21 @@ +error[E0529]: expected an array or slice, found `Vec` + --> $DIR/issue-91328.rs:10:12 + | +LL | match r { + | - help: consider using `as_deref` here: `r.as_deref()` +LL | +LL | Ok([a, b]) => a + b, + | ^^^^^^ pattern cannot match with input type `Vec` + +error[E0529]: expected an array or slice, found `Vec` + --> $DIR/issue-91328.rs:20:14 + | +LL | match o { + | - help: consider using `as_deref` here: `o.as_deref()` +LL | +LL | Some([a, b]) => a + b, + | ^^^^^^ pattern cannot match with input type `Vec` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0529`. From 0363f11ff03269d84eedb50d2130000fdfeceeb0 Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Sat, 15 Jan 2022 21:33:11 +0100 Subject: [PATCH 11/12] Add match on `Vec<_>` to `ui/typeck/issue-91328.rs` test --- src/test/ui/typeck/issue-91328.fixed | 10 ++++++++++ src/test/ui/typeck/issue-91328.rs | 10 ++++++++++ src/test/ui/typeck/issue-91328.stderr | 11 ++++++++++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/test/ui/typeck/issue-91328.fixed b/src/test/ui/typeck/issue-91328.fixed index 48dc26718da31..81b6a99607215 100644 --- a/src/test/ui/typeck/issue-91328.fixed +++ b/src/test/ui/typeck/issue-91328.fixed @@ -24,4 +24,14 @@ fn bar(o: Option>) -> i32 { } } +fn baz(v: Vec) -> i32 { + match v[..] { + //~^ HELP: consider slicing here + [a, b] => a + b, + //~^ ERROR: expected an array or slice + //~| NOTE: pattern cannot match with input type + _ => 42, + } +} + fn main() {} diff --git a/src/test/ui/typeck/issue-91328.rs b/src/test/ui/typeck/issue-91328.rs index de5deb9534902..e938d8f5c9f04 100644 --- a/src/test/ui/typeck/issue-91328.rs +++ b/src/test/ui/typeck/issue-91328.rs @@ -24,4 +24,14 @@ fn bar(o: Option>) -> i32 { } } +fn baz(v: Vec) -> i32 { + match v { + //~^ HELP: consider slicing here + [a, b] => a + b, + //~^ ERROR: expected an array or slice + //~| NOTE: pattern cannot match with input type + _ => 42, + } +} + fn main() {} diff --git a/src/test/ui/typeck/issue-91328.stderr b/src/test/ui/typeck/issue-91328.stderr index 4a9ddb2b92717..96ad00cde4f7b 100644 --- a/src/test/ui/typeck/issue-91328.stderr +++ b/src/test/ui/typeck/issue-91328.stderr @@ -16,6 +16,15 @@ LL | LL | Some([a, b]) => a + b, | ^^^^^^ pattern cannot match with input type `Vec` -error: aborting due to 2 previous errors +error[E0529]: expected an array or slice, found `Vec` + --> $DIR/issue-91328.rs:30:9 + | +LL | match v { + | - help: consider slicing here: `v[..]` +LL | +LL | [a, b] => a + b, + | ^^^^^^ pattern cannot match with input type `Vec` + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0529`. From 95344c02fd72511d3c87eb36f2fb36e28562934a Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Mon, 31 Jan 2022 20:34:26 +0100 Subject: [PATCH 12/12] Add FIXME comment --- compiler/rustc_typeck/src/check/pat.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 9c2182a87ff1c..62cd9c64a4a7c 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -2047,6 +2047,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); None } + // FIXME: instead of checking for Vec only, we could check whether the + // type implements `Deref`; see + // https://github.com/rust-lang/rust/pull/91343#discussion_r761466979 ty::Adt(adt_def, _) if self.tcx.is_diagnostic_item(sym::Vec, adt_def.did) => {