From 4e38d067fe70d383c5ceef4e3631f6f83e6e3f0d Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Wed, 19 Oct 2022 11:40:29 +0200 Subject: [PATCH 01/19] doc: rewrite doc for signed int::{carrying_add,borrowing_sub} --- library/core/src/num/int_macros.rs | 111 +++++++++++++++++++---------- 1 file changed, 75 insertions(+), 36 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 81f050cb283d4..c5d7d07cc8031 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -1506,37 +1506,50 @@ macro_rules! int_impl { (a as Self, b) } - /// Calculates `self + rhs + carry` without the ability to overflow. + /// Calculates `self` + `rhs` + `carry` and checks for overflow. /// - /// Performs "signed ternary addition" which takes in an extra bit to add, and may return an - /// additional bit of overflow. This signed function is used only on the highest-ordered data, - /// for which the signed overflow result indicates whether the big integer overflowed or not. + /// Performs "ternary addition" of two integer operands and a carry-in + /// bit, and returns a tuple of the sum along with a boolean indicating + /// whether an arithmetic overflow would occur. On overflow, the wrapped + /// value is returned. /// - /// # Examples + /// This allows chaining together multiple additions to create a wider + /// addition, and can be useful for bignum addition. This method should + /// only be used for the most significant word; for the less significant + /// words the unsigned method + #[doc = concat!("[`", stringify!($UnsignedT), "::carrying_add`]")] + /// should be used. /// - /// Basic usage: + /// The output boolean returned by this method is *not* a carry flag, + /// and should *not* be added to a more significant word. /// - /// ``` - /// #![feature(bigint_helper_methods)] - #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".carrying_add(2, false), (7, false));")] - #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".carrying_add(2, true), (8, false));")] - #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(1, false), (", stringify!($SelfT), "::MIN, true));")] - #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(0, true), (", stringify!($SelfT), "::MIN, true));")] - #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(1, true), (", stringify!($SelfT), "::MIN + 1, true));")] - #[doc = concat!("assert_eq!(", - stringify!($SelfT), "::MAX.carrying_add(", stringify!($SelfT), "::MAX, true), ", - "(-1, true));" - )] - #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.carrying_add(-1, true), (", stringify!($SelfT), "::MIN, false));")] - #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".carrying_add(", stringify!($SelfT), "::MAX, true), (", stringify!($SelfT), "::MIN, true));")] - /// ``` + /// If the input carry is false, this method is equivalent to + /// [`overflowing_add`](Self::overflowing_add). /// - /// If `carry` is false, this method is equivalent to [`overflowing_add`](Self::overflowing_add): + /// # Examples /// /// ``` /// #![feature(bigint_helper_methods)] - #[doc = concat!("assert_eq!(5_", stringify!($SelfT), ".carrying_add(2, false), 5_", stringify!($SelfT), ".overflowing_add(2));")] - #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.carrying_add(1, false), ", stringify!($SelfT), "::MAX.overflowing_add(1));")] + /// // Only the most significant word is signed. + /// // + #[doc = concat!("// 10 MAX (a = 10 × 2^", stringify!($BITS), " + 2^", stringify!($BITS), " - 1)")] + #[doc = concat!("// + -5 9 (b = -5 × 2^", stringify!($BITS), " + 9)")] + /// // --------- + #[doc = concat!("// 6 8 (sum = 6 × 2^", stringify!($BITS), " + 8)")] + /// + #[doc = concat!("let (a1, a0): (", stringify!($SelfT), ", ", stringify!($UnsignedT), ") = (10, ", stringify!($UnsignedT), "::MAX);")] + #[doc = concat!("let (b1, b0): (", stringify!($SelfT), ", ", stringify!($UnsignedT), ") = (-5, 9);")] + /// let carry0 = false; + /// + #[doc = concat!("// ", stringify!($UnsignedT), "::carrying_add for the less significant words")] + /// let (sum0, carry1) = a0.carrying_add(b0, carry0); + /// assert_eq!(carry1, true); + /// + #[doc = concat!("// ", stringify!($SelfT), "::carrying_add for the most significant word")] + /// let (sum1, overflow) = a1.carrying_add(b1, carry1); + /// assert_eq!(overflow, false); + /// + /// assert_eq!((sum1, sum0), (6, 8)); /// ``` #[unstable(feature = "bigint_helper_methods", issue = "85532")] #[rustc_const_unstable(feature = "const_bigint_helper_methods", issue = "85532")] @@ -1600,25 +1613,51 @@ macro_rules! int_impl { (a as Self, b) } - /// Calculates `self - rhs - borrow` without the ability to overflow. + /// Calculates `self` − `rhs` − `borrow` and checks for + /// overflow. /// - /// Performs "signed ternary subtraction" which takes in an extra bit to subtract, and may return an - /// additional bit of overflow. This signed function is used only on the highest-ordered data, - /// for which the signed overflow result indicates whether the big integer overflowed or not. + /// Performs "ternary subtraction" by subtracting both an integer + /// operandand a borrow-in bit from `self`, and returns a tuple of the + /// difference along with a boolean indicating whether an arithmetic + /// overflow would occur. On overflow, the wrapped value is returned. /// - /// # Examples + /// This allows chaining together multiple subtractions to create a + /// wider subtraction, and can be useful for bignum subtraction. This + /// method should only be used for the most significant word; for the + /// less significant words the unsigned method + #[doc = concat!("[`", stringify!($UnsignedT), "::borrowing_sub`]")] + /// should be used. /// - /// Basic usage: + /// The output boolean returned by this method is *not* a borrow flag, + /// and should *not* be subtracted from a more significant word. + /// + /// If the input borrow is false, this method is equivalent to + /// [`overflowing_sub`](Self::overflowing_sub). + /// + /// # Examples /// /// ``` /// #![feature(bigint_helper_methods)] - #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".borrowing_sub(2, false), (3, false));")] - #[doc = concat!("assert_eq!(5", stringify!($SelfT), ".borrowing_sub(2, true), (2, false));")] - #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".borrowing_sub(1, false), (-1, false));")] - #[doc = concat!("assert_eq!(0", stringify!($SelfT), ".borrowing_sub(1, true), (-2, false));")] - #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.borrowing_sub(1, true), (", stringify!($SelfT), "::MAX - 1, true));")] - #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.borrowing_sub(-1, false), (", stringify!($SelfT), "::MIN, true));")] - #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.borrowing_sub(-1, true), (", stringify!($SelfT), "::MAX, false));")] + /// // Only the most significant word is signed. + /// // + #[doc = concat!("// 6 8 (a = 6 × 2^", stringify!($BITS), " + 8)")] + #[doc = concat!("// - -5 9 (b = -5 × 2^", stringify!($BITS), " + 9)")] + /// // --------- + #[doc = concat!("// 10 MAX (diff = 10 × 2^", stringify!($BITS), " + 2^", stringify!($BITS), " - 1)")] + /// + #[doc = concat!("let (a1, a0): (", stringify!($SelfT), ", ", stringify!($UnsignedT), ") = (6, 8);")] + #[doc = concat!("let (b1, b0): (", stringify!($SelfT), ", ", stringify!($UnsignedT), ") = (-5, 9);")] + /// let borrow0 = false; + /// + #[doc = concat!("// ", stringify!($UnsignedT), "::borrowing_sub for the less significant words")] + /// let (diff0, borrow1) = a0.borrowing_sub(b0, borrow0); + /// assert_eq!(borrow1, true); + /// + #[doc = concat!("// ", stringify!($SelfT), "::borrowing_sub for the most significant word")] + /// let (diff1, overflow) = a1.borrowing_sub(b1, borrow1); + /// assert_eq!(overflow, false); + /// + #[doc = concat!("assert_eq!((diff1, diff0), (10, ", stringify!($UnsignedT), "::MAX));")] /// ``` #[unstable(feature = "bigint_helper_methods", issue = "85532")] #[rustc_const_unstable(feature = "const_bigint_helper_methods", issue = "85532")] From a29425c6d41db7d356d4fc8acddc2e3a497961ab Mon Sep 17 00:00:00 2001 From: Kathryn Long Date: Fri, 30 Dec 2022 23:56:18 -0500 Subject: [PATCH 02/19] Stabilize f16c_target_feature --- compiler/rustc_codegen_ssa/src/target_features.rs | 3 +-- compiler/rustc_feature/src/accepted.rs | 2 ++ compiler/rustc_feature/src/active.rs | 1 - library/core/src/lib.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index da69fc8ecf77e..739963fffd132 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -187,7 +187,7 @@ const X86_ALLOWED_FEATURES: &[(&str, Option)] = &[ ("bmi2", None), ("cmpxchg16b", Some(sym::cmpxchg16b_target_feature)), ("ermsb", Some(sym::ermsb_target_feature)), - ("f16c", Some(sym::f16c_target_feature)), + ("f16c", None), ("fma", None), ("fxsr", None), ("gfni", Some(sym::avx512_target_feature)), @@ -396,7 +396,6 @@ pub fn from_target_feature( Some(sym::cmpxchg16b_target_feature) => rust_features.cmpxchg16b_target_feature, Some(sym::movbe_target_feature) => rust_features.movbe_target_feature, Some(sym::rtm_target_feature) => rust_features.rtm_target_feature, - Some(sym::f16c_target_feature) => rust_features.f16c_target_feature, Some(sym::ermsb_target_feature) => rust_features.ermsb_target_feature, Some(sym::bpf_target_feature) => rust_features.bpf_target_feature, Some(sym::aarch64_ver_target_feature) => rust_features.aarch64_ver_target_feature, diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index e2f30fb89b91e..fcbc5bacfcccf 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -161,6 +161,8 @@ declare_features! ( (accepted, extern_crate_self, "1.34.0", Some(56409), None), /// Allows access to crate names passed via `--extern` through prelude. (accepted, extern_prelude, "1.30.0", Some(44660), None), + /// Allows using F16C intrinsics from `core::arch::{x86, x86_64}`. + (accepted, f16c_target_feature, "CURRENT_RUSTC_VERSION", Some(44839), None), /// Allows field shorthands (`x` meaning `x: x`) in struct literal expressions. (accepted, field_init_shorthand, "1.17.0", Some(37340), None), /// Allows `#[must_use]` on functions, and introduces must-use operators (RFC 1940). diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 34fc62e0549da..0165840d1b263 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -256,7 +256,6 @@ declare_features! ( (active, bpf_target_feature, "1.54.0", Some(44839), None), (active, cmpxchg16b_target_feature, "1.32.0", Some(44839), None), (active, ermsb_target_feature, "1.49.0", Some(44839), None), - (active, f16c_target_feature, "1.36.0", Some(44839), None), (active, hexagon_target_feature, "1.27.0", Some(44839), None), (active, mips_target_feature, "1.27.0", Some(44839), None), (active, movbe_target_feature, "1.34.0", Some(44839), None), diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 890d7df8e1796..5be84a4e6b96a 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -238,7 +238,6 @@ #![feature(arm_target_feature)] #![feature(avx512_target_feature)] #![feature(cmpxchg16b_target_feature)] -#![feature(f16c_target_feature)] #![feature(hexagon_target_feature)] #![feature(mips_target_feature)] #![feature(powerpc_target_feature)] @@ -247,6 +246,7 @@ #![feature(sse4a_target_feature)] #![feature(tbm_target_feature)] #![feature(wasm_target_feature)] +#![cfg_attr(bootstrap, feature(f16c_target_feature))] // allow using `core::` in intra-doc links #[allow(unused_extern_crates)] From 89f1555824799432118c3be1e4c0fb17854a7bbd Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 11 Jan 2023 18:58:44 +0000 Subject: [PATCH 03/19] Add `AstConv::astconv` method to remove `::` calls --- .../rustc_hir_analysis/src/astconv/mod.rs | 7 ++++ compiler/rustc_hir_analysis/src/collect.rs | 37 +++++-------------- .../src/collect/item_bounds.rs | 8 ++-- .../src/collect/predicates_of.rs | 26 ++++--------- compiler/rustc_hir_analysis/src/lib.rs | 5 +-- compiler/rustc_hir_typeck/src/coercion.rs | 4 +- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 12 +++--- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 8 ++-- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 3 +- .../src/fn_ctxt/suggestions.rs | 10 ++--- compiler/rustc_hir_typeck/src/lib.rs | 4 +- .../rustc_hir_typeck/src/method/confirm.rs | 3 +- 12 files changed, 50 insertions(+), 77 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 5a7957be318ea..3a284aecb667d 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -120,6 +120,13 @@ pub trait AstConv<'tcx> { fn set_tainted_by_errors(&self, e: ErrorGuaranteed); fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span); + + fn astconv(&self) -> &dyn AstConv<'tcx> + where + Self: Sized, + { + self + } } #[derive(Debug)] diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index b7f259668a1e4..cd745ee8cab69 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -351,7 +351,7 @@ impl<'tcx> ItemCtxt<'tcx> { } pub fn to_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { - >::ast_ty_to_ty(self, ast_ty) + self.astconv().ast_ty_to_ty(ast_ty) } pub fn hir_id(&self) -> hir::HirId { @@ -413,8 +413,7 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { poly_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Ty<'tcx> { if let Some(trait_ref) = poly_trait_ref.no_bound_vars() { - let item_substs = >::create_substs_for_associated_item( - self, + let item_substs = self.astconv().create_substs_for_associated_item( span, item_def_id, item_segment, @@ -1112,8 +1111,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { tcx.hir().get_parent(hir_id) && i.of_trait.is_some() { - >::ty_of_fn( - &icx, + icx.astconv().ty_of_fn( hir_id, sig.header.unsafety, sig.header.abi, @@ -1130,15 +1128,9 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { kind: TraitItemKind::Fn(FnSig { header, decl, span: _ }, _), generics, .. - }) => >::ty_of_fn( - &icx, - hir_id, - header.unsafety, - header.abi, - decl, - Some(generics), - None, - ), + }) => { + icx.astconv().ty_of_fn(hir_id, header.unsafety, header.abi, decl, Some(generics), None) + } ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(fn_decl, _, _), .. }) => { let abi = tcx.hir().get_foreign_abi(hir_id); @@ -1244,8 +1236,7 @@ fn infer_return_ty_for_fn_sig<'tcx>( ty::Binder::dummy(fn_sig) } - None => >::ty_of_fn( - icx, + None => icx.astconv().ty_of_fn( hir_id, sig.header.unsafety, sig.header.abi, @@ -1354,8 +1345,7 @@ fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { match item.kind { hir::ItemKind::Impl(ref impl_) => impl_.of_trait.as_ref().map(|ast_trait_ref| { let selfty = tcx.type_of(def_id); - >::instantiate_mono_trait_ref( - &icx, + icx.astconv().instantiate_mono_trait_ref( ast_trait_ref, selfty, check_impl_constness(tcx, impl_.constness, ast_trait_ref), @@ -1485,15 +1475,8 @@ fn compute_sig_of_foreign_fn_decl<'tcx>( hir::Unsafety::Unsafe }; let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - let fty = >::ty_of_fn( - &ItemCtxt::new(tcx, def_id), - hir_id, - unsafety, - abi, - decl, - None, - None, - ); + let fty = + ItemCtxt::new(tcx, def_id).astconv().ty_of_fn(hir_id, unsafety, abi, decl, None, None); // Feature gate SIMD types in FFI, since I am not sure that the // ABIs are handled at all correctly. -huonw diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 093e9560ccdfe..62eef710ba48f 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -26,9 +26,9 @@ fn associated_type_bounds<'tcx>( ); let icx = ItemCtxt::new(tcx, assoc_item_def_id); - let mut bounds = >::compute_bounds(&icx, item_ty, ast_bounds); + let mut bounds = icx.astconv().compute_bounds(item_ty, ast_bounds); // Associated types are implicitly sized unless a `?Sized` bound is found - >::add_implicitly_sized(&icx, &mut bounds, item_ty, ast_bounds, None, span); + icx.astconv().add_implicitly_sized(&mut bounds, item_ty, ast_bounds, None, span); let trait_def_id = tcx.parent(assoc_item_def_id); let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id.expect_local()); @@ -70,9 +70,9 @@ fn opaque_type_bounds<'tcx>( }; let icx = ItemCtxt::new(tcx, opaque_def_id); - let mut bounds = >::compute_bounds(&icx, item_ty, ast_bounds); + let mut bounds = icx.astconv().compute_bounds(item_ty, ast_bounds); // Opaque types are implicitly sized unless a `?Sized` bound is found - >::add_implicitly_sized(&icx, &mut bounds, item_ty, ast_bounds, None, span); + icx.astconv().add_implicitly_sized(&mut bounds, item_ty, ast_bounds, None, span); debug!(?bounds); tcx.arena.alloc_from_iter(bounds.predicates()) diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index 18fc43ce15cbd..234253556845b 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -162,8 +162,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP let mut bounds = Bounds::default(); // Params are implicitly sized unless a `?Sized` bound is found - >::add_implicitly_sized( - &icx, + icx.astconv().add_implicitly_sized( &mut bounds, param_ty, &[], @@ -211,22 +210,16 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP } let mut bounds = Bounds::default(); - >::add_bounds( - &icx, - ty, - bound_pred.bounds.iter(), - &mut bounds, - bound_vars, - ); + icx.astconv().add_bounds(ty, bound_pred.bounds.iter(), &mut bounds, bound_vars); predicates.extend(bounds.predicates()); } hir::WherePredicate::RegionPredicate(region_pred) => { - let r1 = >::ast_region_to_region(&icx, ®ion_pred.lifetime, None); + let r1 = icx.astconv().ast_region_to_region(®ion_pred.lifetime, None); predicates.extend(region_pred.bounds.iter().map(|bound| { let (r2, span) = match bound { hir::GenericBound::Outlives(lt) => { - (>::ast_region_to_region(&icx, lt, None), lt.ident.span) + (icx.astconv().ast_region_to_region(lt, None), lt.ident.span) } _ => bug!(), }; @@ -279,7 +272,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP debug!(?lifetimes); for (arg, duplicate) in std::iter::zip(lifetimes, ast_generics.params) { let hir::GenericArg::Lifetime(arg) = arg else { bug!() }; - let orig_region = >::ast_region_to_region(&icx, &arg, None); + let orig_region = icx.astconv().ast_region_to_region(&arg, None); if !matches!(orig_region.kind(), ty::ReEarlyBound(..)) { // Only early-bound regions can point to the original generic parameter. continue; @@ -527,14 +520,9 @@ pub(super) fn super_predicates_that_define_assoc_type( // Convert the bounds that follow the colon, e.g., `Bar + Zed` in `trait Foo: Bar + Zed`. let self_param_ty = tcx.types.self_param; let superbounds1 = if let Some(assoc_name) = assoc_name { - >::compute_bounds_that_match_assoc_type( - &icx, - self_param_ty, - bounds, - assoc_name, - ) + icx.astconv().compute_bounds_that_match_assoc_type(self_param_ty, bounds, assoc_name) } else { - >::compute_bounds(&icx, self_param_ty, bounds) + icx.astconv().compute_bounds(self_param_ty, bounds) }; let superbounds1 = superbounds1.predicates(); diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 2058832d5fdc1..4e76615ac9e4c 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -544,7 +544,7 @@ pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> { // scope. This is derived from the enclosing item-like thing. let env_def_id = tcx.hir().get_parent_item(hir_ty.hir_id); let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id()); - >::ast_ty_to_ty(&item_cx, hir_ty) + item_cx.astconv().ast_ty_to_ty(hir_ty) } pub fn hir_trait_to_predicates<'tcx>( @@ -558,8 +558,7 @@ pub fn hir_trait_to_predicates<'tcx>( let env_def_id = tcx.hir().get_parent_item(hir_trait.hir_ref_id); let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id()); let mut bounds = Bounds::default(); - let _ = >::instantiate_poly_trait_ref( - &item_cx, + let _ = &item_cx.astconv().instantiate_poly_trait_ref( hir_trait, DUMMY_SP, ty::BoundConstness::NotConst, diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index a0e086bc26103..7e1c0faa453a2 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1807,7 +1807,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { // Get the return type. && let hir::TyKind::OpaqueDef(..) = ty.kind { - let ty = >::ast_ty_to_ty(fcx, ty); + let ty = fcx.astconv().ast_ty_to_ty( ty); // Get the `impl Trait`'s `DefId`. if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = ty.kind() // Get the `impl Trait`'s `Item` so that we can get its trait bounds and @@ -1866,7 +1866,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fn is_return_ty_unsized<'a>(&self, fcx: &FnCtxt<'a, 'tcx>, blk_id: hir::HirId) -> bool { if let Some((fn_decl, _)) = fcx.get_fn_decl(blk_id) && let hir::FnRetTy::Return(ty) = fn_decl.output - && let ty = >::ast_ty_to_ty(fcx, ty) + && let ty = fcx.astconv().ast_ty_to_ty( ty) && let ty::Dynamic(..) = ty.kind() { return true; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 47c4b7d743193..006e49f8bf47f 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -374,7 +374,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> RawTy<'tcx> { - let t = >::ast_ty_to_ty(self, ast_t); + let t = self.astconv().ast_ty_to_ty(ast_t); self.register_wf_obligation(t.into(), ast_t.span, traits::WellFormed(None)); self.handle_raw_ty(ast_t.span, t) } @@ -777,7 +777,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // to be object-safe. // We manually call `register_wf_obligation` in the success path // below. - let ty = >::ast_ty_to_ty_in_path(self, qself); + let ty = self.astconv().ast_ty_to_ty_in_path(qself); (self.handle_raw_ty(span, ty), qself, segment) } QPath::LangItem(..) => { @@ -975,8 +975,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let path_segs = match res { Res::Local(_) | Res::SelfCtor(_) => vec![], - Res::Def(kind, def_id) => >::def_ids_for_value_path_segments( - self, + Res::Def(kind, def_id) => self.astconv().def_ids_for_value_path_segments( segments, self_ty.map(|ty| ty.raw), kind, @@ -1027,8 +1026,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // errors if type parameters are provided in an inappropriate place. let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect(); - let generics_has_err = >::prohibit_generics( - self, + let generics_has_err = self.astconv().prohibit_generics( segments.iter().enumerate().filter_map(|(index, seg)| { if !generic_segs.contains(&index) || is_alias_variant_ctor { Some(seg) @@ -1177,7 +1175,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> ty::GenericArg<'tcx> { match (¶m.kind, arg) { (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - >::ast_region_to_region(self.fcx, lt, Some(param)).into() + self.fcx.astconv().ast_region_to_region(lt, Some(param)).into() } (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { self.fcx.to_ty(ty).raw.into() diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 5075d9b893b3b..b9e13fd200924 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1664,15 +1664,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match *qpath { QPath::Resolved(ref maybe_qself, ref path) => { let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself).raw); - let ty = >::res_to_ty(self, self_ty, path, true); + let ty = self.astconv().res_to_ty(self_ty, path, true); (path.res, self.handle_raw_ty(path_span, ty)) } QPath::TypeRelative(ref qself, ref segment) => { let ty = self.to_ty(qself); - let result = >::associated_path_to_ty( - self, hir_id, path_span, ty.raw, qself, segment, true, - ); + let result = self + .astconv() + .associated_path_to_ty(hir_id, path_span, ty.raw, qself, segment, true); let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error()); let ty = self.handle_raw_ty(path_span, ty); let result = result.map(|(_, kind, def_id)| (kind, def_id)); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 6347b9a69a007..f403140789170 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -286,8 +286,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { poly_trait_ref, ); - let item_substs = >::create_substs_for_associated_item( - self, + let item_substs = self.astconv().create_substs_for_associated_item( span, item_def_id, item_segment, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 91498265259de..93c48aa6022c9 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -783,7 +783,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // are not, the expectation must have been caused by something else. debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind); let span = ty.span; - let ty = >::ast_ty_to_ty(self, ty); + let ty = self.astconv().ast_ty_to_ty(ty); debug!("suggest_missing_return_type: return type {:?}", ty); debug!("suggest_missing_return_type: expected type {:?}", ty); let bound_vars = self.tcx.late_bound_vars(fn_id); @@ -854,7 +854,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .. }) => { // FIXME: Maybe these calls to `ast_ty_to_ty` can be removed (and the ones below) - let ty = >::ast_ty_to_ty(self, bounded_ty); + let ty = self.astconv().ast_ty_to_ty(bounded_ty); Some((ty, bounds)) } _ => None, @@ -892,7 +892,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let all_bounds_str = all_matching_bounds_strs.join(" + "); let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| { - let ty = >::ast_ty_to_ty(self, param); + let ty = self.astconv().ast_ty_to_ty( param); matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param) }); @@ -946,7 +946,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let hir::FnRetTy::Return(ty) = fn_decl.output { - let ty = >::ast_ty_to_ty(self, ty); + let ty = self.astconv().ast_ty_to_ty(ty); let bound_vars = self.tcx.late_bound_vars(fn_id); let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars)); let ty = match self.tcx.asyncness(fn_id.owner) { @@ -1339,7 +1339,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::Path { segments: [segment], .. }, )) | hir::ExprKind::Path(QPath::TypeRelative(ty, segment)) => { - let self_ty = >::ast_ty_to_ty(self, ty); + let self_ty = self.astconv().ast_ty_to_ty(ty); if let Ok(pick) = self.probe_for_name( Mode::Path, Ident::new(capitalized_name, segment.ident.span), diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 69929589541f5..7ddf9eaa4d899 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -205,7 +205,7 @@ fn typeck_with_fallback<'tcx>( if let Some(hir::FnSig { header, decl, .. }) = fn_sig { let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() { - >::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None) + fcx.astconv().ty_of_fn(id, header.unsafety, header.abi, decl, None, None) } else { tcx.fn_sig(def_id) }; @@ -220,7 +220,7 @@ fn typeck_with_fallback<'tcx>( } else { let expected_type = body_ty .and_then(|ty| match ty.kind { - hir::TyKind::Infer => Some(>::ast_ty_to_ty(&fcx, ty)), + hir::TyKind::Infer => Some(fcx.astconv().ast_ty_to_ty(ty)), _ => None, }) .unwrap_or_else(|| match tcx.hir().get(id) { diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 7d2ba1fd09df5..ff9f5bca6b5e7 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -369,8 +369,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { ) -> subst::GenericArg<'tcx> { match (¶m.kind, arg) { (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - >::ast_region_to_region(self.cfcx.fcx, lt, Some(param)) - .into() + self.cfcx.fcx.astconv().ast_region_to_region(lt, Some(param)).into() } (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { self.cfcx.to_ty(ty).raw.into() From d642781708801d1227bb76deca803fda43ff8b37 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Wed, 11 Jan 2023 19:07:03 +0000 Subject: [PATCH 04/19] Make selfless `dyn AstConv` methods into toplevel functions --- .../src/astconv/generics.rs | 1134 ++++++++--------- .../rustc_hir_analysis/src/astconv/mod.rs | 17 +- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 7 +- .../rustc_hir_typeck/src/method/confirm.rs | 7 +- 4 files changed, 578 insertions(+), 587 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs index f64d65cc6ad7a..ce3682a8f2d5c 100644 --- a/compiler/rustc_hir_analysis/src/astconv/generics.rs +++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs @@ -1,6 +1,6 @@ use super::IsMethodCall; use crate::astconv::{ - AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, + CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition, }; use crate::errors::AssocTypeBindingNotAllowed; @@ -18,642 +18,624 @@ use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS; use rustc_span::{symbol::kw, Span}; use smallvec::SmallVec; -impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { - /// Report an error that a generic argument did not match the generic parameter that was - /// expected. - fn generic_arg_mismatch_err( - tcx: TyCtxt<'_>, - arg: &GenericArg<'_>, - param: &GenericParamDef, - possible_ordering_error: bool, - help: Option<&str>, - ) { - let sess = tcx.sess; - let mut err = struct_span_err!( - sess, - arg.span(), - E0747, - "{} provided when a {} was expected", - arg.descr(), - param.kind.descr(), - ); - - if let GenericParamDefKind::Const { .. } = param.kind { - if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. })) { - err.help("const arguments cannot yet be inferred with `_`"); - if sess.is_nightly_build() { - err.help( - "add `#![feature(generic_arg_infer)]` to the crate attributes to enable", - ); - } +/// Report an error that a generic argument did not match the generic parameter that was +/// expected. +fn generic_arg_mismatch_err( + tcx: TyCtxt<'_>, + arg: &GenericArg<'_>, + param: &GenericParamDef, + possible_ordering_error: bool, + help: Option<&str>, +) { + let sess = tcx.sess; + let mut err = struct_span_err!( + sess, + arg.span(), + E0747, + "{} provided when a {} was expected", + arg.descr(), + param.kind.descr(), + ); + + if let GenericParamDefKind::Const { .. } = param.kind { + if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. })) { + err.help("const arguments cannot yet be inferred with `_`"); + if sess.is_nightly_build() { + err.help("add `#![feature(generic_arg_infer)]` to the crate attributes to enable"); } } + } - let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut Diagnostic| { - let suggestions = vec![ - (arg.span().shrink_to_lo(), String::from("{ ")), - (arg.span().shrink_to_hi(), String::from(" }")), - ]; - err.multipart_suggestion( - "if this generic argument was intended as a const parameter, \ + let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut Diagnostic| { + let suggestions = vec![ + (arg.span().shrink_to_lo(), String::from("{ ")), + (arg.span().shrink_to_hi(), String::from(" }")), + ]; + err.multipart_suggestion( + "if this generic argument was intended as a const parameter, \ surround it with braces", - suggestions, - Applicability::MaybeIncorrect, - ); - }; - - // Specific suggestion set for diagnostics - match (arg, ¶m.kind) { - ( - GenericArg::Type(hir::Ty { - kind: hir::TyKind::Path(rustc_hir::QPath::Resolved(_, path)), - .. - }), - GenericParamDefKind::Const { .. }, - ) => match path.res { - Res::Err => { - add_braces_suggestion(arg, &mut err); - err.set_primary_message( - "unresolved item provided when a constant was expected", - ) + suggestions, + Applicability::MaybeIncorrect, + ); + }; + + // Specific suggestion set for diagnostics + match (arg, ¶m.kind) { + ( + GenericArg::Type(hir::Ty { + kind: hir::TyKind::Path(rustc_hir::QPath::Resolved(_, path)), + .. + }), + GenericParamDefKind::Const { .. }, + ) => match path.res { + Res::Err => { + add_braces_suggestion(arg, &mut err); + err.set_primary_message("unresolved item provided when a constant was expected") .emit(); - return; - } - Res::Def(DefKind::TyParam, src_def_id) => { - if let Some(param_local_id) = param.def_id.as_local() { - let param_name = tcx.hir().ty_param_name(param_local_id); - let param_type = tcx.type_of(param.def_id); - if param_type.is_suggestable(tcx, false) { - err.span_suggestion( - tcx.def_span(src_def_id), - "consider changing this type parameter to be a `const` generic", - format!("const {}: {}", param_name, param_type), - Applicability::MaybeIncorrect, - ); - }; - } - } - _ => add_braces_suggestion(arg, &mut err), - }, - ( - GenericArg::Type(hir::Ty { kind: hir::TyKind::Path(_), .. }), - GenericParamDefKind::Const { .. }, - ) => add_braces_suggestion(arg, &mut err), - ( - GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }), - GenericParamDefKind::Const { .. }, - ) if tcx.type_of(param.def_id) == tcx.types.usize => { - let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id())); - if let Ok(snippet) = snippet { - err.span_suggestion( - arg.span(), - "array type provided where a `usize` was expected, try", - format!("{{ {} }}", snippet), - Applicability::MaybeIncorrect, - ); + return; + } + Res::Def(DefKind::TyParam, src_def_id) => { + if let Some(param_local_id) = param.def_id.as_local() { + let param_name = tcx.hir().ty_param_name(param_local_id); + let param_type = tcx.type_of(param.def_id); + if param_type.is_suggestable(tcx, false) { + err.span_suggestion( + tcx.def_span(src_def_id), + "consider changing this type parameter to be a `const` generic", + format!("const {}: {}", param_name, param_type), + Applicability::MaybeIncorrect, + ); + }; } } - (GenericArg::Const(cnst), GenericParamDefKind::Type { .. }) => { - let body = tcx.hir().body(cnst.value.body); - if let rustc_hir::ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = - body.value.kind - { - if let Res::Def(DefKind::Fn { .. }, id) = path.res { - err.help(&format!( - "`{}` is a function item, not a type", - tcx.item_name(id) - )); - err.help("function item types cannot be named directly"); - } + _ => add_braces_suggestion(arg, &mut err), + }, + ( + GenericArg::Type(hir::Ty { kind: hir::TyKind::Path(_), .. }), + GenericParamDefKind::Const { .. }, + ) => add_braces_suggestion(arg, &mut err), + ( + GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }), + GenericParamDefKind::Const { .. }, + ) if tcx.type_of(param.def_id) == tcx.types.usize => { + let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id())); + if let Ok(snippet) = snippet { + err.span_suggestion( + arg.span(), + "array type provided where a `usize` was expected, try", + format!("{{ {} }}", snippet), + Applicability::MaybeIncorrect, + ); + } + } + (GenericArg::Const(cnst), GenericParamDefKind::Type { .. }) => { + let body = tcx.hir().body(cnst.value.body); + if let rustc_hir::ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body.value.kind + { + if let Res::Def(DefKind::Fn { .. }, id) = path.res { + err.help(&format!("`{}` is a function item, not a type", tcx.item_name(id))); + err.help("function item types cannot be named directly"); } } - _ => {} } + _ => {} + } - let kind_ord = param.kind.to_ord(); - let arg_ord = arg.to_ord(); + let kind_ord = param.kind.to_ord(); + let arg_ord = arg.to_ord(); - // This note is only true when generic parameters are strictly ordered by their kind. - if possible_ordering_error && kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal { - let (first, last) = if kind_ord < arg_ord { - (param.kind.descr(), arg.descr()) - } else { - (arg.descr(), param.kind.descr()) - }; - err.note(&format!("{} arguments must be provided before {} arguments", first, last)); - if let Some(help) = help { - err.help(help); - } + // This note is only true when generic parameters are strictly ordered by their kind. + if possible_ordering_error && kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal { + let (first, last) = if kind_ord < arg_ord { + (param.kind.descr(), arg.descr()) + } else { + (arg.descr(), param.kind.descr()) + }; + err.note(&format!("{} arguments must be provided before {} arguments", first, last)); + if let Some(help) = help { + err.help(help); } + } + + err.emit(); +} - err.emit(); +/// Creates the relevant generic argument substitutions +/// corresponding to a set of generic parameters. This is a +/// rather complex function. Let us try to explain the role +/// of each of its parameters: +/// +/// To start, we are given the `def_id` of the thing we are +/// creating the substitutions for, and a partial set of +/// substitutions `parent_substs`. In general, the substitutions +/// for an item begin with substitutions for all the "parents" of +/// that item -- e.g., for a method it might include the +/// parameters from the impl. +/// +/// Therefore, the method begins by walking down these parents, +/// starting with the outermost parent and proceed inwards until +/// it reaches `def_id`. For each parent `P`, it will check `parent_substs` +/// first to see if the parent's substitutions are listed in there. If so, +/// we can append those and move on. Otherwise, it invokes the +/// three callback functions: +/// +/// - `args_for_def_id`: given the `DefId` `P`, supplies back the +/// generic arguments that were given to that parent from within +/// the path; so e.g., if you have `::Bar`, the `DefId` +/// might refer to the trait `Foo`, and the arguments might be +/// `[T]`. The boolean value indicates whether to infer values +/// for arguments whose values were not explicitly provided. +/// - `provided_kind`: given the generic parameter and the value from `args_for_def_id`, +/// instantiate a `GenericArg`. +/// - `inferred_kind`: if no parameter was provided, and inference is enabled, then +/// creates a suitable inference variable. +pub fn create_substs_for_generic_args<'tcx, 'a>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + parent_substs: &[subst::GenericArg<'tcx>], + has_self: bool, + self_ty: Option>, + arg_count: &GenericArgCountResult, + ctx: &mut impl CreateSubstsForGenericArgsCtxt<'a, 'tcx>, +) -> SubstsRef<'tcx> { + // Collect the segments of the path; we need to substitute arguments + // for parameters throughout the entire path (wherever there are + // generic parameters). + let mut parent_defs = tcx.generics_of(def_id); + let count = parent_defs.count(); + let mut stack = vec![(def_id, parent_defs)]; + while let Some(def_id) = parent_defs.parent { + parent_defs = tcx.generics_of(def_id); + stack.push((def_id, parent_defs)); } - /// Creates the relevant generic argument substitutions - /// corresponding to a set of generic parameters. This is a - /// rather complex function. Let us try to explain the role - /// of each of its parameters: - /// - /// To start, we are given the `def_id` of the thing we are - /// creating the substitutions for, and a partial set of - /// substitutions `parent_substs`. In general, the substitutions - /// for an item begin with substitutions for all the "parents" of - /// that item -- e.g., for a method it might include the - /// parameters from the impl. - /// - /// Therefore, the method begins by walking down these parents, - /// starting with the outermost parent and proceed inwards until - /// it reaches `def_id`. For each parent `P`, it will check `parent_substs` - /// first to see if the parent's substitutions are listed in there. If so, - /// we can append those and move on. Otherwise, it invokes the - /// three callback functions: - /// - /// - `args_for_def_id`: given the `DefId` `P`, supplies back the - /// generic arguments that were given to that parent from within - /// the path; so e.g., if you have `::Bar`, the `DefId` - /// might refer to the trait `Foo`, and the arguments might be - /// `[T]`. The boolean value indicates whether to infer values - /// for arguments whose values were not explicitly provided. - /// - `provided_kind`: given the generic parameter and the value from `args_for_def_id`, - /// instantiate a `GenericArg`. - /// - `inferred_kind`: if no parameter was provided, and inference is enabled, then - /// creates a suitable inference variable. - pub fn create_substs_for_generic_args<'a>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - parent_substs: &[subst::GenericArg<'tcx>], - has_self: bool, - self_ty: Option>, - arg_count: &GenericArgCountResult, - ctx: &mut impl CreateSubstsForGenericArgsCtxt<'a, 'tcx>, - ) -> SubstsRef<'tcx> { - // Collect the segments of the path; we need to substitute arguments - // for parameters throughout the entire path (wherever there are - // generic parameters). - let mut parent_defs = tcx.generics_of(def_id); - let count = parent_defs.count(); - let mut stack = vec![(def_id, parent_defs)]; - while let Some(def_id) = parent_defs.parent { - parent_defs = tcx.generics_of(def_id); - stack.push((def_id, parent_defs)); + // We manually build up the substitution, rather than using convenience + // methods in `subst.rs`, so that we can iterate over the arguments and + // parameters in lock-step linearly, instead of trying to match each pair. + let mut substs: SmallVec<[subst::GenericArg<'tcx>; 8]> = SmallVec::with_capacity(count); + // Iterate over each segment of the path. + while let Some((def_id, defs)) = stack.pop() { + let mut params = defs.params.iter().peekable(); + + // If we have already computed substitutions for parents, we can use those directly. + while let Some(¶m) = params.peek() { + if let Some(&kind) = parent_substs.get(param.index as usize) { + substs.push(kind); + params.next(); + } else { + break; + } } - // We manually build up the substitution, rather than using convenience - // methods in `subst.rs`, so that we can iterate over the arguments and - // parameters in lock-step linearly, instead of trying to match each pair. - let mut substs: SmallVec<[subst::GenericArg<'tcx>; 8]> = SmallVec::with_capacity(count); - // Iterate over each segment of the path. - while let Some((def_id, defs)) = stack.pop() { - let mut params = defs.params.iter().peekable(); - - // If we have already computed substitutions for parents, we can use those directly. - while let Some(¶m) = params.peek() { - if let Some(&kind) = parent_substs.get(param.index as usize) { - substs.push(kind); - params.next(); - } else { - break; + // `Self` is handled first, unless it's been handled in `parent_substs`. + if has_self { + if let Some(¶m) = params.peek() { + if param.index == 0 { + if let GenericParamDefKind::Type { .. } = param.kind { + substs.push( + self_ty + .map(|ty| ty.into()) + .unwrap_or_else(|| ctx.inferred_kind(None, param, true)), + ); + params.next(); + } } } + } - // `Self` is handled first, unless it's been handled in `parent_substs`. - if has_self { - if let Some(¶m) = params.peek() { - if param.index == 0 { - if let GenericParamDefKind::Type { .. } = param.kind { - substs.push( - self_ty - .map(|ty| ty.into()) - .unwrap_or_else(|| ctx.inferred_kind(None, param, true)), - ); + // Check whether this segment takes generic arguments and the user has provided any. + let (generic_args, infer_args) = ctx.args_for_def_id(def_id); + + let args_iter = generic_args.iter().flat_map(|generic_args| generic_args.args.iter()); + let mut args = args_iter.clone().peekable(); + + // If we encounter a type or const when we expect a lifetime, we infer the lifetimes. + // If we later encounter a lifetime, we know that the arguments were provided in the + // wrong order. `force_infer_lt` records the type or const that forced lifetimes to be + // inferred, so we can use it for diagnostics later. + let mut force_infer_lt = None; + + loop { + // We're going to iterate through the generic arguments that the user + // provided, matching them with the generic parameters we expect. + // Mismatches can occur as a result of elided lifetimes, or for malformed + // input. We try to handle both sensibly. + match (args.peek(), params.peek()) { + (Some(&arg), Some(¶m)) => { + match (arg, ¶m.kind, arg_count.explicit_late_bound) { + (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _) + | ( + GenericArg::Type(_) | GenericArg::Infer(_), + GenericParamDefKind::Type { .. }, + _, + ) + | ( + GenericArg::Const(_) | GenericArg::Infer(_), + GenericParamDefKind::Const { .. }, + _, + ) => { + substs.push(ctx.provided_kind(param, arg)); + args.next(); params.next(); } - } - } - } - - // Check whether this segment takes generic arguments and the user has provided any. - let (generic_args, infer_args) = ctx.args_for_def_id(def_id); - - let args_iter = generic_args.iter().flat_map(|generic_args| generic_args.args.iter()); - let mut args = args_iter.clone().peekable(); - - // If we encounter a type or const when we expect a lifetime, we infer the lifetimes. - // If we later encounter a lifetime, we know that the arguments were provided in the - // wrong order. `force_infer_lt` records the type or const that forced lifetimes to be - // inferred, so we can use it for diagnostics later. - let mut force_infer_lt = None; - - loop { - // We're going to iterate through the generic arguments that the user - // provided, matching them with the generic parameters we expect. - // Mismatches can occur as a result of elided lifetimes, or for malformed - // input. We try to handle both sensibly. - match (args.peek(), params.peek()) { - (Some(&arg), Some(¶m)) => { - match (arg, ¶m.kind, arg_count.explicit_late_bound) { - (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _) - | ( - GenericArg::Type(_) | GenericArg::Infer(_), - GenericParamDefKind::Type { .. }, - _, - ) - | ( - GenericArg::Const(_) | GenericArg::Infer(_), - GenericParamDefKind::Const { .. }, - _, - ) => { - substs.push(ctx.provided_kind(param, arg)); - args.next(); - params.next(); - } - ( - GenericArg::Infer(_) | GenericArg::Type(_) | GenericArg::Const(_), - GenericParamDefKind::Lifetime, - _, - ) => { - // We expected a lifetime argument, but got a type or const - // argument. That means we're inferring the lifetimes. - substs.push(ctx.inferred_kind(None, param, infer_args)); - force_infer_lt = Some((arg, param)); - params.next(); - } - (GenericArg::Lifetime(_), _, ExplicitLateBound::Yes) => { - // We've come across a lifetime when we expected something else in - // the presence of explicit late bounds. This is most likely - // due to the presence of the explicit bound so we're just going to - // ignore it. - args.next(); - } - (_, _, _) => { - // We expected one kind of parameter, but the user provided - // another. This is an error. However, if we already know that - // the arguments don't match up with the parameters, we won't issue - // an additional error, as the user already knows what's wrong. - if arg_count.correct.is_ok() { - // We're going to iterate over the parameters to sort them out, and - // show that order to the user as a possible order for the parameters - let mut param_types_present = defs - .params - .iter() - .map(|param| (param.kind.to_ord(), param.clone())) - .collect::>(); - param_types_present.sort_by_key(|(ord, _)| *ord); - let (mut param_types_present, ordered_params): ( - Vec, - Vec, - ) = param_types_present.into_iter().unzip(); - param_types_present.dedup(); - - Self::generic_arg_mismatch_err( - tcx, - arg, - param, - !args_iter.clone().is_sorted_by_key(|arg| arg.to_ord()), - Some(&format!( - "reorder the arguments: {}: `<{}>`", - param_types_present - .into_iter() - .map(|ord| format!("{}s", ord)) - .collect::>() - .join(", then "), - ordered_params - .into_iter() - .filter_map(|param| { - if param.name == kw::SelfUpper { - None - } else { - Some(param.name.to_string()) - } - }) - .collect::>() - .join(", ") - )), - ); - } - - // We've reported the error, but we want to make sure that this - // problem doesn't bubble down and create additional, irrelevant - // errors. In this case, we're simply going to ignore the argument - // and any following arguments. The rest of the parameters will be - // inferred. - while args.next().is_some() {} - } + ( + GenericArg::Infer(_) | GenericArg::Type(_) | GenericArg::Const(_), + GenericParamDefKind::Lifetime, + _, + ) => { + // We expected a lifetime argument, but got a type or const + // argument. That means we're inferring the lifetimes. + substs.push(ctx.inferred_kind(None, param, infer_args)); + force_infer_lt = Some((arg, param)); + params.next(); } - } - - (Some(&arg), None) => { - // We should never be able to reach this point with well-formed input. - // There are three situations in which we can encounter this issue. - // - // 1. The number of arguments is incorrect. In this case, an error - // will already have been emitted, and we can ignore it. - // 2. There are late-bound lifetime parameters present, yet the - // lifetime arguments have also been explicitly specified by the - // user. - // 3. We've inferred some lifetimes, which have been provided later (i.e. - // after a type or const). We want to throw an error in this case. - - if arg_count.correct.is_ok() - && arg_count.explicit_late_bound == ExplicitLateBound::No - { - let kind = arg.descr(); - assert_eq!(kind, "lifetime"); - let (provided_arg, param) = - force_infer_lt.expect("lifetimes ought to have been inferred"); - Self::generic_arg_mismatch_err(tcx, provided_arg, param, false, None); + (GenericArg::Lifetime(_), _, ExplicitLateBound::Yes) => { + // We've come across a lifetime when we expected something else in + // the presence of explicit late bounds. This is most likely + // due to the presence of the explicit bound so we're just going to + // ignore it. + args.next(); } + (_, _, _) => { + // We expected one kind of parameter, but the user provided + // another. This is an error. However, if we already know that + // the arguments don't match up with the parameters, we won't issue + // an additional error, as the user already knows what's wrong. + if arg_count.correct.is_ok() { + // We're going to iterate over the parameters to sort them out, and + // show that order to the user as a possible order for the parameters + let mut param_types_present = defs + .params + .iter() + .map(|param| (param.kind.to_ord(), param.clone())) + .collect::>(); + param_types_present.sort_by_key(|(ord, _)| *ord); + let (mut param_types_present, ordered_params): ( + Vec, + Vec, + ) = param_types_present.into_iter().unzip(); + param_types_present.dedup(); + + generic_arg_mismatch_err( + tcx, + arg, + param, + !args_iter.clone().is_sorted_by_key(|arg| arg.to_ord()), + Some(&format!( + "reorder the arguments: {}: `<{}>`", + param_types_present + .into_iter() + .map(|ord| format!("{}s", ord)) + .collect::>() + .join(", then "), + ordered_params + .into_iter() + .filter_map(|param| { + if param.name == kw::SelfUpper { + None + } else { + Some(param.name.to_string()) + } + }) + .collect::>() + .join(", ") + )), + ); + } - break; + // We've reported the error, but we want to make sure that this + // problem doesn't bubble down and create additional, irrelevant + // errors. In this case, we're simply going to ignore the argument + // and any following arguments. The rest of the parameters will be + // inferred. + while args.next().is_some() {} + } } + } - (None, Some(¶m)) => { - // If there are fewer arguments than parameters, it means - // we're inferring the remaining arguments. - substs.push(ctx.inferred_kind(Some(&substs), param, infer_args)); - params.next(); + (Some(&arg), None) => { + // We should never be able to reach this point with well-formed input. + // There are three situations in which we can encounter this issue. + // + // 1. The number of arguments is incorrect. In this case, an error + // will already have been emitted, and we can ignore it. + // 2. There are late-bound lifetime parameters present, yet the + // lifetime arguments have also been explicitly specified by the + // user. + // 3. We've inferred some lifetimes, which have been provided later (i.e. + // after a type or const). We want to throw an error in this case. + + if arg_count.correct.is_ok() + && arg_count.explicit_late_bound == ExplicitLateBound::No + { + let kind = arg.descr(); + assert_eq!(kind, "lifetime"); + let (provided_arg, param) = + force_infer_lt.expect("lifetimes ought to have been inferred"); + generic_arg_mismatch_err(tcx, provided_arg, param, false, None); } - (None, None) => break, + break; + } + + (None, Some(¶m)) => { + // If there are fewer arguments than parameters, it means + // we're inferring the remaining arguments. + substs.push(ctx.inferred_kind(Some(&substs), param, infer_args)); + params.next(); } + + (None, None) => break, } } - - tcx.intern_substs(&substs) } - /// Checks that the correct number of generic arguments have been provided. - /// Used specifically for function calls. - pub fn check_generic_arg_count_for_call( - tcx: TyCtxt<'_>, - span: Span, - def_id: DefId, - generics: &ty::Generics, - seg: &hir::PathSegment<'_>, - is_method_call: IsMethodCall, - ) -> GenericArgCountResult { - let empty_args = hir::GenericArgs::none(); - let gen_args = seg.args.unwrap_or(&empty_args); - let gen_pos = if is_method_call == IsMethodCall::Yes { - GenericArgPosition::MethodCall + tcx.intern_substs(&substs) +} + +/// Checks that the correct number of generic arguments have been provided. +/// Used specifically for function calls. +pub fn check_generic_arg_count_for_call( + tcx: TyCtxt<'_>, + span: Span, + def_id: DefId, + generics: &ty::Generics, + seg: &hir::PathSegment<'_>, + is_method_call: IsMethodCall, +) -> GenericArgCountResult { + let empty_args = hir::GenericArgs::none(); + let gen_args = seg.args.unwrap_or(&empty_args); + let gen_pos = if is_method_call == IsMethodCall::Yes { + GenericArgPosition::MethodCall + } else { + GenericArgPosition::Value + }; + let has_self = generics.parent.is_none() && generics.has_self; + + check_generic_arg_count( + tcx, + span, + def_id, + seg, + generics, + gen_args, + gen_pos, + has_self, + seg.infer_args, + ) +} + +/// Checks that the correct number of generic arguments have been provided. +/// This is used both for datatypes and function calls. +#[instrument(skip(tcx, gen_pos), level = "debug")] +pub(crate) fn check_generic_arg_count( + tcx: TyCtxt<'_>, + span: Span, + def_id: DefId, + seg: &hir::PathSegment<'_>, + gen_params: &ty::Generics, + gen_args: &hir::GenericArgs<'_>, + gen_pos: GenericArgPosition, + has_self: bool, + infer_args: bool, +) -> GenericArgCountResult { + let default_counts = gen_params.own_defaults(); + let param_counts = gen_params.own_counts(); + + // Subtracting from param count to ensure type params synthesized from `impl Trait` + // cannot be explicitly specified. + let synth_type_param_count = gen_params + .params + .iter() + .filter(|param| matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: true, .. })) + .count(); + let named_type_param_count = param_counts.types - has_self as usize - synth_type_param_count; + let infer_lifetimes = + (gen_pos != GenericArgPosition::Type || infer_args) && !gen_args.has_lifetime_params(); + + if gen_pos != GenericArgPosition::Type && let Some(b) = gen_args.bindings.first() { + prohibit_assoc_ty_binding(tcx, b.span); + } + + let explicit_late_bound = + prohibit_explicit_late_bound_lifetimes(tcx, gen_params, gen_args, gen_pos); + + let mut invalid_args = vec![]; + + let mut check_lifetime_args = |min_expected_args: usize, + max_expected_args: usize, + provided_args: usize, + late_bounds_ignore: bool| { + if (min_expected_args..=max_expected_args).contains(&provided_args) { + return Ok(()); + } + + if late_bounds_ignore { + return Ok(()); + } + + if provided_args > max_expected_args { + invalid_args.extend( + gen_args.args[max_expected_args..provided_args].iter().map(|arg| arg.span()), + ); + }; + + let gen_args_info = if provided_args > min_expected_args { + invalid_args.extend( + gen_args.args[min_expected_args..provided_args].iter().map(|arg| arg.span()), + ); + let num_redundant_args = provided_args - min_expected_args; + GenericArgsInfo::ExcessLifetimes { num_redundant_args } } else { - GenericArgPosition::Value + let num_missing_args = min_expected_args - provided_args; + GenericArgsInfo::MissingLifetimes { num_missing_args } }; - let has_self = generics.parent.is_none() && generics.has_self; - Self::check_generic_arg_count( + let reported = WrongNumberOfGenericArgs::new( tcx, - span, - def_id, + gen_args_info, seg, - generics, + gen_params, + has_self as usize, gen_args, - gen_pos, - has_self, - seg.infer_args, + def_id, ) - } - - /// Checks that the correct number of generic arguments have been provided. - /// This is used both for datatypes and function calls. - #[instrument(skip(tcx, gen_pos), level = "debug")] - pub(crate) fn check_generic_arg_count( - tcx: TyCtxt<'_>, - span: Span, - def_id: DefId, - seg: &hir::PathSegment<'_>, - gen_params: &ty::Generics, - gen_args: &hir::GenericArgs<'_>, - gen_pos: GenericArgPosition, - has_self: bool, - infer_args: bool, - ) -> GenericArgCountResult { - let default_counts = gen_params.own_defaults(); - let param_counts = gen_params.own_counts(); - - // Subtracting from param count to ensure type params synthesized from `impl Trait` - // cannot be explicitly specified. - let synth_type_param_count = gen_params - .params - .iter() - .filter(|param| { - matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: true, .. }) - }) - .count(); - let named_type_param_count = - param_counts.types - has_self as usize - synth_type_param_count; - let infer_lifetimes = - (gen_pos != GenericArgPosition::Type || infer_args) && !gen_args.has_lifetime_params(); - - if gen_pos != GenericArgPosition::Type && let Some(b) = gen_args.bindings.first() { - Self::prohibit_assoc_ty_binding(tcx, b.span); + .diagnostic() + .emit(); + + Err(reported) + }; + + let min_expected_lifetime_args = if infer_lifetimes { 0 } else { param_counts.lifetimes }; + let max_expected_lifetime_args = param_counts.lifetimes; + let num_provided_lifetime_args = gen_args.num_lifetime_params(); + + let lifetimes_correct = check_lifetime_args( + min_expected_lifetime_args, + max_expected_lifetime_args, + num_provided_lifetime_args, + explicit_late_bound == ExplicitLateBound::Yes, + ); + + let mut check_types_and_consts = |expected_min, + expected_max, + expected_max_with_synth, + provided, + params_offset, + args_offset| { + debug!( + ?expected_min, + ?expected_max, + ?provided, + ?params_offset, + ?args_offset, + "check_types_and_consts" + ); + if (expected_min..=expected_max).contains(&provided) { + return Ok(()); } - let explicit_late_bound = - Self::prohibit_explicit_late_bound_lifetimes(tcx, gen_params, gen_args, gen_pos); - - let mut invalid_args = vec![]; + let num_default_params = expected_max - expected_min; - let mut check_lifetime_args = - |min_expected_args: usize, - max_expected_args: usize, - provided_args: usize, - late_bounds_ignore: bool| { - if (min_expected_args..=max_expected_args).contains(&provided_args) { - return Ok(()); - } - - if late_bounds_ignore { - return Ok(()); - } + let gen_args_info = if provided > expected_max { + invalid_args.extend( + gen_args.args[args_offset + expected_max..args_offset + provided] + .iter() + .map(|arg| arg.span()), + ); + let num_redundant_args = provided - expected_max; - if provided_args > max_expected_args { - invalid_args.extend( - gen_args.args[max_expected_args..provided_args] - .iter() - .map(|arg| arg.span()), - ); - }; - - let gen_args_info = if provided_args > min_expected_args { - invalid_args.extend( - gen_args.args[min_expected_args..provided_args] - .iter() - .map(|arg| arg.span()), - ); - let num_redundant_args = provided_args - min_expected_args; - GenericArgsInfo::ExcessLifetimes { num_redundant_args } - } else { - let num_missing_args = min_expected_args - provided_args; - GenericArgsInfo::MissingLifetimes { num_missing_args } - }; - - let reported = WrongNumberOfGenericArgs::new( - tcx, - gen_args_info, - seg, - gen_params, - has_self as usize, - gen_args, - def_id, - ) - .diagnostic() - .emit(); - - Err(reported) - }; - - let min_expected_lifetime_args = if infer_lifetimes { 0 } else { param_counts.lifetimes }; - let max_expected_lifetime_args = param_counts.lifetimes; - let num_provided_lifetime_args = gen_args.num_lifetime_params(); - - let lifetimes_correct = check_lifetime_args( - min_expected_lifetime_args, - max_expected_lifetime_args, - num_provided_lifetime_args, - explicit_late_bound == ExplicitLateBound::Yes, - ); + // Provide extra note if synthetic arguments like `impl Trait` are specified. + let synth_provided = provided <= expected_max_with_synth; - let mut check_types_and_consts = |expected_min, - expected_max, - expected_max_with_synth, - provided, - params_offset, - args_offset| { - debug!( - ?expected_min, - ?expected_max, - ?provided, - ?params_offset, - ?args_offset, - "check_types_and_consts" - ); - if (expected_min..=expected_max).contains(&provided) { - return Ok(()); + GenericArgsInfo::ExcessTypesOrConsts { + num_redundant_args, + num_default_params, + args_offset, + synth_provided, } + } else { + let num_missing_args = expected_max - provided; - let num_default_params = expected_max - expected_min; - - let gen_args_info = if provided > expected_max { - invalid_args.extend( - gen_args.args[args_offset + expected_max..args_offset + provided] - .iter() - .map(|arg| arg.span()), - ); - let num_redundant_args = provided - expected_max; + GenericArgsInfo::MissingTypesOrConsts { + num_missing_args, + num_default_params, + args_offset, + } + }; - // Provide extra note if synthetic arguments like `impl Trait` are specified. - let synth_provided = provided <= expected_max_with_synth; + debug!(?gen_args_info); - GenericArgsInfo::ExcessTypesOrConsts { - num_redundant_args, - num_default_params, - args_offset, - synth_provided, - } - } else { - let num_missing_args = expected_max - provided; + let reported = WrongNumberOfGenericArgs::new( + tcx, + gen_args_info, + seg, + gen_params, + params_offset, + gen_args, + def_id, + ) + .diagnostic() + .emit_unless(gen_args.has_err()); - GenericArgsInfo::MissingTypesOrConsts { - num_missing_args, - num_default_params, - args_offset, - } - }; - - debug!(?gen_args_info); - - let reported = WrongNumberOfGenericArgs::new( - tcx, - gen_args_info, - seg, - gen_params, - params_offset, - gen_args, - def_id, - ) - .diagnostic() - .emit_unless(gen_args.has_err()); - - Err(reported) - }; + Err(reported) + }; - let args_correct = { - let expected_min = if infer_args { - 0 - } else { - param_counts.consts + named_type_param_count - - default_counts.types - - default_counts.consts - }; - debug!(?expected_min); - debug!(arg_counts.lifetimes=?gen_args.num_lifetime_params()); - - check_types_and_consts( - expected_min, - param_counts.consts + named_type_param_count, - param_counts.consts + named_type_param_count + synth_type_param_count, - gen_args.num_generic_params(), - param_counts.lifetimes + has_self as usize, - gen_args.num_lifetime_params(), - ) + let args_correct = { + let expected_min = if infer_args { + 0 + } else { + param_counts.consts + named_type_param_count + - default_counts.types + - default_counts.consts }; + debug!(?expected_min); + debug!(arg_counts.lifetimes=?gen_args.num_lifetime_params()); + + check_types_and_consts( + expected_min, + param_counts.consts + named_type_param_count, + param_counts.consts + named_type_param_count + synth_type_param_count, + gen_args.num_generic_params(), + param_counts.lifetimes + has_self as usize, + gen_args.num_lifetime_params(), + ) + }; - GenericArgCountResult { - explicit_late_bound, - correct: lifetimes_correct.and(args_correct).map_err(|reported| { - GenericArgCountMismatch { reported: Some(reported), invalid_args } - }), - } + GenericArgCountResult { + explicit_late_bound, + correct: lifetimes_correct + .and(args_correct) + .map_err(|reported| GenericArgCountMismatch { reported: Some(reported), invalid_args }), } +} - /// Emits an error regarding forbidden type binding associations - pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) { - tcx.sess.emit_err(AssocTypeBindingNotAllowed { span }); - } +/// Emits an error regarding forbidden type binding associations +pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) { + tcx.sess.emit_err(AssocTypeBindingNotAllowed { span }); +} - /// Prohibits explicit lifetime arguments if late-bound lifetime parameters - /// are present. This is used both for datatypes and function calls. - pub(crate) fn prohibit_explicit_late_bound_lifetimes( - tcx: TyCtxt<'_>, - def: &ty::Generics, - args: &hir::GenericArgs<'_>, - position: GenericArgPosition, - ) -> ExplicitLateBound { - let param_counts = def.own_counts(); - let infer_lifetimes = position != GenericArgPosition::Type && !args.has_lifetime_params(); - - if infer_lifetimes { - return ExplicitLateBound::No; - } +/// Prohibits explicit lifetime arguments if late-bound lifetime parameters +/// are present. This is used both for datatypes and function calls. +pub(crate) fn prohibit_explicit_late_bound_lifetimes( + tcx: TyCtxt<'_>, + def: &ty::Generics, + args: &hir::GenericArgs<'_>, + position: GenericArgPosition, +) -> ExplicitLateBound { + let param_counts = def.own_counts(); + let infer_lifetimes = position != GenericArgPosition::Type && !args.has_lifetime_params(); + + if infer_lifetimes { + return ExplicitLateBound::No; + } - if let Some(span_late) = def.has_late_bound_regions { - let msg = "cannot specify lifetime arguments explicitly \ + if let Some(span_late) = def.has_late_bound_regions { + let msg = "cannot specify lifetime arguments explicitly \ if late bound lifetime parameters are present"; - let note = "the late bound lifetime parameter is introduced here"; - let span = args.args[0].span(); - - if position == GenericArgPosition::Value - && args.num_lifetime_params() != param_counts.lifetimes - { - let mut err = tcx.sess.struct_span_err(span, msg); - err.span_note(span_late, note); - err.emit(); - } else { - let mut multispan = MultiSpan::from_span(span); - multispan.push_span_label(span_late, note); - tcx.struct_span_lint_hir( - LATE_BOUND_LIFETIME_ARGUMENTS, - args.args[0].hir_id(), - multispan, - msg, - |lint| lint, - ); - } - - ExplicitLateBound::Yes + let note = "the late bound lifetime parameter is introduced here"; + let span = args.args[0].span(); + + if position == GenericArgPosition::Value + && args.num_lifetime_params() != param_counts.lifetimes + { + let mut err = tcx.sess.struct_span_err(span, msg); + err.span_note(span_late, note); + err.emit(); } else { - ExplicitLateBound::No + let mut multispan = MultiSpan::from_span(span); + multispan.push_span_label(span_late, note); + tcx.struct_span_lint_hir( + LATE_BOUND_LIFETIME_ARGUMENTS, + args.args[0].hir_id(), + multispan, + msg, + |lint| lint, + ); } + + ExplicitLateBound::Yes + } else { + ExplicitLateBound::No } } diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 3a284aecb667d..9fa0e6e8eaa64 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -3,8 +3,11 @@ //! instance of `AstConv`. mod errors; -mod generics; +pub mod generics; +use crate::astconv::generics::{ + check_generic_arg_count, create_substs_for_generic_args, prohibit_assoc_ty_binding, +}; use crate::bounds::Bounds; use crate::collect::HirPlaceholderCollector; use crate::errors::{ @@ -286,7 +289,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ty::BoundConstness::NotConst, ); if let Some(b) = item_segment.args().bindings.first() { - Self::prohibit_assoc_ty_binding(self.tcx(), b.span); + prohibit_assoc_ty_binding(self.tcx(), b.span); } substs @@ -356,7 +359,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { assert!(self_ty.is_none()); } - let arg_count = Self::check_generic_arg_count( + let arg_count = check_generic_arg_count( tcx, span, def_id, @@ -531,7 +534,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { inferred_params: vec![], infer_args, }; - let substs = Self::create_substs_for_generic_args( + let substs = create_substs_for_generic_args( tcx, def_id, parent_substs, @@ -617,7 +620,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ); if let Some(b) = item_segment.args().bindings.first() { - Self::prohibit_assoc_ty_binding(self.tcx(), b.span); + prohibit_assoc_ty_binding(self.tcx(), b.span); } args @@ -811,7 +814,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { constness, ); if let Some(b) = trait_segment.args().bindings.first() { - Self::prohibit_assoc_ty_binding(self.tcx(), b.span); + prohibit_assoc_ty_binding(self.tcx(), b.span); } self.tcx().mk_trait_ref(trait_def_id, substs) } @@ -2308,7 +2311,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { for segment in segments { // Only emit the first error to avoid overloading the user with error messages. if let Some(b) = segment.args().bindings.first() { - Self::prohibit_assoc_ty_binding(self.tcx(), b.span); + prohibit_assoc_ty_binding(self.tcx(), b.span); return true; } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 006e49f8bf47f..8570715b41e59 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -10,6 +10,9 @@ use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, GenericArg, Node, QPath}; +use rustc_hir_analysis::astconv::generics::{ + check_generic_arg_count_for_call, create_substs_for_generic_args, +}; use rustc_hir_analysis::astconv::{ AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, IsMethodCall, PathSeg, @@ -1067,7 +1070,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // parameter internally, but we don't allow users to specify the // parameter's value explicitly, so we have to do some error- // checking here. - let arg_count = >::check_generic_arg_count_for_call( + let arg_count = check_generic_arg_count_for_call( tcx, span, def_id, @@ -1233,7 +1236,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let substs_raw = self_ctor_substs.unwrap_or_else(|| { - >::create_substs_for_generic_args( + create_substs_for_generic_args( tcx, def_id, &[], diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index ff9f5bca6b5e7..4a33a791e1b7f 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -4,6 +4,9 @@ use crate::{callee, FnCtxt}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::GenericArg; +use rustc_hir_analysis::astconv::generics::{ + check_generic_arg_count_for_call, create_substs_for_generic_args, +}; use rustc_hir_analysis::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall}; use rustc_infer::infer::{self, InferOk}; use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext}; @@ -331,7 +334,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // variables. let generics = self.tcx.generics_of(pick.item.def_id); - let arg_count_correct = >::check_generic_arg_count_for_call( + let arg_count_correct = check_generic_arg_count_for_call( self.tcx, self.span, pick.item.def_id, @@ -398,7 +401,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { } } - let substs = >::create_substs_for_generic_args( + let substs = create_substs_for_generic_args( self.tcx, pick.item.def_id, parent_substs, From ede5c31af4388b1259138f4f7d8eb52daeae7f5f Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 6 Jan 2023 03:09:14 +0000 Subject: [PATCH 05/19] Be more specific about constructor `FnDef`s in type mismatch --- compiler/rustc_middle/src/ty/error.rs | 14 +++++++++++--- tests/ui/issues/issue-35241.stderr | 6 +++--- tests/ui/resolve/privacy-enum-ctor.stderr | 18 +++++++++--------- .../fn-or-tuple-struct-without-args.stderr | 18 +++++++++--------- .../typeck/issue-87181/empty-tuple-method.rs | 2 +- .../issue-87181/empty-tuple-method.stderr | 2 +- tests/ui/typeck/issue-87181/enum-variant.rs | 2 +- .../ui/typeck/issue-87181/enum-variant.stderr | 2 +- tests/ui/typeck/issue-87181/tuple-method.rs | 2 +- .../ui/typeck/issue-87181/tuple-method.stderr | 2 +- tests/ui/typeck/issue-96738.stderr | 2 +- 11 files changed, 39 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs index 50554cf9a82c8..5d394f71f0d76 100644 --- a/compiler/rustc_middle/src/ty/error.rs +++ b/compiler/rustc_middle/src/ty/error.rs @@ -2,10 +2,10 @@ use crate::traits::{ObligationCause, ObligationCauseCode}; use crate::ty::diagnostics::suggest_constraining_type_param; use crate::ty::print::{with_forced_trimmed_paths, FmtPrinter, Printer}; use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt}; -use hir::def::DefKind; use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; use rustc_errors::{pluralize, Diagnostic, MultiSpan}; use rustc_hir as hir; +use rustc_hir::def::{CtorOf, DefKind}; use rustc_hir::def_id::DefId; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{BytePos, Span}; @@ -319,7 +319,11 @@ impl<'tcx> Ty<'tcx> { .into() } } - ty::FnDef(..) => "fn item".into(), + ty::FnDef(def_id, ..) => match tcx.def_kind(def_id) { + DefKind::Ctor(CtorOf::Struct, _) => "struct constructor".into(), + DefKind::Ctor(CtorOf::Variant, _) => "enum constructor".into(), + _ => "fn item".into(), + }, ty::FnPtr(_) => "fn pointer".into(), ty::Dynamic(ref inner, ..) if let Some(principal) = inner.principal() => { format!("trait object `dyn {}`", tcx.def_path_str(principal.def_id())).into() @@ -366,7 +370,11 @@ impl<'tcx> Ty<'tcx> { _ => "reference", } .into(), - ty::FnDef(..) => "fn item".into(), + ty::FnDef(def_id, ..) => match tcx.def_kind(def_id) { + DefKind::Ctor(CtorOf::Struct, _) => "struct constructor".into(), + DefKind::Ctor(CtorOf::Variant, _) => "enum constructor".into(), + _ => "fn item".into(), + }, ty::FnPtr(_) => "fn pointer".into(), ty::Dynamic(..) => "trait object".into(), ty::Closure(..) => "closure".into(), diff --git a/tests/ui/issues/issue-35241.stderr b/tests/ui/issues/issue-35241.stderr index 42a78ed97e025..a01a8ffe76ee4 100644 --- a/tests/ui/issues/issue-35241.stderr +++ b/tests/ui/issues/issue-35241.stderr @@ -5,12 +5,12 @@ LL | struct Foo(u32); | ---------- fn(u32) -> Foo {Foo} defined here LL | LL | fn test() -> Foo { Foo } - | --- ^^^ expected struct `Foo`, found fn item + | --- ^^^ expected struct `Foo`, found struct constructor | | | expected `Foo` because of return type | - = note: expected struct `Foo` - found fn item `fn(u32) -> Foo {Foo}` + = note: expected struct `Foo` + found struct constructor `fn(u32) -> Foo {Foo}` help: use parentheses to construct this tuple struct | LL | fn test() -> Foo { Foo(/* u32 */) } diff --git a/tests/ui/resolve/privacy-enum-ctor.stderr b/tests/ui/resolve/privacy-enum-ctor.stderr index d734fa76b4a35..d9dbfb9f54173 100644 --- a/tests/ui/resolve/privacy-enum-ctor.stderr +++ b/tests/ui/resolve/privacy-enum-ctor.stderr @@ -267,12 +267,12 @@ LL | Fn(u8), | -- fn(u8) -> Z {Z::Fn} defined here ... LL | let _: Z = Z::Fn; - | - ^^^^^ expected enum `Z`, found fn item + | - ^^^^^ expected enum `Z`, found enum constructor | | | expected due to this | - = note: expected enum `Z` - found fn item `fn(u8) -> Z {Z::Fn}` + = note: expected enum `Z` + found enum constructor `fn(u8) -> Z {Z::Fn}` help: use parentheses to construct this tuple variant | LL | let _: Z = Z::Fn(/* u8 */); @@ -308,12 +308,12 @@ LL | Fn(u8), | -- fn(u8) -> E {E::Fn} defined here ... LL | let _: E = m::E::Fn; - | - ^^^^^^^^ expected enum `E`, found fn item + | - ^^^^^^^^ expected enum `E`, found enum constructor | | | expected due to this | - = note: expected enum `E` - found fn item `fn(u8) -> E {E::Fn}` + = note: expected enum `E` + found enum constructor `fn(u8) -> E {E::Fn}` help: use parentheses to construct this tuple variant | LL | let _: E = m::E::Fn(/* u8 */); @@ -349,12 +349,12 @@ LL | Fn(u8), | -- fn(u8) -> E {E::Fn} defined here ... LL | let _: E = E::Fn; - | - ^^^^^ expected enum `E`, found fn item + | - ^^^^^ expected enum `E`, found enum constructor | | | expected due to this | - = note: expected enum `E` - found fn item `fn(u8) -> E {E::Fn}` + = note: expected enum `E` + found enum constructor `fn(u8) -> E {E::Fn}` help: use parentheses to construct this tuple variant | LL | let _: E = E::Fn(/* u8 */); diff --git a/tests/ui/suggestions/fn-or-tuple-struct-without-args.stderr b/tests/ui/suggestions/fn-or-tuple-struct-without-args.stderr index d0ddb34d9fe2c..a92568ada4c9a 100644 --- a/tests/ui/suggestions/fn-or-tuple-struct-without-args.stderr +++ b/tests/ui/suggestions/fn-or-tuple-struct-without-args.stderr @@ -23,12 +23,12 @@ LL | struct S(usize, usize); | -------- fn(usize, usize) -> S {S} defined here ... LL | let _: S = S; - | - ^ expected struct `S`, found fn item + | - ^ expected struct `S`, found struct constructor | | | expected due to this | - = note: expected struct `S` - found fn item `fn(usize, usize) -> S {S}` + = note: expected struct `S` + found struct constructor `fn(usize, usize) -> S {S}` help: use parentheses to construct this tuple struct | LL | let _: S = S(/* usize */, /* usize */); @@ -59,12 +59,12 @@ LL | struct V(); | -------- fn() -> V {V} defined here ... LL | let _: V = V; - | - ^ expected struct `V`, found fn item + | - ^ expected struct `V`, found struct constructor | | | expected due to this | - = note: expected struct `V` - found fn item `fn() -> V {V}` + = note: expected struct `V` + found struct constructor `fn() -> V {V}` help: use parentheses to construct this tuple struct | LL | let _: V = V(); @@ -113,12 +113,12 @@ LL | A(usize), | - fn(usize) -> E {E::A} defined here ... LL | let _: E = E::A; - | - ^^^^ expected enum `E`, found fn item + | - ^^^^ expected enum `E`, found enum constructor | | | expected due to this | - = note: expected enum `E` - found fn item `fn(usize) -> E {E::A}` + = note: expected enum `E` + found enum constructor `fn(usize) -> E {E::A}` help: use parentheses to construct this tuple variant | LL | let _: E = E::A(/* usize */); diff --git a/tests/ui/typeck/issue-87181/empty-tuple-method.rs b/tests/ui/typeck/issue-87181/empty-tuple-method.rs index be68ad32ae55b..96b3f8dab8da7 100644 --- a/tests/ui/typeck/issue-87181/empty-tuple-method.rs +++ b/tests/ui/typeck/issue-87181/empty-tuple-method.rs @@ -10,5 +10,5 @@ impl Foo { fn main() { let thing = Bar { bar: Foo }; thing.bar.foo(); - //~^ ERROR no method named `foo` found for fn item `fn() -> Foo {Foo}` in the current scope [E0599] + //~^ ERROR no method named `foo` found for struct constructor `fn() -> Foo {Foo}` in the current scope [E0599] } diff --git a/tests/ui/typeck/issue-87181/empty-tuple-method.stderr b/tests/ui/typeck/issue-87181/empty-tuple-method.stderr index 23e7b7cc363fe..f0ca49e6d1e3e 100644 --- a/tests/ui/typeck/issue-87181/empty-tuple-method.stderr +++ b/tests/ui/typeck/issue-87181/empty-tuple-method.stderr @@ -1,4 +1,4 @@ -error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo}` in the current scope +error[E0599]: no method named `foo` found for struct constructor `fn() -> Foo {Foo}` in the current scope --> $DIR/empty-tuple-method.rs:12:15 | LL | thing.bar.foo(); diff --git a/tests/ui/typeck/issue-87181/enum-variant.rs b/tests/ui/typeck/issue-87181/enum-variant.rs index d87f99c3c5a19..ed01656ce72a3 100644 --- a/tests/ui/typeck/issue-87181/enum-variant.rs +++ b/tests/ui/typeck/issue-87181/enum-variant.rs @@ -12,5 +12,5 @@ impl Foo { fn main() { let thing = Bar { bar: Foo::Tup }; thing.bar.foo(); - //~^ ERROR no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` in the current scope [E0599] + //~^ ERROR no method named `foo` found for enum constructor `fn() -> Foo {Foo::Tup}` in the current scope [E0599] } diff --git a/tests/ui/typeck/issue-87181/enum-variant.stderr b/tests/ui/typeck/issue-87181/enum-variant.stderr index 2247ea27021f4..d313a887abd98 100644 --- a/tests/ui/typeck/issue-87181/enum-variant.stderr +++ b/tests/ui/typeck/issue-87181/enum-variant.stderr @@ -1,4 +1,4 @@ -error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` in the current scope +error[E0599]: no method named `foo` found for enum constructor `fn() -> Foo {Foo::Tup}` in the current scope --> $DIR/enum-variant.rs:14:15 | LL | thing.bar.foo(); diff --git a/tests/ui/typeck/issue-87181/tuple-method.rs b/tests/ui/typeck/issue-87181/tuple-method.rs index e88f642b0707b..6310984438c69 100644 --- a/tests/ui/typeck/issue-87181/tuple-method.rs +++ b/tests/ui/typeck/issue-87181/tuple-method.rs @@ -10,5 +10,5 @@ impl Foo { fn main() { let thing = Bar { bar: Foo }; thing.bar.foo(); - //~^ ERROR no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}` in the current scope [E0599] + //~^ ERROR no method named `foo` found for struct constructor `fn(u8, i32) -> Foo {Foo}` in the current scope [E0599] } diff --git a/tests/ui/typeck/issue-87181/tuple-method.stderr b/tests/ui/typeck/issue-87181/tuple-method.stderr index e27c41858d322..de3dc15a54b12 100644 --- a/tests/ui/typeck/issue-87181/tuple-method.stderr +++ b/tests/ui/typeck/issue-87181/tuple-method.stderr @@ -1,4 +1,4 @@ -error[E0599]: no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}` in the current scope +error[E0599]: no method named `foo` found for struct constructor `fn(u8, i32) -> Foo {Foo}` in the current scope --> $DIR/tuple-method.rs:12:15 | LL | thing.bar.foo(); diff --git a/tests/ui/typeck/issue-96738.stderr b/tests/ui/typeck/issue-96738.stderr index 0d4d87ef47e2b..547cffffa2ee3 100644 --- a/tests/ui/typeck/issue-96738.stderr +++ b/tests/ui/typeck/issue-96738.stderr @@ -1,4 +1,4 @@ -error[E0599]: no method named `nonexistent_method` found for fn item `fn(_) -> Option<_> {Option::<_>::Some}` in the current scope +error[E0599]: no method named `nonexistent_method` found for enum constructor `fn(_) -> Option<_> {Option::<_>::Some}` in the current scope --> $DIR/issue-96738.rs:2:10 | LL | Some.nonexistent_method(); From d375440dab2378dc3075755f388f2cbb242a3971 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 6 Jan 2023 03:33:57 +0000 Subject: [PATCH 06/19] label where constructor is defined and note that it should be called --- .../src/fn_ctxt/suggestions.rs | 18 ++++++++++--- .../substs-ppaux.normal.stderr | 8 +++--- .../substs-ppaux.verbose.stderr | 8 +++--- tests/ui/issues/issue-35241.stderr | 2 +- tests/ui/resolve/privacy-enum-ctor.stderr | 6 ++--- .../fn-or-tuple-struct-without-args.stderr | 26 +++++++++---------- 6 files changed, 39 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 91498265259de..3372a1b0dcbe0 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -5,7 +5,7 @@ use crate::method::probe::{IsSuggestion, Mode, ProbeScope}; use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; use rustc_errors::{Applicability, Diagnostic, MultiSpan}; use rustc_hir as hir; -use rustc_hir::def::{CtorOf, DefKind}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, @@ -417,10 +417,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if self.suggest_else_fn_with_closure(err, expr, found, expected) { return true; } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected)) - && let ty::FnDef(def_id, ..) = &found.kind() - && let Some(sp) = self.tcx.hir().span_if_local(*def_id) + && let ty::FnDef(def_id, ..) = *found.kind() + && let Some(sp) = self.tcx.hir().span_if_local(def_id) { - err.span_label(sp, format!("{found} defined here")); + let name = self.tcx.item_name(def_id); + let kind = self.tcx.def_kind(def_id); + if let DefKind::Ctor(of, CtorKind::Fn) = kind { + err.span_label(sp, format!("`{name}` defines {} constructor here, which should be called", match of { + CtorOf::Struct => "a struct", + CtorOf::Variant => "an enum variant", + })); + } else { + let descr = kind.descr(def_id); + err.span_label(sp, format!("{descr} `{name}` defined here")); + } return true; } else if self.check_for_cast(err, expr, found, expected, expected_ty_expr) { return true; diff --git a/tests/ui/associated-types/substs-ppaux.normal.stderr b/tests/ui/associated-types/substs-ppaux.normal.stderr index 3f180cf4f1f89..b50eac05c6bcc 100644 --- a/tests/ui/associated-types/substs-ppaux.normal.stderr +++ b/tests/ui/associated-types/substs-ppaux.normal.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/substs-ppaux.rs:16:17 | LL | fn bar<'a, T>() where T: 'a {} - | --------------------------- fn() {>::bar::<'static, char>} defined here + | --------------------------- associated function `bar` defined here ... LL | let x: () = >::bar::<'static, char>; | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found fn item @@ -20,7 +20,7 @@ error[E0308]: mismatched types --> $DIR/substs-ppaux.rs:25:17 | LL | fn bar<'a, T>() where T: 'a {} - | --------------------------- fn() {>::bar::<'static, char>} defined here + | --------------------------- associated function `bar` defined here ... LL | let x: () = >::bar::<'static, char>; | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found fn item @@ -38,7 +38,7 @@ error[E0308]: mismatched types --> $DIR/substs-ppaux.rs:33:17 | LL | fn baz() {} - | -------- fn() {>::baz} defined here + | -------- associated function `baz` defined here ... LL | let x: () = >::baz; | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found fn item @@ -56,7 +56,7 @@ error[E0308]: mismatched types --> $DIR/substs-ppaux.rs:41:17 | LL | fn foo<'z>() where &'z (): Sized { - | -------------------------------- fn() {foo::<'static>} defined here + | -------------------------------- function `foo` defined here ... LL | let x: () = foo::<'static>; | -- ^^^^^^^^^^^^^^ expected `()`, found fn item diff --git a/tests/ui/associated-types/substs-ppaux.verbose.stderr b/tests/ui/associated-types/substs-ppaux.verbose.stderr index 16dd29de2c543..a81924d8b3c58 100644 --- a/tests/ui/associated-types/substs-ppaux.verbose.stderr +++ b/tests/ui/associated-types/substs-ppaux.verbose.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/substs-ppaux.rs:16:17 | LL | fn bar<'a, T>() where T: 'a {} - | --------------------------- fn() {>::bar::} defined here + | --------------------------- associated function `bar` defined here ... LL | let x: () = >::bar::<'static, char>; | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found fn item @@ -20,7 +20,7 @@ error[E0308]: mismatched types --> $DIR/substs-ppaux.rs:25:17 | LL | fn bar<'a, T>() where T: 'a {} - | --------------------------- fn() {>::bar::} defined here + | --------------------------- associated function `bar` defined here ... LL | let x: () = >::bar::<'static, char>; | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found fn item @@ -38,7 +38,7 @@ error[E0308]: mismatched types --> $DIR/substs-ppaux.rs:33:17 | LL | fn baz() {} - | -------- fn() {>::baz} defined here + | -------- associated function `baz` defined here ... LL | let x: () = >::baz; | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found fn item @@ -56,7 +56,7 @@ error[E0308]: mismatched types --> $DIR/substs-ppaux.rs:41:17 | LL | fn foo<'z>() where &'z (): Sized { - | -------------------------------- fn() {foo::} defined here + | -------------------------------- function `foo` defined here ... LL | let x: () = foo::<'static>; | -- ^^^^^^^^^^^^^^ expected `()`, found fn item diff --git a/tests/ui/issues/issue-35241.stderr b/tests/ui/issues/issue-35241.stderr index a01a8ffe76ee4..d600e934bd577 100644 --- a/tests/ui/issues/issue-35241.stderr +++ b/tests/ui/issues/issue-35241.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/issue-35241.rs:3:20 | LL | struct Foo(u32); - | ---------- fn(u32) -> Foo {Foo} defined here + | ---------- `Foo` defines a struct constructor here, which should be called LL | LL | fn test() -> Foo { Foo } | --- ^^^ expected struct `Foo`, found struct constructor diff --git a/tests/ui/resolve/privacy-enum-ctor.stderr b/tests/ui/resolve/privacy-enum-ctor.stderr index d9dbfb9f54173..a24fe4d23ea25 100644 --- a/tests/ui/resolve/privacy-enum-ctor.stderr +++ b/tests/ui/resolve/privacy-enum-ctor.stderr @@ -264,7 +264,7 @@ error[E0308]: mismatched types --> $DIR/privacy-enum-ctor.rs:27:20 | LL | Fn(u8), - | -- fn(u8) -> Z {Z::Fn} defined here + | -- `Fn` defines an enum variant constructor here, which should be called ... LL | let _: Z = Z::Fn; | - ^^^^^ expected enum `Z`, found enum constructor @@ -305,7 +305,7 @@ error[E0308]: mismatched types --> $DIR/privacy-enum-ctor.rs:43:16 | LL | Fn(u8), - | -- fn(u8) -> E {E::Fn} defined here + | -- `Fn` defines an enum variant constructor here, which should be called ... LL | let _: E = m::E::Fn; | - ^^^^^^^^ expected enum `E`, found enum constructor @@ -346,7 +346,7 @@ error[E0308]: mismatched types --> $DIR/privacy-enum-ctor.rs:51:16 | LL | Fn(u8), - | -- fn(u8) -> E {E::Fn} defined here + | -- `Fn` defines an enum variant constructor here, which should be called ... LL | let _: E = E::Fn; | - ^^^^^ expected enum `E`, found enum constructor diff --git a/tests/ui/suggestions/fn-or-tuple-struct-without-args.stderr b/tests/ui/suggestions/fn-or-tuple-struct-without-args.stderr index a92568ada4c9a..4cbcd31fa5ec1 100644 --- a/tests/ui/suggestions/fn-or-tuple-struct-without-args.stderr +++ b/tests/ui/suggestions/fn-or-tuple-struct-without-args.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:29:20 | LL | fn foo(a: usize, b: usize) -> usize { a } - | ----------------------------------- fn(usize, usize) -> usize {foo} defined here + | ----------------------------------- function `foo` defined here ... LL | let _: usize = foo; | ----- ^^^ expected `usize`, found fn item @@ -20,7 +20,7 @@ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:30:16 | LL | struct S(usize, usize); - | -------- fn(usize, usize) -> S {S} defined here + | -------- `S` defines a struct constructor here, which should be called ... LL | let _: S = S; | - ^ expected struct `S`, found struct constructor @@ -38,7 +38,7 @@ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:31:20 | LL | fn bar() -> usize { 42 } - | ----------------- fn() -> usize {bar} defined here + | ----------------- function `bar` defined here ... LL | let _: usize = bar; | ----- ^^^ expected `usize`, found fn item @@ -56,7 +56,7 @@ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:32:16 | LL | struct V(); - | -------- fn() -> V {V} defined here + | -------- `V` defines a struct constructor here, which should be called ... LL | let _: V = V; | - ^ expected struct `V`, found struct constructor @@ -74,7 +74,7 @@ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:33:20 | LL | fn baz(x: usize, y: usize) -> usize { x } - | ----------------------------------- fn(usize, usize) -> usize {<_ as T>::baz} defined here + | ----------------------------------- associated function `baz` defined here ... LL | let _: usize = T::baz; | ----- ^^^^^^ expected `usize`, found fn item @@ -92,7 +92,7 @@ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:34:20 | LL | fn bat(x: usize) -> usize { 42 } - | ------------------------- fn(usize) -> usize {<_ as T>::bat} defined here + | ------------------------- associated function `bat` defined here ... LL | let _: usize = T::bat; | ----- ^^^^^^ expected `usize`, found fn item @@ -110,7 +110,7 @@ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:35:16 | LL | A(usize), - | - fn(usize) -> E {E::A} defined here + | - `A` defines an enum variant constructor here, which should be called ... LL | let _: E = E::A; | - ^^^^ expected enum `E`, found enum constructor @@ -134,7 +134,7 @@ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:37:20 | LL | fn baz(x: usize, y: usize) -> usize { x } - | ----------------------------------- fn(usize, usize) -> usize {::baz} defined here + | ----------------------------------- associated function `baz` defined here ... LL | let _: usize = X::baz; | ----- ^^^^^^ expected `usize`, found fn item @@ -152,7 +152,7 @@ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:38:20 | LL | fn bat(x: usize) -> usize { 42 } - | ------------------------- fn(usize) -> usize {::bat} defined here + | ------------------------- associated function `bat` defined here ... LL | let _: usize = X::bat; | ----- ^^^^^^ expected `usize`, found fn item @@ -170,7 +170,7 @@ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:39:20 | LL | fn bax(x: usize) -> usize { 42 } - | ------------------------- fn(usize) -> usize {::bax} defined here + | ------------------------- associated function `bax` defined here ... LL | let _: usize = X::bax; | ----- ^^^^^^ expected `usize`, found fn item @@ -188,7 +188,7 @@ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:40:20 | LL | fn bach(x: usize) -> usize; - | --------------------------- fn(usize) -> usize {::bach} defined here + | --------------------------- associated function `bach` defined here ... LL | let _: usize = X::bach; | ----- ^^^^^^^ expected `usize`, found fn item @@ -206,7 +206,7 @@ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:41:20 | LL | fn ban(&self) -> usize { 42 } - | ---------------------- for<'a> fn(&'a X) -> usize {::ban} defined here + | ---------------------- associated function `ban` defined here ... LL | let _: usize = X::ban; | ----- ^^^^^^ expected `usize`, found fn item @@ -224,7 +224,7 @@ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:42:20 | LL | fn bal(&self) -> usize; - | ----------------------- for<'a> fn(&'a X) -> usize {::bal} defined here + | ----------------------- associated function `bal` defined here ... LL | let _: usize = X::bal; | ----- ^^^^^^ expected `usize`, found fn item From c8334ce60cce9571fcfdea31a3e67e3933fb7665 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 27 Dec 2022 00:39:36 +0000 Subject: [PATCH 07/19] Move autoderef to rustc_hir_analysis --- .../locales/en-US/hir_analysis.ftl | 4 ++ .../locales/en-US/trait_selection.ftl | 4 -- .../src/autoderef.rs | 4 ++ .../rustc_hir_analysis/src/check/wfcheck.rs | 3 +- compiler/rustc_hir_analysis/src/errors.rs | 12 +++++ compiler/rustc_hir_analysis/src/lib.rs | 1 + compiler/rustc_hir_typeck/src/autoderef.rs | 2 +- compiler/rustc_hir_typeck/src/callee.rs | 2 +- compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs | 10 ++++- compiler/rustc_hir_typeck/src/method/probe.rs | 2 +- compiler/rustc_hir_typeck/src/place_op.rs | 2 +- .../src/infer/error_reporting/mod.rs | 7 ++- compiler/rustc_infer/src/infer/mod.rs | 4 ++ compiler/rustc_trait_selection/src/errors.rs | 13 ------ compiler/rustc_trait_selection/src/lib.rs | 1 - .../src/traits/error_reporting/suggestions.rs | 45 ++++++++++--------- 16 files changed, 70 insertions(+), 46 deletions(-) rename compiler/{rustc_trait_selection => rustc_hir_analysis}/src/autoderef.rs (98%) diff --git a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl index 26cdf8a58f3fb..41f458f6c1785 100644 --- a/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl +++ b/compiler/rustc_error_messages/locales/en-US/hir_analysis.ftl @@ -120,3 +120,7 @@ hir_analysis_self_in_impl_self = hir_analysis_linkage_type = invalid type for variable with `#[linkage]` attribute + +hir_analysis_auto_deref_reached_recursion_limit = reached the recursion limit while auto-dereferencing `{$ty}` + .label = deref recursion limit reached + .help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`) diff --git a/compiler/rustc_error_messages/locales/en-US/trait_selection.ftl b/compiler/rustc_error_messages/locales/en-US/trait_selection.ftl index 004e0ab189694..14eb4a5502d5c 100644 --- a/compiler/rustc_error_messages/locales/en-US/trait_selection.ftl +++ b/compiler/rustc_error_messages/locales/en-US/trait_selection.ftl @@ -2,10 +2,6 @@ trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entri trait_selection_unable_to_construct_constant_value = unable to construct a constant value for the unevaluated constant {$unevaluated} -trait_selection_auto_deref_reached_recursion_limit = reached the recursion limit while auto-dereferencing `{$ty}` - .label = deref recursion limit reached - .help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`) - trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]` .label = empty on-clause here diff --git a/compiler/rustc_trait_selection/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs similarity index 98% rename from compiler/rustc_trait_selection/src/autoderef.rs rename to compiler/rustc_hir_analysis/src/autoderef.rs index e988c77a064f6..730560cc68686 100644 --- a/compiler/rustc_trait_selection/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -178,6 +178,10 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { self.state.obligations } + pub fn current_obligations(&self) -> Vec> { + self.state.obligations.clone() + } + pub fn steps(&self) -> &[(Ty<'tcx>, AutoderefKind)] { &self.state.steps } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index e9baab594530e..d1f4dbc8d8454 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1,4 +1,6 @@ +use crate::autoderef::Autoderef; use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter}; + use hir::def::DefKind; use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; @@ -22,7 +24,6 @@ use rustc_session::parse::feature_err; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::autoderef::Autoderef; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index d383fcacb3a9c..04f5f3f62765a 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -300,3 +300,15 @@ pub(crate) struct LinkageType { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[help] +#[diag(hir_analysis_auto_deref_reached_recursion_limit, code = "E0055")] +pub struct AutoDerefReachedRecursionLimit<'a> { + #[primary_span] + #[label] + pub span: Span, + pub ty: Ty<'a>, + pub suggested_limit: rustc_session::Limit, + pub crate_name: Symbol, +} diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 2058832d5fdc1..65c1d7f373a34 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -84,6 +84,7 @@ extern crate rustc_middle; pub mod check; pub mod astconv; +pub mod autoderef; mod bounds; mod check_unused; mod coherence; diff --git a/compiler/rustc_hir_typeck/src/autoderef.rs b/compiler/rustc_hir_typeck/src/autoderef.rs index 41b52a4c4a9fc..7873257c4e3d1 100644 --- a/compiler/rustc_hir_typeck/src/autoderef.rs +++ b/compiler/rustc_hir_typeck/src/autoderef.rs @@ -2,11 +2,11 @@ use super::method::MethodCallee; use super::{FnCtxt, PlaceOp}; +use rustc_hir_analysis::autoderef::{Autoderef, AutoderefKind}; use rustc_infer::infer::InferOk; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; -use rustc_trait_selection::autoderef::{Autoderef, AutoderefKind}; use std::iter; diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 3b664363d232f..8d417290407ed 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -8,6 +8,7 @@ use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, use rustc_hir as hir; use rustc_hir::def::{self, CtorKind, Namespace, Res}; use rustc_hir::def_id::DefId; +use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::{ infer, traits::{self, Obligation}, @@ -25,7 +26,6 @@ use rustc_span::def_id::LocalDefId; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_target::spec::abi; -use rustc_trait_selection::autoderef::Autoderef; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::error_reporting::DefIdOrName; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 6347b9a69a007..27db4d27b6094 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -20,7 +20,7 @@ use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitable}; use rustc_session::Session; use rustc_span::symbol::Ident; -use rustc_span::{self, Span}; +use rustc_span::{self, Span, DUMMY_SP}; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; use std::cell::{Cell, RefCell}; @@ -175,6 +175,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn_sig }) }), + autoderef_steps: Box::new(|ty| { + let mut autoderef = self.autoderef(DUMMY_SP, ty).silence_errors(); + let mut steps = vec![]; + while let Some((ty, _)) = autoderef.next() { + steps.push((ty, autoderef.current_obligations())); + } + steps + }), } } diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 5d8383170f0dc..ba9c0c8d15e7c 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -9,6 +9,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::DefKind; +use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::OriginalQueryValues; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -29,7 +30,6 @@ use rustc_span::lev_distance::{ }; use rustc_span::symbol::sym; use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP}; -use rustc_trait_selection::autoderef::{self, Autoderef}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::query::method_autoderef::MethodAutoderefBadTy; use rustc_trait_selection::traits::query::method_autoderef::{ diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index a0f048fc09b9b..ae0df5aa8f1cf 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -3,6 +3,7 @@ use crate::{has_expected_num_generic_args, FnCtxt, PlaceOp}; use rustc_ast as ast; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::InferOk; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast}; @@ -10,7 +11,6 @@ use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutabili use rustc_middle::ty::{self, Ty}; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; -use rustc_trait_selection::autoderef::Autoderef; use std::slice; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 66db1a2f92890..0223979263d85 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -55,6 +55,7 @@ use crate::infer::ExpectedFound; use crate::traits::error_reporting::report_object_safety_error; use crate::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, + PredicateObligation, }; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; @@ -91,8 +92,12 @@ pub mod nice_region_error; pub struct TypeErrCtxt<'a, 'tcx> { pub infcx: &'a InferCtxt<'tcx>, pub typeck_results: Option>>, - pub normalize_fn_sig: Box) -> ty::PolyFnSig<'tcx> + 'a>, pub fallback_has_occurred: bool, + + pub normalize_fn_sig: Box) -> ty::PolyFnSig<'tcx> + 'a>, + + pub autoderef_steps: + Box) -> Vec<(Ty<'tcx>, Vec>)> + 'a>, } impl TypeErrCtxt<'_, '_> { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 6bef3f000a5ac..8825b5e12c3d9 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -688,6 +688,10 @@ impl<'tcx> InferCtxt<'tcx> { typeck_results: None, fallback_has_occurred: false, normalize_fn_sig: Box::new(|fn_sig| fn_sig), + autoderef_steps: Box::new(|ty| { + debug_assert!(false, "shouldn't be using autoderef_steps outside of typeck"); + vec![(ty, vec![])] + }), } } diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 19f404cb5b788..4405537c645a9 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1,7 +1,6 @@ use rustc_errors::{fluent, ErrorGuaranteed, Handler, IntoDiagnostic}; use rustc_macros::Diagnostic; use rustc_middle::ty::{self, PolyTraitRef, Ty}; -use rustc_session::Limit; use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] @@ -21,18 +20,6 @@ pub struct UnableToConstructConstantValue<'a> { pub unevaluated: ty::UnevaluatedConst<'a>, } -#[derive(Diagnostic)] -#[help] -#[diag(trait_selection_auto_deref_reached_recursion_limit, code = "E0055")] -pub struct AutoDerefReachedRecursionLimit<'a> { - #[primary_span] - #[label] - pub span: Span, - pub ty: Ty<'a>, - pub suggested_limit: Limit, - pub crate_name: Symbol, -} - #[derive(Diagnostic)] #[diag(trait_selection_empty_on_clause_in_rustc_on_unimplemented, code = "E0232")] pub struct EmptyOnClauseInOnUnimplemented { diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index a30d1df4ede52..081ac966c6961 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -35,7 +35,6 @@ extern crate rustc_middle; #[macro_use] extern crate smallvec; -pub mod autoderef; pub mod errors; pub mod infer; pub mod solve; diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 439854958270c..6c6696439c1b9 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -5,7 +5,6 @@ use super::{ PredicateObligation, }; -use crate::autoderef::Autoderef; use crate::infer::InferCtxt; use crate::traits::{NormalizeExt, ObligationCtxt}; @@ -750,26 +749,30 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } if let ty::Ref(region, base_ty, mutbl) = *real_ty.skip_binder().kind() { - let mut autoderef = Autoderef::new( - self, - obligation.param_env, - obligation.cause.body_id, - span, - base_ty, - ); - if let Some(steps) = autoderef.find_map(|(ty, steps)| { - // Re-add the `&` - let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl }); - - // Remapping bound vars here - let real_trait_pred_and_ty = - real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, ty)); - let obligation = self.mk_trait_obligation_with_new_self_ty( - obligation.param_env, - real_trait_pred_and_ty, - ); - Some(steps).filter(|_| self.predicate_may_hold(&obligation)) - }) { + let autoderef = (self.autoderef_steps)(base_ty); + if let Some(steps) = + autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| { + // Re-add the `&` + let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl }); + + // Remapping bound vars here + let real_trait_pred_and_ty = + real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, ty)); + let obligation = self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + real_trait_pred_and_ty, + ); + if obligations + .iter() + .chain([&obligation]) + .all(|obligation| self.predicate_may_hold(obligation)) + { + Some(steps) + } else { + None + } + }) + { if steps > 0 { // Don't care about `&mut` because `DerefMut` is used less // often and user will not expect autoderef happens. From 2024aa48b4e92894e6f780a13c03db7605e1a5f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 1 Jan 2023 18:43:47 -0800 Subject: [PATCH 08/19] Make `&`-removal suggestion verbose --- .../src/traits/error_reporting/suggestions.rs | 71 ++++++++++++------- .../impl-trait/in-trait/issue-102140.stderr | 12 ++-- tests/ui/not-panic/not-panic-safe.stderr | 5 +- .../suggestions/suggest-remove-refs-1.stderr | 10 +-- .../suggestions/suggest-remove-refs-2.stderr | 10 +-- .../suggestions/suggest-remove-refs-3.stderr | 20 +++--- .../suggestions/suggest-remove-refs-4.fixed | 5 ++ tests/ui/suggestions/suggest-remove-refs-4.rs | 5 ++ .../suggestions/suggest-remove-refs-4.stderr | 17 +++++ tests/ui/typeck/issue-57404.stderr | 11 +-- 10 files changed, 113 insertions(+), 53 deletions(-) create mode 100644 tests/ui/suggestions/suggest-remove-refs-4.fixed create mode 100644 tests/ui/suggestions/suggest-remove-refs-4.rs create mode 100644 tests/ui/suggestions/suggest-remove-refs-4.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 439854958270c..553144078ee4d 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1358,26 +1358,41 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { - let span = obligation.cause.span; - + let mut span = obligation.cause.span; + while span.desugaring_kind().is_some() { + // Remove all the hir desugaring contexts while maintaining the macro contexts. + span.remove_mark(); + } let mut suggested = false; - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - let refs_number = - snippet.chars().filter(|c| !c.is_whitespace()).take_while(|c| *c == '&').count(); - if let Some('\'') = snippet.chars().filter(|c| !c.is_whitespace()).nth(refs_number) { - // Do not suggest removal of borrow from type arguments. - return false; - } - // Skipping binder here, remapping below - let mut suggested_ty = trait_pred.self_ty().skip_binder(); + let mut expr_finder = super::FindExprBySpan { span, result: None }; + let Some(hir::Node::Expr(body)) = self.tcx.hir().find(obligation.cause.body_id) else { + return false; + }; + expr_finder.visit_expr(&body); + let mut count = 0; + let mut suggestions = vec![]; + let Some(mut expr) = expr_finder.result else { return false; }; + // Skipping binder here, remapping below + let mut suggested_ty = trait_pred.self_ty().skip_binder(); + + 'outer: loop { + while let hir::ExprKind::AddrOf(_, _, borrowed) = expr.kind { + count += 1; + let span = if expr.span.eq_ctxt(borrowed.span) { + expr.span.until(borrowed.span) + } else { + expr.span.with_hi(expr.span.lo() + BytePos(1)) + }; + suggestions.push((span, String::new())); - for refs_remaining in 0..refs_number { let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else { break; }; suggested_ty = *inner_ty; + expr = borrowed; + // Remapping bound vars here let trait_pred_and_suggested_ty = trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty)); @@ -1388,25 +1403,33 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { ); if self.predicate_may_hold(&new_obligation) { - let sp = self - .tcx - .sess - .source_map() - .span_take_while(span, |c| c.is_whitespace() || *c == '&'); - - let remove_refs = refs_remaining + 1; - - let msg = if remove_refs == 1 { + let msg = if count == 1 { "consider removing the leading `&`-reference".to_string() } else { - format!("consider removing {} leading `&`-references", remove_refs) + format!("consider removing {count} leading `&`-references") }; - err.span_suggestion_short(sp, &msg, "", Applicability::MachineApplicable); + err.multipart_suggestion_verbose( + &msg, + suggestions, + Applicability::MachineApplicable, + ); suggested = true; - break; + break 'outer; } } + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind + && let hir::def::Res::Local(hir_id) = path.res + && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(hir_id) + && let parent_hir_id = self.tcx.hir().get_parent_node(binding.hir_id) + && let Some(hir::Node::Local(local)) = self.tcx.hir().find(parent_hir_id) + && let None = local.ty + && let Some(binding_expr) = local.init + { + expr = binding_expr; + } else { + break 'outer; + } } suggested } diff --git a/tests/ui/impl-trait/in-trait/issue-102140.stderr b/tests/ui/impl-trait/in-trait/issue-102140.stderr index 08602185f50ca..18bb63745d7a5 100644 --- a/tests/ui/impl-trait/in-trait/issue-102140.stderr +++ b/tests/ui/impl-trait/in-trait/issue-102140.stderr @@ -2,11 +2,15 @@ error[E0277]: the trait bound `&dyn MyTrait: MyTrait` is not satisfied --> $DIR/issue-102140.rs:23:22 | LL | MyTrait::foo(&self) - | ------------ -^^^^ - | | | - | | the trait `MyTrait` is not implemented for `&dyn MyTrait` - | | help: consider removing the leading `&`-reference + | ------------ ^^^^^ the trait `MyTrait` is not implemented for `&dyn MyTrait` + | | | required by a bound introduced by this call + | +help: consider removing the leading `&`-reference + | +LL - MyTrait::foo(&self) +LL + MyTrait::foo(self) + | error[E0277]: the trait bound `&dyn MyTrait: MyTrait` is not satisfied --> $DIR/issue-102140.rs:23:9 diff --git a/tests/ui/not-panic/not-panic-safe.stderr b/tests/ui/not-panic/not-panic-safe.stderr index 2cd51a439988d..013c5ee704458 100644 --- a/tests/ui/not-panic/not-panic-safe.stderr +++ b/tests/ui/not-panic/not-panic-safe.stderr @@ -2,10 +2,7 @@ error[E0277]: the type `&mut i32` may not be safely transferred across an unwind --> $DIR/not-panic-safe.rs:8:14 | LL | assert::<&mut i32>(); - | -^^^^^^^ - | | - | `&mut i32` may not be safely transferred across an unwind boundary - | help: consider removing the leading `&`-reference + | ^^^^^^^^ `&mut i32` may not be safely transferred across an unwind boundary | = help: the trait `UnwindSafe` is not implemented for `&mut i32` = note: `UnwindSafe` is implemented for `&i32`, but not for `&mut i32` diff --git a/tests/ui/suggestions/suggest-remove-refs-1.stderr b/tests/ui/suggestions/suggest-remove-refs-1.stderr index 1a843f3f5097b..387770535f689 100644 --- a/tests/ui/suggestions/suggest-remove-refs-1.stderr +++ b/tests/ui/suggestions/suggest-remove-refs-1.stderr @@ -2,13 +2,15 @@ error[E0277]: `&Enumerate>` is not an iterator --> $DIR/suggest-remove-refs-1.rs:6:19 | LL | for (i, _) in &v.iter().enumerate() { - | -^^^^^^^^^^^^^^^^^^^^ - | | - | `&Enumerate>` is not an iterator - | help: consider removing the leading `&`-reference + | ^^^^^^^^^^^^^^^^^^^^^ `&Enumerate>` is not an iterator | = help: the trait `Iterator` is not implemented for `&Enumerate>` = note: required for `&Enumerate>` to implement `IntoIterator` +help: consider removing the leading `&`-reference + | +LL - for (i, _) in &v.iter().enumerate() { +LL + for (i, _) in v.iter().enumerate() { + | error: aborting due to previous error diff --git a/tests/ui/suggestions/suggest-remove-refs-2.stderr b/tests/ui/suggestions/suggest-remove-refs-2.stderr index f39361d529fbf..1632b2abb2f87 100644 --- a/tests/ui/suggestions/suggest-remove-refs-2.stderr +++ b/tests/ui/suggestions/suggest-remove-refs-2.stderr @@ -2,13 +2,15 @@ error[E0277]: `&&&&&Enumerate>` is not an iterat --> $DIR/suggest-remove-refs-2.rs:6:19 | LL | for (i, _) in & & & & &v.iter().enumerate() { - | ---------^^^^^^^^^^^^^^^^^^^^ - | | - | `&&&&&Enumerate>` is not an iterator - | help: consider removing 5 leading `&`-references + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&&&&&Enumerate>` is not an iterator | = help: the trait `Iterator` is not implemented for `&&&&&Enumerate>` = note: required for `&&&&&Enumerate>` to implement `IntoIterator` +help: consider removing 5 leading `&`-references + | +LL - for (i, _) in & & & & &v.iter().enumerate() { +LL + for (i, _) in v.iter().enumerate() { + | error: aborting due to previous error diff --git a/tests/ui/suggestions/suggest-remove-refs-3.stderr b/tests/ui/suggestions/suggest-remove-refs-3.stderr index 31cca323d0e42..7bf421a7729df 100644 --- a/tests/ui/suggestions/suggest-remove-refs-3.stderr +++ b/tests/ui/suggestions/suggest-remove-refs-3.stderr @@ -1,18 +1,20 @@ error[E0277]: `&&&&&Enumerate>` is not an iterator --> $DIR/suggest-remove-refs-3.rs:6:19 | -LL | for (i, _) in & & & - | ____________________^ - | | ___________________| - | || -LL | || & &v - | ||___________- help: consider removing 5 leading `&`-references -LL | | .iter() -LL | | .enumerate() { - | |_____________________^ `&&&&&Enumerate>` is not an iterator +LL | for (i, _) in & & & + | ___________________^ +LL | | & &v +LL | | .iter() +LL | | .enumerate() { + | |____________________^ `&&&&&Enumerate>` is not an iterator | = help: the trait `Iterator` is not implemented for `&&&&&Enumerate>` = note: required for `&&&&&Enumerate>` to implement `IntoIterator` +help: consider removing 5 leading `&`-references + | +LL - for (i, _) in & & & +LL + for (i, _) in v + | error: aborting due to previous error diff --git a/tests/ui/suggestions/suggest-remove-refs-4.fixed b/tests/ui/suggestions/suggest-remove-refs-4.fixed new file mode 100644 index 0000000000000..dd63d21597243 --- /dev/null +++ b/tests/ui/suggestions/suggest-remove-refs-4.fixed @@ -0,0 +1,5 @@ +// run-rustfix +fn main() { + let foo = [1,2,3].iter(); + for _i in foo {} //~ ERROR E0277 +} diff --git a/tests/ui/suggestions/suggest-remove-refs-4.rs b/tests/ui/suggestions/suggest-remove-refs-4.rs new file mode 100644 index 0000000000000..3c3d9b1b3f981 --- /dev/null +++ b/tests/ui/suggestions/suggest-remove-refs-4.rs @@ -0,0 +1,5 @@ +// run-rustfix +fn main() { + let foo = &[1,2,3].iter(); + for _i in &foo {} //~ ERROR E0277 +} diff --git a/tests/ui/suggestions/suggest-remove-refs-4.stderr b/tests/ui/suggestions/suggest-remove-refs-4.stderr new file mode 100644 index 0000000000000..e4ad17e06716f --- /dev/null +++ b/tests/ui/suggestions/suggest-remove-refs-4.stderr @@ -0,0 +1,17 @@ +error[E0277]: `&&std::slice::Iter<'_, {integer}>` is not an iterator + --> $DIR/suggest-remove-refs-4.rs:4:15 + | +LL | for _i in &foo {} + | ^^^^ `&&std::slice::Iter<'_, {integer}>` is not an iterator + | + = help: the trait `Iterator` is not implemented for `&&std::slice::Iter<'_, {integer}>` + = note: required for `&&std::slice::Iter<'_, {integer}>` to implement `IntoIterator` +help: consider removing 2 leading `&`-references + | +LL ~ let foo = [1,2,3].iter(); +LL ~ for _i in foo {} + | + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/typeck/issue-57404.stderr b/tests/ui/typeck/issue-57404.stderr index 5065ac32ad2b0..a631dbb39fb8f 100644 --- a/tests/ui/typeck/issue-57404.stderr +++ b/tests/ui/typeck/issue-57404.stderr @@ -2,14 +2,17 @@ error[E0277]: `&mut ()` is not a tuple --> $DIR/issue-57404.rs:6:41 | LL | handlers.unwrap().as_mut().call_mut(&mut ()); - | -------- -^^^^^^ - | | | - | | the trait `Tuple` is not implemented for `&mut ()` - | | help: consider removing the leading `&`-reference + | -------- ^^^^^^^ the trait `Tuple` is not implemented for `&mut ()` + | | | required by a bound introduced by this call | note: required by a bound in `call_mut` --> $SRC_DIR/core/src/ops/function.rs:LL:COL +help: consider removing the leading `&`-reference + | +LL - handlers.unwrap().as_mut().call_mut(&mut ()); +LL + handlers.unwrap().as_mut().call_mut(()); + | error: aborting due to previous error From ce83be4af8462d8d6fadb2829c8ca7f4353fca9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 1 Jan 2023 19:35:53 -0800 Subject: [PATCH 09/19] Account for type params --- .../src/traits/error_reporting/suggestions.rs | 88 +++++++++++++------ tests/ui/kindck/kindck-copy.stderr | 12 ++- tests/ui/not-panic/not-panic-safe.rs | 4 +- tests/ui/not-panic/not-panic-safe.stderr | 15 ++-- 4 files changed, 81 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 553144078ee4d..bcc8200480497 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1363,19 +1363,70 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Remove all the hir desugaring contexts while maintaining the macro contexts. span.remove_mark(); } - let mut suggested = false; - - let mut expr_finder = super::FindExprBySpan { span, result: None }; + let mut expr_finder = super::FindExprBySpan::new(span); let Some(hir::Node::Expr(body)) = self.tcx.hir().find(obligation.cause.body_id) else { return false; }; expr_finder.visit_expr(&body); + let mut maybe_suggest = |suggested_ty, count, suggestions| { + // Remapping bound vars here + let trait_pred_and_suggested_ty = + trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty)); + + let new_obligation = self.mk_trait_obligation_with_new_self_ty( + obligation.param_env, + trait_pred_and_suggested_ty, + ); + + if self.predicate_may_hold(&new_obligation) { + let msg = if count == 1 { + "consider removing the leading `&`-reference".to_string() + } else { + format!("consider removing {count} leading `&`-references") + }; + + err.multipart_suggestion_verbose( + &msg, + suggestions, + Applicability::MachineApplicable, + ); + true + } else { + false + } + }; + + // Maybe suggest removal of borrows from types in type parameters, like in + // `src/test/ui/not-panic/not-panic-safe.rs`. let mut count = 0; let mut suggestions = vec![]; - let Some(mut expr) = expr_finder.result else { return false; }; // Skipping binder here, remapping below let mut suggested_ty = trait_pred.self_ty().skip_binder(); + if let Some(mut hir_ty) = expr_finder.ty_result { + while let hir::TyKind::Ref(_, mut_ty) = &hir_ty.kind { + count += 1; + let span = hir_ty.span.until(mut_ty.ty.span); + suggestions.push((span, String::new())); + + let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else { + break; + }; + suggested_ty = *inner_ty; + + hir_ty = mut_ty.ty; + + if maybe_suggest(suggested_ty, count, suggestions.clone()) { + return true; + } + } + } + // Maybe suggest removal of borrows from expressions, like in `for i in &&&foo {}`. + let Some(mut expr) = expr_finder.result else { return false; }; + let mut count = 0; + let mut suggestions = vec![]; + // Skipping binder here, remapping below + let mut suggested_ty = trait_pred.self_ty().skip_binder(); 'outer: loop { while let hir::ExprKind::AddrOf(_, _, borrowed) = expr.kind { count += 1; @@ -1387,35 +1438,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { suggestions.push((span, String::new())); let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else { - break; + break 'outer; }; suggested_ty = *inner_ty; expr = borrowed; - // Remapping bound vars here - let trait_pred_and_suggested_ty = - trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty)); - - let new_obligation = self.mk_trait_obligation_with_new_self_ty( - obligation.param_env, - trait_pred_and_suggested_ty, - ); - - if self.predicate_may_hold(&new_obligation) { - let msg = if count == 1 { - "consider removing the leading `&`-reference".to_string() - } else { - format!("consider removing {count} leading `&`-references") - }; - - err.multipart_suggestion_verbose( - &msg, - suggestions, - Applicability::MachineApplicable, - ); - suggested = true; - break 'outer; + if maybe_suggest(suggested_ty, count, suggestions.clone()) { + return true; } } if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind @@ -1431,7 +1461,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { break 'outer; } } - suggested + false } fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) { diff --git a/tests/ui/kindck/kindck-copy.stderr b/tests/ui/kindck/kindck-copy.stderr index 9af89159a8cfc..aee2aa98a60c2 100644 --- a/tests/ui/kindck/kindck-copy.stderr +++ b/tests/ui/kindck/kindck-copy.stderr @@ -4,12 +4,16 @@ error[E0277]: the trait bound `&'static mut isize: Copy` is not satisfied LL | assert_copy::<&'static mut isize>(); | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `&'static mut isize` | - = help: the trait `Copy` is implemented for `isize` note: required by a bound in `assert_copy` --> $DIR/kindck-copy.rs:5:18 | LL | fn assert_copy() { } | ^^^^ required by this bound in `assert_copy` +help: consider removing the leading `&`-reference + | +LL - assert_copy::<&'static mut isize>(); +LL + assert_copy::(); + | error[E0277]: the trait bound `&'a mut isize: Copy` is not satisfied --> $DIR/kindck-copy.rs:28:19 @@ -17,12 +21,16 @@ error[E0277]: the trait bound `&'a mut isize: Copy` is not satisfied LL | assert_copy::<&'a mut isize>(); | ^^^^^^^^^^^^^ the trait `Copy` is not implemented for `&'a mut isize` | - = help: the trait `Copy` is implemented for `isize` note: required by a bound in `assert_copy` --> $DIR/kindck-copy.rs:5:18 | LL | fn assert_copy() { } | ^^^^ required by this bound in `assert_copy` +help: consider removing the leading `&`-reference + | +LL - assert_copy::<&'a mut isize>(); +LL + assert_copy::(); + | error[E0277]: the trait bound `Box: Copy` is not satisfied --> $DIR/kindck-copy.rs:31:19 diff --git a/tests/ui/not-panic/not-panic-safe.rs b/tests/ui/not-panic/not-panic-safe.rs index 4165c5dc13aaa..1b3c6482ce94f 100644 --- a/tests/ui/not-panic/not-panic-safe.rs +++ b/tests/ui/not-panic/not-panic-safe.rs @@ -5,6 +5,6 @@ use std::panic::UnwindSafe; fn assert() {} fn main() { - assert::<&mut i32>(); - //~^ ERROR the type `&mut i32` may not be safely transferred across an unwind boundary + assert::<&mut &mut &i32>(); + //~^ ERROR the type `&mut &mut &i32` may not be safely transferred across an unwind boundary } diff --git a/tests/ui/not-panic/not-panic-safe.stderr b/tests/ui/not-panic/not-panic-safe.stderr index 013c5ee704458..37a6aee390669 100644 --- a/tests/ui/not-panic/not-panic-safe.stderr +++ b/tests/ui/not-panic/not-panic-safe.stderr @@ -1,16 +1,21 @@ -error[E0277]: the type `&mut i32` may not be safely transferred across an unwind boundary +error[E0277]: the type `&mut &mut &i32` may not be safely transferred across an unwind boundary --> $DIR/not-panic-safe.rs:8:14 | -LL | assert::<&mut i32>(); - | ^^^^^^^^ `&mut i32` may not be safely transferred across an unwind boundary +LL | assert::<&mut &mut &i32>(); + | ^^^^^^^^^^^^^^ `&mut &mut &i32` may not be safely transferred across an unwind boundary | - = help: the trait `UnwindSafe` is not implemented for `&mut i32` - = note: `UnwindSafe` is implemented for `&i32`, but not for `&mut i32` + = help: the trait `UnwindSafe` is not implemented for `&mut &mut &i32` + = note: `UnwindSafe` is implemented for `&&mut &i32`, but not for `&mut &mut &i32` note: required by a bound in `assert` --> $DIR/not-panic-safe.rs:5:14 | LL | fn assert() {} | ^^^^^^^^^^ required by this bound in `assert` +help: consider removing 2 leading `&`-references + | +LL - assert::<&mut &mut &i32>(); +LL + assert::<&i32>(); + | error: aborting due to previous error From bb7211702e17dd2d10a6d04962d37331ea032010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 7 Jan 2023 19:35:40 +0000 Subject: [PATCH 10/19] fix rebase --- .../src/traits/error_reporting/suggestions.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index bcc8200480497..7e900fe0034b4 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1451,8 +1451,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let hir::def::Res::Local(hir_id) = path.res && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(hir_id) - && let parent_hir_id = self.tcx.hir().get_parent_node(binding.hir_id) - && let Some(hir::Node::Local(local)) = self.tcx.hir().find(parent_hir_id) + && let Some(hir::Node::Local(local)) = self.tcx.hir().find_parent(binding.hir_id) && let None = local.ty && let Some(binding_expr) = local.init { From 8b8cce16bfb74bcb06250ed3b9b3dc8c97e1ee4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 9 Jan 2023 04:43:18 +0000 Subject: [PATCH 11/19] Use the root trait predicate to determine whether to remove references Fix #84837. --- .../src/traits/error_reporting/suggestions.rs | 8 ++++ ...ypeck-default-trait-impl-precedence.stderr | 6 ++- tests/ui/not-panic/not-panic-safe-4.stderr | 10 +++++ .../suggestions/suggest-remove-refs-5.fixed | 8 ++++ tests/ui/suggestions/suggest-remove-refs-5.rs | 8 ++++ .../suggestions/suggest-remove-refs-5.stderr | 37 +++++++++++++++++++ 6 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 tests/ui/suggestions/suggest-remove-refs-5.fixed create mode 100644 tests/ui/suggestions/suggest-remove-refs-5.rs create mode 100644 tests/ui/suggestions/suggest-remove-refs-5.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 7e900fe0034b4..678e7b8782989 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1359,6 +1359,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { let mut span = obligation.cause.span; + let mut trait_pred = trait_pred; + let mut code = obligation.cause.code(); + while let Some((c, Some(parent_trait_pred))) = code.parent() { + // We want the root obligation, in order to detect properly handle + // `for _ in &mut &mut vec![] {}`. + code = c; + trait_pred = parent_trait_pred; + } while span.desugaring_kind().is_some() { // Remove all the hir desugaring contexts while maintaining the macro contexts. span.remove_mark(); diff --git a/tests/ui/auto-traits/typeck-default-trait-impl-precedence.stderr b/tests/ui/auto-traits/typeck-default-trait-impl-precedence.stderr index ce7095664c11a..32256ed69a769 100644 --- a/tests/ui/auto-traits/typeck-default-trait-impl-precedence.stderr +++ b/tests/ui/auto-traits/typeck-default-trait-impl-precedence.stderr @@ -4,7 +4,6 @@ error[E0277]: the trait bound `u32: Signed` is not satisfied LL | is_defaulted::<&'static u32>(); | ^^^^^^^^^^^^ the trait `Signed` is not implemented for `u32` | - = help: the trait `Signed` is implemented for `i32` note: required for `&'static u32` to implement `Defaulted` --> $DIR/typeck-default-trait-impl-precedence.rs:10:19 | @@ -15,6 +14,11 @@ note: required by a bound in `is_defaulted` | LL | fn is_defaulted() { } | ^^^^^^^^^ required by this bound in `is_defaulted` +help: consider removing the leading `&`-reference + | +LL - is_defaulted::<&'static u32>(); +LL + is_defaulted::(); + | error: aborting due to previous error diff --git a/tests/ui/not-panic/not-panic-safe-4.stderr b/tests/ui/not-panic/not-panic-safe-4.stderr index fc1c594d0d422..9428c125651ec 100644 --- a/tests/ui/not-panic/not-panic-safe-4.stderr +++ b/tests/ui/not-panic/not-panic-safe-4.stderr @@ -12,6 +12,11 @@ note: required by a bound in `assert` | LL | fn assert() {} | ^^^^^^^^^^ required by this bound in `assert` +help: consider removing the leading `&`-reference + | +LL - assert::<&RefCell>(); +LL + assert::>(); + | error[E0277]: the type `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary --> $DIR/not-panic-safe-4.rs:9:14 @@ -28,6 +33,11 @@ note: required by a bound in `assert` | LL | fn assert() {} | ^^^^^^^^^^ required by this bound in `assert` +help: consider removing the leading `&`-reference + | +LL - assert::<&RefCell>(); +LL + assert::>(); + | error: aborting due to 2 previous errors diff --git a/tests/ui/suggestions/suggest-remove-refs-5.fixed b/tests/ui/suggestions/suggest-remove-refs-5.fixed new file mode 100644 index 0000000000000..9f59f9c199a33 --- /dev/null +++ b/tests/ui/suggestions/suggest-remove-refs-5.fixed @@ -0,0 +1,8 @@ +// run-rustfix +fn main() { + let v = &mut Vec::::new(); + for _ in v {} //~ ERROR E0277 + + let v = &mut [1u8]; + for _ in v {} //~ ERROR E0277 +} diff --git a/tests/ui/suggestions/suggest-remove-refs-5.rs b/tests/ui/suggestions/suggest-remove-refs-5.rs new file mode 100644 index 0000000000000..d56aa0c9ca479 --- /dev/null +++ b/tests/ui/suggestions/suggest-remove-refs-5.rs @@ -0,0 +1,8 @@ +// run-rustfix +fn main() { + let v = &mut &mut Vec::::new(); + for _ in &mut &mut v {} //~ ERROR E0277 + + let v = &mut &mut [1u8]; + for _ in &mut v {} //~ ERROR E0277 +} diff --git a/tests/ui/suggestions/suggest-remove-refs-5.stderr b/tests/ui/suggestions/suggest-remove-refs-5.stderr new file mode 100644 index 0000000000000..7de84d6122b58 --- /dev/null +++ b/tests/ui/suggestions/suggest-remove-refs-5.stderr @@ -0,0 +1,37 @@ +error[E0277]: `Vec` is not an iterator + --> $DIR/suggest-remove-refs-5.rs:4:14 + | +LL | for _ in &mut &mut v {} + | ^^^^^^^^^^^ `Vec` is not an iterator; try calling `.into_iter()` or `.iter()` + | + = help: the trait `Iterator` is not implemented for `Vec` + = note: required for `&mut Vec` to implement `Iterator` + = note: 3 redundant requirements hidden + = note: required for `&mut &mut &mut &mut Vec` to implement `Iterator` + = note: required for `&mut &mut &mut &mut Vec` to implement `IntoIterator` +help: consider removing 3 leading `&`-references + | +LL ~ let v = &mut Vec::::new(); +LL ~ for _ in v {} + | + +error[E0277]: `[u8; 1]` is not an iterator + --> $DIR/suggest-remove-refs-5.rs:7:14 + | +LL | for _ in &mut v {} + | ^^^^^^ `[u8; 1]` is not an iterator; try calling `.into_iter()` or `.iter()` + | + = help: the trait `Iterator` is not implemented for `[u8; 1]` + = note: required for `&mut [u8; 1]` to implement `Iterator` + = note: 2 redundant requirements hidden + = note: required for `&mut &mut &mut [u8; 1]` to implement `Iterator` + = note: required for `&mut &mut &mut [u8; 1]` to implement `IntoIterator` +help: consider removing 2 leading `&`-references + | +LL ~ let v = &mut [1u8]; +LL ~ for _ in v {} + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. From 48b7e2a5b93e01fbf30484a8dd4f8ead24db0196 Mon Sep 17 00:00:00 2001 From: Daniel Henry-Mantilla Date: Mon, 31 Oct 2022 13:07:40 +0100 Subject: [PATCH 12/19] Stabilize `::{core,std}::pin::pin!` --- library/core/src/pin.rs | 10 +++------- library/core/tests/lib.rs | 1 - src/tools/miri/tests/pass/issues/issue-miri-2068.rs | 2 -- .../pass/stacked-borrows/future-self-referential.rs | 2 -- tests/ui/pin-macro/cant_access_internals.rs | 1 - tests/ui/pin-macro/cant_access_internals.stderr | 2 +- .../pin-macro/lifetime_errors_on_promotion_misusage.rs | 1 - .../lifetime_errors_on_promotion_misusage.stderr | 4 ++-- 8 files changed, 6 insertions(+), 17 deletions(-) diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 3f8acc8505ff1..2eb29d4f9c574 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -622,9 +622,8 @@ impl Pin

{ /// that the closure is pinned. /// /// The better alternative is to avoid all that trouble and do the pinning in the outer function - /// instead (here using the unstable `pin` macro): + /// instead (here using the [`pin!`][crate::pin::pin] macro): /// ``` - /// #![feature(pin_macro)] /// use std::pin::pin; /// use std::task::Context; /// use std::future::Future; @@ -1026,7 +1025,6 @@ impl DispatchFromDyn> for Pin

where P: DispatchFromDyn {} /// ### Basic usage /// /// ```rust -/// #![feature(pin_macro)] /// # use core::marker::PhantomPinned as Foo; /// use core::pin::{pin, Pin}; /// @@ -1044,7 +1042,6 @@ impl DispatchFromDyn> for Pin

where P: DispatchFromDyn {} /// ### Manually polling a `Future` (without `Unpin` bounds) /// /// ```rust -/// #![feature(pin_macro)] /// use std::{ /// future::Future, /// pin::pin, @@ -1083,7 +1080,7 @@ impl DispatchFromDyn> for Pin

where P: DispatchFromDyn {} /// ### With `Generator`s /// /// ```rust -/// #![feature(generators, generator_trait, pin_macro)] +/// #![feature(generators, generator_trait)] /// use core::{ /// ops::{Generator, GeneratorState}, /// pin::pin, @@ -1126,7 +1123,6 @@ impl DispatchFromDyn> for Pin

where P: DispatchFromDyn {} /// The following, for instance, fails to compile: /// /// ```rust,compile_fail -/// #![feature(pin_macro)] /// use core::pin::{pin, Pin}; /// # use core::{marker::PhantomPinned as Foo, mem::drop as stuff}; /// @@ -1168,7 +1164,7 @@ impl DispatchFromDyn> for Pin

where P: DispatchFromDyn {} /// constructor. /// /// [`Box::pin`]: ../../std/boxed/struct.Box.html#method.pin -#[unstable(feature = "pin_macro", issue = "93178")] +#[stable(feature = "pin_macro", since = "CURRENT_RUSTC_VERSION")] #[rustc_macro_transparency = "semitransparent"] #[allow_internal_unstable(unsafe_pin_internals)] pub macro pin($value:expr $(,)?) { diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index c910cb65c55de..42a26ae1675c1 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -48,7 +48,6 @@ #![feature(is_sorted)] #![feature(layout_for_ptr)] #![feature(pattern)] -#![feature(pin_macro)] #![feature(sort_internals)] #![feature(slice_take)] #![feature(slice_from_ptr_range)] diff --git a/src/tools/miri/tests/pass/issues/issue-miri-2068.rs b/src/tools/miri/tests/pass/issues/issue-miri-2068.rs index 7576ba78f607d..fe4078f77107b 100644 --- a/src/tools/miri/tests/pass/issues/issue-miri-2068.rs +++ b/src/tools/miri/tests/pass/issues/issue-miri-2068.rs @@ -1,5 +1,3 @@ -#![feature(pin_macro)] - use core::future::Future; use core::pin::Pin; use core::task::{Context, Poll}; diff --git a/src/tools/miri/tests/pass/stacked-borrows/future-self-referential.rs b/src/tools/miri/tests/pass/stacked-borrows/future-self-referential.rs index 3ba21552fd362..96fc0be344dbf 100644 --- a/src/tools/miri/tests/pass/stacked-borrows/future-self-referential.rs +++ b/src/tools/miri/tests/pass/stacked-borrows/future-self-referential.rs @@ -1,5 +1,3 @@ -#![feature(pin_macro)] - use std::future::*; use std::marker::PhantomPinned; use std::pin::*; diff --git a/tests/ui/pin-macro/cant_access_internals.rs b/tests/ui/pin-macro/cant_access_internals.rs index 120d08894f8f7..5826a18b5718b 100644 --- a/tests/ui/pin-macro/cant_access_internals.rs +++ b/tests/ui/pin-macro/cant_access_internals.rs @@ -1,5 +1,4 @@ // edition:2018 -#![feature(pin_macro)] use core::{ marker::PhantomPinned, diff --git a/tests/ui/pin-macro/cant_access_internals.stderr b/tests/ui/pin-macro/cant_access_internals.stderr index 060c9c48c21c8..d43027657f046 100644 --- a/tests/ui/pin-macro/cant_access_internals.stderr +++ b/tests/ui/pin-macro/cant_access_internals.stderr @@ -1,5 +1,5 @@ error[E0658]: use of unstable library feature 'unsafe_pin_internals' - --> $DIR/cant_access_internals.rs:12:15 + --> $DIR/cant_access_internals.rs:11:15 | LL | mem::take(phantom_pinned.pointer); | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.rs b/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.rs index ca2b6cf759376..59774bc753dc9 100644 --- a/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.rs +++ b/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.rs @@ -1,5 +1,4 @@ // edition:2018 -#![feature(pin_macro)] use core::{ convert::identity, diff --git a/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr b/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr index fc1be052fb791..4ecc6370d3caa 100644 --- a/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr +++ b/tests/ui/pin-macro/lifetime_errors_on_promotion_misusage.stderr @@ -1,5 +1,5 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/lifetime_errors_on_promotion_misusage.rs:12:35 + --> $DIR/lifetime_errors_on_promotion_misusage.rs:11:35 | LL | let phantom_pinned = identity(pin!(PhantomPinned)); | ^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement @@ -13,7 +13,7 @@ LL | stuff(phantom_pinned) = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0716]: temporary value dropped while borrowed - --> $DIR/lifetime_errors_on_promotion_misusage.rs:19:30 + --> $DIR/lifetime_errors_on_promotion_misusage.rs:18:30 | LL | let phantom_pinned = { | -------------- borrow later stored here From 519b1abd198255a6f6c69d80c20b57729622d61a Mon Sep 17 00:00:00 2001 From: mejrs <> Date: Tue, 20 Dec 2022 00:28:33 +0100 Subject: [PATCH 13/19] Translate const_to_pat.rs --- .../locales/en-US/mir_build.ftl | 19 ++ compiler/rustc_mir_build/src/errors.rs | 51 +++++ .../src/thir/pattern/const_to_pat.rs | 190 +++++------------- 3 files changed, 122 insertions(+), 138 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl index 60d3d3e69abbe..4faaffce7bb35 100644 --- a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl +++ b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl @@ -299,3 +299,22 @@ mir_build_multiple_mut_borrows = cannot borrow value as mutable more than once a .mutable_borrow = another mutable borrow, by `{$name_mut}`, occurs here .immutable_borrow = also borrowed as immutable, by `{$name_immut}`, here .moved = also moved into `{$name_moved}` here + +mir_build_union_pattern = cannot use unions in constant patterns + +mir_build_type_not_structural = + to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]` + +mir_build_unsized_pattern = cannot use unsized non-slice type `{$non_sm_ty}` in constant patterns + +mir_build_invalid_pattern = `{$non_sm_ty}` cannot be used in patterns + +mir_build_float_pattern = floating-point types cannot be used in patterns + +mir_build_pointer_pattern = function pointers and unsized pointers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + +mir_build_indirect_structural_match = + to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]` + +mir_build_nontrivial_structural_match = + to use a constant of type `{$non_sm_ty}` in a pattern, the constant's initializer must be trivial or `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]` \ No newline at end of file diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 68179001b916d..840a1faf95a26 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -614,3 +614,54 @@ pub enum MultipleMutBorrowOccurence { name_moved: Ident, }, } + +#[derive(Diagnostic)] +#[diag(mir_build_union_pattern)] +pub struct UnionPattern { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(mir_build_type_not_structural)] +pub struct TypeNotStructural<'tcx> { + #[primary_span] + pub span: Span, + pub non_sm_ty: Ty<'tcx>, +} + +#[derive(Diagnostic)] +#[diag(mir_build_invalid_pattern)] +pub struct InvalidPattern<'tcx> { + #[primary_span] + pub span: Span, + pub non_sm_ty: Ty<'tcx>, +} + +#[derive(Diagnostic)] +#[diag(mir_build_unsized_pattern)] +pub struct UnsizedPattern<'tcx> { + #[primary_span] + pub span: Span, + pub non_sm_ty: Ty<'tcx>, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_float_pattern)] +pub struct FloatPattern; + +#[derive(LintDiagnostic)] +#[diag(mir_build_pointer_pattern)] +pub struct PointerPattern; + +#[derive(LintDiagnostic)] +#[diag(mir_build_indirect_structural_match)] +pub struct IndirectStructuralMatch<'tcx> { + pub non_sm_ty: Ty<'tcx>, +} + +#[derive(LintDiagnostic)] +#[diag(mir_build_nontrivial_structural_match)] +pub struct NontrivialStructuralMatch<'tcx> { + pub non_sm_ty: Ty<'tcx>, +} diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 6470efab2e93c..8e7ca32feb346 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -1,11 +1,9 @@ -use rustc_errors::DelayDm; use rustc_hir as hir; use rustc_index::vec::Idx; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_middle::mir::{self, Field}; use rustc_middle::thir::{FieldPat, Pat, PatKind}; -use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::lint; use rustc_span::Span; use rustc_trait_selection::traits::predicate_for_trait_def; @@ -15,6 +13,10 @@ use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation}; use std::cell::Cell; use super::PatCtxt; +use crate::errors::{ + FloatPattern, IndirectStructuralMatch, InvalidPattern, NontrivialStructuralMatch, + PointerPattern, TypeNotStructural, UnionPattern, UnsizedPattern, +}; impl<'a, 'tcx> PatCtxt<'a, 'tcx> { /// Converts an evaluated constant to a pattern (if possible). @@ -105,47 +107,6 @@ impl<'tcx> ConstToPat<'tcx> { self.infcx.tcx } - fn adt_derive_msg(&self, adt_def: AdtDef<'tcx>) -> String { - let path = self.tcx().def_path_str(adt_def.did()); - format!( - "to use a constant of type `{}` in a pattern, \ - `{}` must be annotated with `#[derive(PartialEq, Eq)]`", - path, path, - ) - } - - fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option { - traits::search_for_structural_match_violation(self.span, self.tcx(), ty).map(|non_sm_ty| { - with_no_trimmed_paths!(match non_sm_ty.kind() { - ty::Adt(adt, _) => self.adt_derive_msg(*adt), - ty::Dynamic(..) => { - "trait objects cannot be used in patterns".to_string() - } - ty::Alias(ty::Opaque, ..) => { - "opaque types cannot be used in patterns".to_string() - } - ty::Closure(..) => { - "closures cannot be used in patterns".to_string() - } - ty::Generator(..) | ty::GeneratorWitness(..) => { - "generators cannot be used in patterns".to_string() - } - ty::Float(..) => { - "floating-point numbers cannot be used in patterns".to_string() - } - ty::FnPtr(..) => { - "function pointers cannot be used in patterns".to_string() - } - ty::RawPtr(..) => { - "raw pointers cannot be used in patterns".to_string() - } - _ => { - bug!("use of a value of `{non_sm_ty}` inside a pattern") - } - }) - }) - } - fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool { ty.is_structural_eq_shallow(self.infcx.tcx) } @@ -176,7 +137,8 @@ impl<'tcx> ConstToPat<'tcx> { // If we were able to successfully convert the const to some pat, // double-check that all types in the const implement `Structural`. - let structural = self.search_for_structural_match_violation(cv.ty()); + let structural = + traits::search_for_structural_match_violation(self.span, self.tcx(), cv.ty()); debug!( "search_for_structural_match_violation cv.ty: {:?} returned: {:?}", cv.ty(), @@ -194,17 +156,18 @@ impl<'tcx> ConstToPat<'tcx> { return inlined_const_as_pat; } - if let Some(msg) = structural { + if let Some(non_sm_ty) = structural { if !self.type_may_have_partial_eq_impl(cv.ty()) { - // span_fatal avoids ICE from resolution of non-existent method (rare case). - self.tcx().sess.span_fatal(self.span, &msg); + // fatal avoids ICE from resolution of non-existent method (rare case). + self.tcx() + .sess + .emit_fatal(TypeNotStructural { span: self.span, non_sm_ty: non_sm_ty }); } else if mir_structural_match_violation && !self.saw_const_match_lint.get() { - self.tcx().struct_span_lint_hir( + self.tcx().emit_spanned_lint( lint::builtin::INDIRECT_STRUCTURAL_MATCH, self.id, self.span, - msg, - |lint| lint, + IndirectStructuralMatch { non_sm_ty }, ); } else { debug!( @@ -278,12 +241,11 @@ impl<'tcx> ConstToPat<'tcx> { let kind = match cv.ty().kind() { ty::Float(_) => { if self.include_lint_checks { - tcx.struct_span_lint_hir( + tcx.emit_spanned_lint( lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN, id, span, - "floating-point types cannot be used in patterns", - |lint| lint, + FloatPattern, ); } PatKind::Constant { value: cv } @@ -291,29 +253,22 @@ impl<'tcx> ConstToPat<'tcx> { ty::Adt(adt_def, _) if adt_def.is_union() => { // Matching on union fields is unsafe, we can't hide it in constants self.saw_const_match_error.set(true); - let msg = "cannot use unions in constant patterns"; - if self.include_lint_checks { - tcx.sess.span_err(span, msg); - } else { - tcx.sess.delay_span_bug(span, msg); - } + let err = UnionPattern { span }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); PatKind::Wild } ty::Adt(..) if !self.type_may_have_partial_eq_impl(cv.ty()) // FIXME(#73448): Find a way to bring const qualification into parity with // `search_for_structural_match_violation` and then remove this condition. - && self.search_for_structural_match_violation(cv.ty()).is_some() => + + // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we + // could get `Option`, even though `Option` is annotated with derive. + && let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty()) => { - // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we - // could get `Option`, even though `Option` is annotated with derive. - let msg = self.search_for_structural_match_violation(cv.ty()).unwrap(); self.saw_const_match_error.set(true); - if self.include_lint_checks { - tcx.sess.span_err(self.span, &msg); - } else { - tcx.sess.delay_span_bug(self.span, &msg); - } + let err = TypeNotStructural { span, non_sm_ty }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); PatKind::Wild } // If the type is not structurally comparable, just emit the constant directly, @@ -331,19 +286,11 @@ impl<'tcx> ConstToPat<'tcx> { && !self.saw_const_match_lint.get() { self.saw_const_match_lint.set(true); - tcx.struct_span_lint_hir( + tcx.emit_spanned_lint( lint::builtin::INDIRECT_STRUCTURAL_MATCH, id, span, - DelayDm(|| { - format!( - "to use a constant of type `{}` in a pattern, \ - `{}` must be annotated with `#[derive(PartialEq, Eq)]`", - cv.ty(), - cv.ty(), - ) - }), - |lint| lint, + IndirectStructuralMatch { non_sm_ty: cv.ty() }, ); } // Since we are behind a reference, we can just bubble the error up so we get a @@ -357,18 +304,9 @@ impl<'tcx> ConstToPat<'tcx> { adt_def, cv.ty() ); - let path = tcx.def_path_str(adt_def.did()); - let msg = format!( - "to use a constant of type `{}` in a pattern, \ - `{}` must be annotated with `#[derive(PartialEq, Eq)]`", - path, path, - ); self.saw_const_match_error.set(true); - if self.include_lint_checks { - tcx.sess.span_err(span, &msg); - } else { - tcx.sess.delay_span_bug(span, &msg); - } + let err = TypeNotStructural { span, non_sm_ty: cv.ty() }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); PatKind::Wild } ty::Adt(adt_def, substs) if adt_def.is_enum() => { @@ -401,12 +339,8 @@ impl<'tcx> ConstToPat<'tcx> { // These are not allowed and will error elsewhere anyway. ty::Dynamic(..) => { self.saw_const_match_error.set(true); - let msg = format!("`{}` cannot be used in patterns", cv.ty()); - if self.include_lint_checks { - tcx.sess.span_err(span, &msg); - } else { - tcx.sess.delay_span_bug(span, &msg); - } + let err = InvalidPattern { span, non_sm_ty: cv.ty() }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); PatKind::Wild } // `&str` is represented as `ConstValue::Slice`, let's keep using this @@ -471,32 +405,26 @@ impl<'tcx> ConstToPat<'tcx> { // this pattern to a `PartialEq::eq` comparison and `PartialEq::eq` takes a // reference. This makes the rest of the matching logic simpler as it doesn't have // to figure out how to get a reference again. - ty::Adt(adt_def, _) if !self.type_marked_structural(*pointee_ty) => { + ty::Adt(_, _) if !self.type_marked_structural(*pointee_ty) => { if self.behind_reference.get() { if self.include_lint_checks && !self.saw_const_match_error.get() && !self.saw_const_match_lint.get() { - self.saw_const_match_lint.set(true); - let msg = self.adt_derive_msg(adt_def); - self.tcx().struct_span_lint_hir( + self.saw_const_match_lint.set(true); + tcx.emit_spanned_lint( lint::builtin::INDIRECT_STRUCTURAL_MATCH, self.id, - self.span, - msg, - |lint| lint, + span, + IndirectStructuralMatch { non_sm_ty: *pointee_ty }, ); } PatKind::Constant { value: cv } } else { if !self.saw_const_match_error.get() { self.saw_const_match_error.set(true); - let msg = self.adt_derive_msg(adt_def); - if self.include_lint_checks { - tcx.sess.span_err(span, &msg); - } else { - tcx.sess.delay_span_bug(span, &msg); - } + let err = TypeNotStructural { span, non_sm_ty: *pointee_ty }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); } PatKind::Wild } @@ -507,13 +435,11 @@ impl<'tcx> ConstToPat<'tcx> { _ => { if !pointee_ty.is_sized(tcx, param_env) { // `tcx.deref_mir_constant()` below will ICE with an unsized type - // (except slices, which are handled in a separate arm above). - let msg = format!("cannot use unsized non-slice type `{}` in constant patterns", pointee_ty); - if self.include_lint_checks { - tcx.sess.span_err(span, &msg); - } else { - tcx.sess.delay_span_bug(span, &msg); - } + // (except slices, which are handled in a separate arm above). + + let err = UnsizedPattern { span, non_sm_ty: *pointee_ty }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); + PatKind::Wild } else { let old = self.behind_reference.replace(true); @@ -545,27 +471,19 @@ impl<'tcx> ConstToPat<'tcx> { && !self.saw_const_match_lint.get() { self.saw_const_match_lint.set(true); - let msg = "function pointers and unsized pointers in patterns behave \ - unpredictably and should not be relied upon. \ - See https://github.com/rust-lang/rust/issues/70861 for details."; - tcx.struct_span_lint_hir( + tcx.emit_spanned_lint( lint::builtin::POINTER_STRUCTURAL_MATCH, id, span, - msg, - |lint| lint, + PointerPattern ); } PatKind::Constant { value: cv } } _ => { self.saw_const_match_error.set(true); - let msg = format!("`{}` cannot be used in patterns", cv.ty()); - if self.include_lint_checks { - tcx.sess.span_err(span, &msg); - } else { - tcx.sess.delay_span_bug(span, &msg); - } + let err = InvalidPattern { span, non_sm_ty: cv.ty() }; + tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); PatKind::Wild } }; @@ -576,21 +494,17 @@ impl<'tcx> ConstToPat<'tcx> { && mir_structural_match_violation // FIXME(#73448): Find a way to bring const qualification into parity with // `search_for_structural_match_violation` and then remove this condition. - && self.search_for_structural_match_violation(cv.ty()).is_some() - { - self.saw_const_match_lint.set(true); + // Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we // could get `Option`, even though `Option` is annotated with derive. - let msg = self.search_for_structural_match_violation(cv.ty()).unwrap().replace( - "in a pattern,", - "in a pattern, the constant's initializer must be trivial or", - ); - tcx.struct_span_lint_hir( + && let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty()) + { + self.saw_const_match_lint.set(true); + tcx.emit_spanned_lint( lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH, id, span, - msg, - |lint| lint, + NontrivialStructuralMatch {non_sm_ty} ); } From 5d2b9a9ed09ac8a4cb4b7977cdc513fe1cf56408 Mon Sep 17 00:00:00 2001 From: mejrs <> Date: Tue, 20 Dec 2022 16:43:34 +0100 Subject: [PATCH 14/19] Migrate deconstruct_pat.rs --- .../locales/en-US/mir_build.ftl | 8 ++++- compiler/rustc_middle/src/thir.rs | 7 ++++ compiler/rustc_mir_build/src/errors.rs | 20 +++++++++++ .../src/thir/pattern/deconstruct_pat.rs | 35 ++++++++----------- .../overlapping_range_endpoints.stderr | 1 - 5 files changed, 48 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl index 4faaffce7bb35..162cfce0a2416 100644 --- a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl +++ b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl @@ -317,4 +317,10 @@ mir_build_indirect_structural_match = to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]` mir_build_nontrivial_structural_match = - to use a constant of type `{$non_sm_ty}` in a pattern, the constant's initializer must be trivial or `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]` \ No newline at end of file + to use a constant of type `{$non_sm_ty}` in a pattern, the constant's initializer must be trivial or `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]` + +mir_build_overlapping_range_endpoints = multiple patterns overlap on their endpoints + .range = ... with this range + .note = you likely meant to write mutually exclusive ranges + +mir_build_overlapping_range = this range overlaps on `{$range}`... diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index ac903010c8d31..5f320708c8416 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -9,6 +9,7 @@ //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/thir.html use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece}; +use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::RangeEnd; @@ -575,6 +576,12 @@ impl<'tcx> Pat<'tcx> { } } +impl<'tcx> IntoDiagnosticArg for Pat<'tcx> { + fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { + format!("{}", self).into_diagnostic_arg() + } +} + #[derive(Clone, Debug, HashStable)] pub struct Ascription<'tcx> { pub annotation: CanonicalUserTypeAnnotation<'tcx>, diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 840a1faf95a26..6bb15730b0069 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -4,6 +4,7 @@ use rustc_errors::{ error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, MultiSpan, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; +use rustc_middle::thir::Pat; use rustc_middle::ty::{self, Ty}; use rustc_span::{symbol::Ident, Span}; @@ -665,3 +666,22 @@ pub struct IndirectStructuralMatch<'tcx> { pub struct NontrivialStructuralMatch<'tcx> { pub non_sm_ty: Ty<'tcx>, } + +#[derive(LintDiagnostic)] +#[diag(mir_build_overlapping_range_endpoints)] +#[note] +pub struct OverlappingRangeEndpoints<'tcx> { + #[label(range)] + pub range: Span, + #[subdiagnostic] + pub overlap: Overlap<'tcx>, +} + +#[derive(Debug)] +#[derive(Subdiagnostic)] +#[label(mir_build_overlapping_range)] +pub struct Overlap<'tcx> { + #[primary_span] + pub span: Span, + pub range: Pat<'tcx>, +} diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index a95349d767098..323df1b8147d8 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -67,6 +67,7 @@ use self::SliceKind::*; use super::compare_const_vals; use super::usefulness::{MatchCheckCtxt, PatCtxt}; +use crate::errors::{Overlap, OverlappingRangeEndpoints}; /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns. fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { @@ -96,7 +97,7 @@ fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { /// `IntRange` is never used to encode an empty range or a "range" that wraps /// around the (offset) space: i.e., `range.lo <= range.hi`. #[derive(Clone, PartialEq, Eq)] -pub(super) struct IntRange { +pub(crate) struct IntRange { range: RangeInclusive, /// Keeps the bias used for encoding the range. It depends on the type of the range and /// possibly the pointer size of the current architecture. The algorithm ensures we never @@ -284,32 +285,24 @@ impl IntRange { return; } - let overlaps: Vec<_> = pats + // Get the first overlap. We get only the first rather than all of them + // because displaying multiple overlaps requires a way to eagerly translate + // lintdiagnostics, but that doesn't exist. + let overlap = pats .filter_map(|pat| Some((pat.ctor().as_int_range()?, pat.span()))) .filter(|(range, _)| self.suspicious_intersection(range)) - .map(|(range, span)| (self.intersection(&range).unwrap(), span)) - .collect(); + .map(|(range, span)| Overlap { + range: self.intersection(&range).unwrap().to_pat(pcx.cx.tcx, pcx.ty), + span, + }) + .next(); - if !overlaps.is_empty() { - pcx.cx.tcx.struct_span_lint_hir( + if let Some(overlap) = overlap { + pcx.cx.tcx.emit_spanned_lint( lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, hir_id, pcx.span, - "multiple patterns overlap on their endpoints", - |lint| { - for (int_range, span) in overlaps { - lint.span_label( - span, - &format!( - "this range overlaps on `{}`...", - int_range.to_pat(pcx.cx.tcx, pcx.ty) - ), - ); - } - lint.span_label(pcx.span, "... with this range"); - lint.note("you likely meant to write mutually exclusive ranges"); - lint - }, + OverlappingRangeEndpoints { overlap, range: pcx.span }, ); } } diff --git a/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr index ea0e8f6e49e07..a1c802add170c 100644 --- a/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr +++ b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr @@ -59,7 +59,6 @@ error: multiple patterns overlap on their endpoints LL | 0..=10 => {} | ------ this range overlaps on `10_u8`... LL | 20..=30 => {} - | ------- this range overlaps on `20_u8`... LL | 10..=20 => {} | ^^^^^^^ ... with this range | From ef3307289056ac3151a1a6eb29065932e5326bcf Mon Sep 17 00:00:00 2001 From: mejrs <> Date: Tue, 20 Dec 2022 21:24:18 +0100 Subject: [PATCH 15/19] Migrate usefulness.rs --- .../locales/en-US/mir_build.ftl | 14 +++++++++++-- compiler/rustc_mir_build/src/errors.rs | 14 +++++++++++++ .../src/thir/pattern/const_to_pat.rs | 2 +- .../rustc_mir_build/src/thir/pattern/mod.rs | 2 +- .../src/thir/pattern/usefulness.rs | 21 +++++++++++++++++-- 5 files changed, 47 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl index 162cfce0a2416..9f10dc4b634c0 100644 --- a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl +++ b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl @@ -313,10 +313,10 @@ mir_build_float_pattern = floating-point types cannot be used in patterns mir_build_pointer_pattern = function pointers and unsized pointers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. -mir_build_indirect_structural_match = +mir_build_indirect_structural_match = to use a constant of type `{$non_sm_ty}` in a pattern, `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]` -mir_build_nontrivial_structural_match = +mir_build_nontrivial_structural_match = to use a constant of type `{$non_sm_ty}` in a pattern, the constant's initializer must be trivial or `{$non_sm_ty}` must be annotated with `#[derive(PartialEq, Eq)]` mir_build_overlapping_range_endpoints = multiple patterns overlap on their endpoints @@ -324,3 +324,13 @@ mir_build_overlapping_range_endpoints = multiple patterns overlap on their endpo .note = you likely meant to write mutually exclusive ranges mir_build_overlapping_range = this range overlaps on `{$range}`... + +mir_build_non_exhaustive_omitted_pattern = some variants are not matched explicitly + .label = {$count -> + [1] pattern `{$witness_1}` + [2] patterns `{$witness_1}` and `{$witness_2}` + [3] patterns `{$witness_1}`, `{$witness_2}` and `{$witness_3}` + *[other] patterns `{$witness_1}`, `{$witness_2}`, `{$witness_3}` and more + } not covered + .help = ensure that all variants are matched explicitly by adding the suggested match arms + .note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 6bb15730b0069..e15da5bb9ce1b 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -685,3 +685,17 @@ pub struct Overlap<'tcx> { pub span: Span, pub range: Pat<'tcx>, } + +#[derive(LintDiagnostic)] +#[diag(mir_build_non_exhaustive_omitted_pattern)] +#[help] +#[note] +pub(crate) struct NonExhaustiveOmittedPattern<'tcx> { + pub scrut_ty: Ty<'tcx>, + #[label] + pub uncovered: Span, + pub count: usize, + pub witness_1: Pat<'tcx>, + pub witness_2: Pat<'tcx>, + pub witness_3: Pat<'tcx>, +} diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 8e7ca32feb346..7f3519945c3fe 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -435,7 +435,7 @@ impl<'tcx> ConstToPat<'tcx> { _ => { if !pointee_ty.is_sized(tcx, param_env) { // `tcx.deref_mir_constant()` below will ICE with an unsized type - // (except slices, which are handled in a separate arm above). + // (except slices, which are handled in a separate arm above). let err = UnsizedPattern { span, non_sm_ty: *pointee_ty }; tcx.sess.create_err(err).emit_unless(!self.include_lint_checks); diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 2c775b397182b..6395b75a08f6f 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -2,7 +2,7 @@ mod check_match; mod const_to_pat; -mod deconstruct_pat; +pub(crate) mod deconstruct_pat; mod usefulness; pub(crate) use self::check_match::check_match; diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 8f80cb95e58e7..37aed5bd14da4 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -291,9 +291,8 @@ use self::ArmType::*; use self::Usefulness::*; - -use super::check_match::{joined_uncovered_patterns, pattern_not_covered_label}; use super::deconstruct_pat::{Constructor, DeconstructedPat, Fields, SplitWildcard}; +use crate::errors::NonExhaustiveOmittedPattern; use rustc_data_structures::captures::Captures; @@ -754,6 +753,23 @@ fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( hir_id: HirId, witnesses: Vec>, ) { + let witness_1 = witnesses.get(0).unwrap().to_pat(cx); + + cx.tcx.emit_spanned_lint( + NON_EXHAUSTIVE_OMITTED_PATTERNS, + hir_id, + sp, + NonExhaustiveOmittedPattern { + scrut_ty, + uncovered: sp, + count: witnesses.len(), + // Substitute dummy values if witnesses is smaller than 3. + witness_2: witnesses.get(1).map(|w| w.to_pat(cx)).unwrap_or_else(|| witness_1.clone()), + witness_3: witnesses.get(2).map(|w| w.to_pat(cx)).unwrap_or_else(|| witness_1.clone()), + witness_1, + }, + ); + /* cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, sp, "some variants are not matched explicitly", |lint| { let joined_patterns = joined_uncovered_patterns(cx, &witnesses); lint.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns)); @@ -766,6 +782,7 @@ fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( )); lint }); + */ } /// Algorithm from . From 31c20210b9683f983953e1a4e45db94146b3c7cb Mon Sep 17 00:00:00 2001 From: mejrs <> Date: Fri, 23 Dec 2022 21:02:23 +0100 Subject: [PATCH 16/19] Migrate pattern matching --- .../locales/en-US/mir_build.ftl | 36 ++- compiler/rustc_mir_build/src/errors.rs | 164 +++++++++++++- compiler/rustc_mir_build/src/lib.rs | 1 + .../src/thir/pattern/check_match.rs | 207 +++++++----------- .../src/thir/pattern/usefulness.rs | 59 ++--- .../ui/consts/const-match-check.eval1.stderr | 2 +- .../ui/consts/const-match-check.eval2.stderr | 2 +- .../consts/const-match-check.matchck.stderr | 8 +- tests/ui/consts/const-pattern-irrefutable.rs | 18 +- .../consts/const-pattern-irrefutable.stderr | 19 +- tests/ui/consts/const_let_refutable.stderr | 2 +- tests/ui/empty/empty-never-array.rs | 3 +- tests/ui/empty/empty-never-array.stderr | 10 +- tests/ui/error-codes/E0005.stderr | 9 +- tests/ui/error-codes/E0297.stderr | 7 +- .../feature-gate-exhaustive-patterns.stderr | 9 +- ...oop-refutable-pattern-error-message.stderr | 2 +- tests/ui/issues/issue-15381.rs | 3 +- tests/ui/issues/issue-15381.stderr | 2 +- .../ui/never_type/exhaustive_patterns.stderr | 8 +- ...een-expanded-earlier-non-exhaustive.stderr | 2 +- tests/ui/pattern/usefulness/issue-31561.rs | 3 +- .../ui/pattern/usefulness/issue-31561.stderr | 12 +- .../usefulness/non-exhaustive-defined-here.rs | 38 ++-- .../non-exhaustive-defined-here.stderr | 54 ++--- .../usefulness/refutable-pattern-errors.rs | 6 +- .../refutable-pattern-errors.stderr | 6 +- .../usefulness/refutable-pattern-in-fn-arg.rs | 3 +- .../refutable-pattern-in-fn-arg.stderr | 2 +- ...recursive-types-are-not-uninhabited.stderr | 9 +- .../const-pat-non-exaustive-let-new-var.rs | 5 +- ...const-pat-non-exaustive-let-new-var.stderr | 5 +- .../ui/uninhabited/uninhabited-irrefutable.rs | 4 +- .../uninhabited-irrefutable.stderr | 10 +- .../uninhabited-matches-feature-gated.stderr | 9 +- 35 files changed, 415 insertions(+), 324 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl index 9f10dc4b634c0..b277942cdcca1 100644 --- a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl +++ b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl @@ -326,11 +326,39 @@ mir_build_overlapping_range_endpoints = multiple patterns overlap on their endpo mir_build_overlapping_range = this range overlaps on `{$range}`... mir_build_non_exhaustive_omitted_pattern = some variants are not matched explicitly - .label = {$count -> + .help = ensure that all variants are matched explicitly by adding the suggested match arms + .note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found + +mir_build_uncovered = {$count -> [1] pattern `{$witness_1}` [2] patterns `{$witness_1}` and `{$witness_2}` [3] patterns `{$witness_1}`, `{$witness_2}` and `{$witness_3}` - *[other] patterns `{$witness_1}`, `{$witness_2}`, `{$witness_3}` and more + *[other] patterns `{$witness_1}`, `{$witness_2}`, `{$witness_3}` and {$remainder} more } not covered - .help = ensure that all variants are matched explicitly by adding the suggested match arms - .note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found + +mir_build_pattern_not_covered = refutable pattern in {$origin} + .pattern_ty = the matched value is of type `{$pattern_ty}` + +mir_build_inform_irrefutable = `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant + +mir_build_more_information = for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html + +mir_build_res_defined_here = {$res} defined here + +mir_build_adt_defined_here = `{$ty}` defined here + +mir_build_variant_defined_here = not covered + +mir_build_interpreted_as_const = introduce a variable instead + +mir_build_confused = missing patterns are not covered because `{$variable}` is interpreted as {$article} {$res} pattern, not a new variable + +mir_build_suggest_if_let = you might want to use `if let` to ignore the {$count -> + [one] variant that isn't + *[other] variants that aren't + } matched + +mir_build_suggest_let_else = alternatively, you might want to use `let else` to handle the {$count -> + [one] variant that isn't + *[other] variants that aren't + } matched diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index e15da5bb9ce1b..a3c58c31654fd 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -1,8 +1,11 @@ +use crate::thir::pattern::deconstruct_pat::DeconstructedPat; use crate::thir::pattern::MatchCheckCtxt; use rustc_errors::Handler; use rustc_errors::{ - error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, MultiSpan, + error_code, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, + IntoDiagnostic, MultiSpan, SubdiagnosticMessage, }; +use rustc_hir::def::Res; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::thir::Pat; use rustc_middle::ty::{self, Ty}; @@ -677,7 +680,6 @@ pub struct OverlappingRangeEndpoints<'tcx> { pub overlap: Overlap<'tcx>, } -#[derive(Debug)] #[derive(Subdiagnostic)] #[label(mir_build_overlapping_range)] pub struct Overlap<'tcx> { @@ -692,10 +694,158 @@ pub struct Overlap<'tcx> { #[note] pub(crate) struct NonExhaustiveOmittedPattern<'tcx> { pub scrut_ty: Ty<'tcx>, - #[label] - pub uncovered: Span, + #[subdiagnostic] + pub uncovered: Uncovered<'tcx>, +} + +#[derive(Subdiagnostic)] +#[label(mir_build_uncovered)] +pub(crate) struct Uncovered<'tcx> { + #[primary_span] + span: Span, + count: usize, + witness_1: Pat<'tcx>, + witness_2: Pat<'tcx>, + witness_3: Pat<'tcx>, + remainder: usize, +} + +impl<'tcx> Uncovered<'tcx> { + pub fn new<'p>( + span: Span, + cx: &MatchCheckCtxt<'p, 'tcx>, + witnesses: Vec>, + ) -> Self { + let witness_1 = witnesses.get(0).unwrap().to_pat(cx); + Self { + span, + count: witnesses.len(), + // Substitute dummy values if witnesses is smaller than 3. These will never be read. + witness_2: witnesses.get(1).map(|w| w.to_pat(cx)).unwrap_or_else(|| witness_1.clone()), + witness_3: witnesses.get(2).map(|w| w.to_pat(cx)).unwrap_or_else(|| witness_1.clone()), + witness_1, + remainder: witnesses.len().saturating_sub(3), + } + } +} + +#[derive(Diagnostic)] +#[diag(mir_build_pattern_not_covered, code = "E0005")] +pub(crate) struct PatternNotCovered<'s, 'tcx> { + #[primary_span] + pub span: Span, + pub origin: &'s str, + #[subdiagnostic] + pub uncovered: Uncovered<'tcx>, + #[subdiagnostic] + pub inform: Option, + #[subdiagnostic] + pub interpreted_as_const: Option, + #[subdiagnostic] + pub adt_defined_here: Option>, + #[note(pattern_ty)] + pub _p: (), + pub pattern_ty: Ty<'tcx>, + #[subdiagnostic] + pub if_let_suggestion: Option, + #[subdiagnostic] + pub let_else_suggestion: Option, + #[subdiagnostic] + pub res_defined_here: Option, +} + +#[derive(Subdiagnostic)] +#[note(mir_build_inform_irrefutable)] +#[note(mir_build_more_information)] +pub struct Inform; + +pub struct AdtDefinedHere<'tcx> { + pub adt_def_span: Span, + pub ty: Ty<'tcx>, + pub variants: Vec, +} + +pub struct Variant { + pub span: Span, +} + +impl<'tcx> AddToDiagnostic for AdtDefinedHere<'tcx> { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + diag.set_arg("ty", self.ty); + let mut spans = MultiSpan::from(self.adt_def_span); + + for Variant { span } in self.variants { + spans.push_span_label(span, rustc_errors::fluent::mir_build_variant_defined_here); + } + + diag.span_note(spans, rustc_errors::fluent::mir_build_adt_defined_here); + } +} + +#[derive(Subdiagnostic)] +#[label(mir_build_res_defined_here)] +pub struct ResDefinedHere { + #[primary_span] + pub def_span: Span, + pub res: Res, +} + +#[derive(Subdiagnostic)] +#[suggestion( + mir_build_interpreted_as_const, + code = "{variable}_var", + applicability = "maybe-incorrect" +)] +#[label(mir_build_confused)] +pub struct InterpretedAsConst { + #[primary_span] + pub span: Span, + pub article: &'static str, + pub variable: String, + pub res: Res, +} + +#[derive(Subdiagnostic)] +pub enum SuggestIfLet { + #[multipart_suggestion(mir_build_suggest_if_let, applicability = "has-placeholders")] + None { + #[suggestion_part(code = "if ")] + start_span: Span, + #[suggestion_part(code = " {{ todo!() }}")] + semi_span: Span, + count: usize, + }, + #[multipart_suggestion(mir_build_suggest_if_let, applicability = "has-placeholders")] + One { + #[suggestion_part(code = "let {binding} = if ")] + start_span: Span, + #[suggestion_part(code = " {{ {binding} }} else {{ todo!() }}")] + end_span: Span, + binding: Ident, + count: usize, + }, + #[multipart_suggestion(mir_build_suggest_if_let, applicability = "has-placeholders")] + More { + #[suggestion_part(code = "let ({bindings}) = if ")] + start_span: Span, + #[suggestion_part(code = " {{ ({bindings}) }} else {{ todo!() }}")] + end_span: Span, + bindings: String, + count: usize, + }, +} + +#[derive(Subdiagnostic)] +#[suggestion( + mir_build_suggest_let_else, + code = " else {{ todo!() }}", + applicability = "has-placeholders" +)] +pub struct SuggestLetElse { + #[primary_span] + pub end_span: Span, pub count: usize, - pub witness_1: Pat<'tcx>, - pub witness_2: Pat<'tcx>, - pub witness_3: Pat<'tcx>, } diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs index 2b05e92fdcf20..fb7ae6f1d2424 100644 --- a/compiler/rustc_mir_build/src/lib.rs +++ b/compiler/rustc_mir_build/src/lib.rs @@ -10,6 +10,7 @@ #![feature(let_chains)] #![feature(min_specialization)] #![feature(once_cell)] +#![feature(try_blocks)] #![recursion_limit = "256"] #[macro_use] diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index e7ee0d9e908e0..422c2ff3ede0d 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1,3 +1,8 @@ +//#![allow(unused_imports, unused_variables)] + +//#![warn(rustc::untranslatable_diagnostic)] +//#![warn(rustc::diagnostic_outside_of_impl)] + use super::deconstruct_pat::{Constructor, DeconstructedPat}; use super::usefulness::{ compute_match_usefulness, MatchArm, MatchCheckCtxt, Reachability, UsefulnessReport, @@ -9,8 +14,7 @@ use crate::errors::*; use rustc_arena::TypedArena; use rustc_ast::Mutability; use rustc_errors::{ - pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, - MultiSpan, + struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, }; use rustc_hir as hir; use rustc_hir::def::*; @@ -378,8 +382,8 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let pattern = self.lower_pattern(&mut cx, pat, &mut false); let pattern_ty = pattern.ty(); - let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }]; - let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty); + let arm = MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }; + let report = compute_match_usefulness(&cx, &[arm], pat.hir_id, pattern_ty); // Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We // only care about exhaustiveness here. @@ -390,145 +394,82 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { return; } - let joined_patterns = joined_uncovered_patterns(&cx, &witnesses); - - let mut bindings = vec![]; - - let mut err = struct_span_err!( - self.tcx.sess, - pat.span, - E0005, - "refutable pattern in {}: {} not covered", - origin, - joined_patterns - ); - let suggest_if_let = match &pat.kind { - hir::PatKind::Path(hir::QPath::Resolved(None, path)) - if path.segments.len() == 1 && path.segments[0].args.is_none() => + let (inform, interpreted_as_const, res_defined_here, if_let_suggestion, let_else_suggestion) = + if let hir::PatKind::Path(hir::QPath::Resolved( + None, + hir::Path { + segments: &[hir::PathSegment { args: None, res, ident, .. }], + .. + }, + )) = &pat.kind { - const_not_var(&mut err, cx.tcx, pat, path); - false - } - _ => { - pat.walk(&mut |pat: &hir::Pat<'_>| { - match pat.kind { - hir::PatKind::Binding(_, _, ident, _) => { - bindings.push(ident); + ( + None, + Some(InterpretedAsConst { + span: pat.span, + article: res.article(), + variable: ident.to_string().to_lowercase(), + res, + }), + try { + ResDefinedHere { + def_span: cx.tcx.hir().res_span(res)?, + res, } - _ => {} + }, + None, None, + ) + } else if let Some(span) = sp && self.tcx.sess.source_map().is_span_accessible(span) { + let mut bindings = vec![]; + pat.walk_always(&mut |pat: &hir::Pat<'_>| { + if let hir::PatKind::Binding(_, _, ident, _) = pat.kind { + bindings.push(ident); } - true }); - - err.span_label(pat.span, pattern_not_covered_label(&witnesses, &joined_patterns)); - true - } - }; - - if let (Some(span), true) = (sp, suggest_if_let) { - err.note( - "`let` bindings require an \"irrefutable pattern\", like a `struct` or \ - an `enum` with only one variant", - ); - if self.tcx.sess.source_map().is_span_accessible(span) { let semi_span = span.shrink_to_hi().with_lo(span.hi() - BytePos(1)); let start_span = span.shrink_to_lo(); let end_span = semi_span.shrink_to_lo(); - err.multipart_suggestion( - &format!( - "you might want to use `if let` to ignore the variant{} that {} matched", - pluralize!(witnesses.len()), - match witnesses.len() { - 1 => "isn't", - _ => "aren't", - }, - ), - vec![ - match &bindings[..] { - [] => (start_span, "if ".to_string()), - [binding] => (start_span, format!("let {} = if ", binding)), - bindings => ( - start_span, - format!( - "let ({}) = if ", - bindings - .iter() - .map(|ident| ident.to_string()) - .collect::>() - .join(", ") - ), - ), - }, - match &bindings[..] { - [] => (semi_span, " { todo!() }".to_string()), - [binding] => { - (end_span, format!(" {{ {} }} else {{ todo!() }}", binding)) - } - bindings => ( - end_span, - format!( - " {{ ({}) }} else {{ todo!() }}", - bindings - .iter() - .map(|ident| ident.to_string()) - .collect::>() - .join(", ") - ), - ), - }, - ], - Applicability::HasPlaceholders, - ); - if !bindings.is_empty() { - err.span_suggestion_verbose( - semi_span.shrink_to_lo(), - &format!( - "alternatively, you might want to use \ - let else to handle the variant{} that {} matched", - pluralize!(witnesses.len()), - match witnesses.len() { - 1 => "isn't", - _ => "aren't", - }, - ), - " else { todo!() }", - Applicability::HasPlaceholders, - ); - } - } - err.note( - "for more information, visit \ - https://doc.rust-lang.org/book/ch18-02-refutability.html", - ); - } - - adt_defined_here(&cx, &mut err, pattern_ty, &witnesses); - err.note(&format!("the matched value is of type `{}`", pattern_ty)); - err.emit(); - } -} + let count = witnesses.len(); + let if_let = match *bindings { + [] => SuggestIfLet::None{start_span, semi_span, count}, + [binding] => SuggestIfLet::One{start_span, end_span, count, binding }, + _ => SuggestIfLet::More{start_span, end_span, count, bindings: bindings + .iter() + .map(|ident| ident.to_string()) + .collect::>() + .join(", ")}, + }; + let let_else = if bindings.is_empty() {None} else{Some( SuggestLetElse{end_span, count })}; + (sp.map(|_|Inform), None, None, Some(if_let), let_else) + } else{ + (sp.map(|_|Inform), None, None, None, None) + }; -/// A path pattern was interpreted as a constant, not a new variable. -/// This caused an irrefutable match failure in e.g. `let`. -fn const_not_var(err: &mut Diagnostic, tcx: TyCtxt<'_>, pat: &Pat<'_>, path: &hir::Path<'_>) { - let descr = path.res.descr(); - err.span_label( - pat.span, - format!("interpreted as {} {} pattern, not a new variable", path.res.article(), descr,), - ); + let adt_defined_here = try { + let ty = pattern_ty.peel_refs(); + let ty::Adt(def, _) = ty.kind() else { None? }; + let adt_def_span = cx.tcx.hir().get_if_local(def.did())?.ident()?.span; + let mut variants = vec![]; - err.span_suggestion( - pat.span, - "introduce a variable instead", - format!("{}_var", path.segments[0].ident).to_lowercase(), - // Cannot use `MachineApplicable` as it's not really *always* correct - // because there may be such an identifier in scope or the user maybe - // really wanted to match against the constant. This is quite unlikely however. - Applicability::MaybeIncorrect, - ); + for span in maybe_point_at_variant(&cx, *def, witnesses.iter().take(5)) { + variants.push(Variant { span }); + } + AdtDefinedHere { adt_def_span, ty, variants } + }; - if let Some(span) = tcx.hir().res_span(path.res) { - err.span_label(span, format!("{} defined here", descr)); + self.tcx.sess.emit_err(PatternNotCovered { + span: pat.span, + origin, + uncovered: Uncovered::new(pat.span, &cx, witnesses), + inform, + interpreted_as_const, + _p: (), + pattern_ty, + if_let_suggestion, + let_else_suggestion, + res_defined_here, + adt_defined_here, + }); } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 37aed5bd14da4..be66d0d476513 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -292,7 +292,7 @@ use self::ArmType::*; use self::Usefulness::*; use super::deconstruct_pat::{Constructor, DeconstructedPat, Fields, SplitWildcard}; -use crate::errors::NonExhaustiveOmittedPattern; +use crate::errors::{NonExhaustiveOmittedPattern, Uncovered}; use rustc_data_structures::captures::Captures; @@ -742,49 +742,6 @@ impl<'p, 'tcx> Witness<'p, 'tcx> { } } -/// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns` -/// is not exhaustive enough. -/// -/// NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`. -fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>( - cx: &MatchCheckCtxt<'p, 'tcx>, - scrut_ty: Ty<'tcx>, - sp: Span, - hir_id: HirId, - witnesses: Vec>, -) { - let witness_1 = witnesses.get(0).unwrap().to_pat(cx); - - cx.tcx.emit_spanned_lint( - NON_EXHAUSTIVE_OMITTED_PATTERNS, - hir_id, - sp, - NonExhaustiveOmittedPattern { - scrut_ty, - uncovered: sp, - count: witnesses.len(), - // Substitute dummy values if witnesses is smaller than 3. - witness_2: witnesses.get(1).map(|w| w.to_pat(cx)).unwrap_or_else(|| witness_1.clone()), - witness_3: witnesses.get(2).map(|w| w.to_pat(cx)).unwrap_or_else(|| witness_1.clone()), - witness_1, - }, - ); - /* - cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, sp, "some variants are not matched explicitly", |lint| { - let joined_patterns = joined_uncovered_patterns(cx, &witnesses); - lint.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns)); - lint.help( - "ensure that all variants are matched explicitly by adding the suggested match arms", - ); - lint.note(&format!( - "the matched value is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found", - scrut_ty, - )); - lint - }); - */ -} - /// Algorithm from . /// The algorithm from the paper has been modified to correctly handle empty /// types. The changes are: @@ -930,7 +887,19 @@ fn is_useful<'p, 'tcx>( .collect::>() }; - lint_non_exhaustive_omitted_patterns(pcx.cx, pcx.ty, pcx.span, hir_id, patterns); + // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns` + // is not exhaustive enough. + // + // NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`. + cx.tcx.emit_spanned_lint( + NON_EXHAUSTIVE_OMITTED_PATTERNS, + hir_id, + pcx.span, + NonExhaustiveOmittedPattern { + scrut_ty: pcx.ty, + uncovered: Uncovered::new(pcx.span, pcx.cx, patterns), + }, + ); } ret.extend(usefulness); diff --git a/tests/ui/consts/const-match-check.eval1.stderr b/tests/ui/consts/const-match-check.eval1.stderr index 6e61dbbd8eee3..1caf1617e213c 100644 --- a/tests/ui/consts/const-match-check.eval1.stderr +++ b/tests/ui/consts/const-match-check.eval1.stderr @@ -1,4 +1,4 @@ -error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered +error[E0005]: refutable pattern in local binding --> $DIR/const-match-check.rs:25:15 | LL | A = { let 0 = 0; 0 }, diff --git a/tests/ui/consts/const-match-check.eval2.stderr b/tests/ui/consts/const-match-check.eval2.stderr index 1b3b6e06c3df6..f038ba1c8ed85 100644 --- a/tests/ui/consts/const-match-check.eval2.stderr +++ b/tests/ui/consts/const-match-check.eval2.stderr @@ -1,4 +1,4 @@ -error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered +error[E0005]: refutable pattern in local binding --> $DIR/const-match-check.rs:31:24 | LL | let x: [i32; { let 0 = 0; 0 }] = []; diff --git a/tests/ui/consts/const-match-check.matchck.stderr b/tests/ui/consts/const-match-check.matchck.stderr index bc8edfa7af9f4..b1921f8a41e48 100644 --- a/tests/ui/consts/const-match-check.matchck.stderr +++ b/tests/ui/consts/const-match-check.matchck.stderr @@ -1,4 +1,4 @@ -error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered +error[E0005]: refutable pattern in local binding --> $DIR/const-match-check.rs:4:22 | LL | const X: i32 = { let 0 = 0; 0 }; @@ -12,7 +12,7 @@ help: you might want to use `if let` to ignore the variants that aren't matched LL | const X: i32 = { if let 0 = 0 { todo!() } 0 }; | ++ ~~~~~~~~~~~ -error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered +error[E0005]: refutable pattern in local binding --> $DIR/const-match-check.rs:8:23 | LL | static Y: i32 = { let 0 = 0; 0 }; @@ -26,7 +26,7 @@ help: you might want to use `if let` to ignore the variants that aren't matched LL | static Y: i32 = { if let 0 = 0 { todo!() } 0 }; | ++ ~~~~~~~~~~~ -error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered +error[E0005]: refutable pattern in local binding --> $DIR/const-match-check.rs:13:26 | LL | const X: i32 = { let 0 = 0; 0 }; @@ -40,7 +40,7 @@ help: you might want to use `if let` to ignore the variants that aren't matched LL | const X: i32 = { if let 0 = 0 { todo!() } 0 }; | ++ ~~~~~~~~~~~ -error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered +error[E0005]: refutable pattern in local binding --> $DIR/const-match-check.rs:19:26 | LL | const X: i32 = { let 0 = 0; 0 }; diff --git a/tests/ui/consts/const-pattern-irrefutable.rs b/tests/ui/consts/const-pattern-irrefutable.rs index 2105c12a1680a..61bdf57ffdb94 100644 --- a/tests/ui/consts/const-pattern-irrefutable.rs +++ b/tests/ui/consts/const-pattern-irrefutable.rs @@ -9,8 +9,20 @@ use foo::d; const a: u8 = 2; fn main() { - let a = 4; //~ ERROR refutable pattern in local binding: `0_u8..=1_u8` and `3_u8..=u8::MAX - let c = 4; //~ ERROR refutable pattern in local binding: `0_u8..=1_u8` and `3_u8..=u8::MAX - let d = 4; //~ ERROR refutable pattern in local binding: `0_u8..=1_u8` and `3_u8..=u8::MAX + let a = 4; + //~^ ERROR refutable pattern in local binding + //~| patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered + //~| missing patterns are not covered because `a` is interpreted as a constant pattern, not a new variable + //~| HELP introduce a variable instead + let c = 4; + //~^ ERROR refutable pattern in local binding + //~| patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered + //~| missing patterns are not covered because `c` is interpreted as a constant pattern, not a new variable + //~| HELP introduce a variable instead + let d = 4; + //~^ ERROR refutable pattern in local binding + //~| patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered + //~| missing patterns are not covered because `d` is interpreted as a constant pattern, not a new variable + //~| HELP introduce a variable instead fn f() {} // Check that the `NOTE`s still work with an item here (cf. issue #35115). } diff --git a/tests/ui/consts/const-pattern-irrefutable.stderr b/tests/ui/consts/const-pattern-irrefutable.stderr index a2b8f072c6ea7..c156ea1610c4f 100644 --- a/tests/ui/consts/const-pattern-irrefutable.stderr +++ b/tests/ui/consts/const-pattern-irrefutable.stderr @@ -1,4 +1,4 @@ -error[E0005]: refutable pattern in local binding: `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered +error[E0005]: refutable pattern in local binding --> $DIR/const-pattern-irrefutable.rs:12:9 | LL | const a: u8 = 2; @@ -7,13 +7,14 @@ LL | const a: u8 = 2; LL | let a = 4; | ^ | | - | interpreted as a constant pattern, not a new variable + | patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered + | missing patterns are not covered because `a` is interpreted as a constant pattern, not a new variable | help: introduce a variable instead: `a_var` | = note: the matched value is of type `u8` -error[E0005]: refutable pattern in local binding: `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered - --> $DIR/const-pattern-irrefutable.rs:13:9 +error[E0005]: refutable pattern in local binding + --> $DIR/const-pattern-irrefutable.rs:17:9 | LL | pub const b: u8 = 2; | --------------- constant defined here @@ -21,13 +22,14 @@ LL | pub const b: u8 = 2; LL | let c = 4; | ^ | | - | interpreted as a constant pattern, not a new variable + | patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered + | missing patterns are not covered because `c` is interpreted as a constant pattern, not a new variable | help: introduce a variable instead: `c_var` | = note: the matched value is of type `u8` -error[E0005]: refutable pattern in local binding: `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered - --> $DIR/const-pattern-irrefutable.rs:14:9 +error[E0005]: refutable pattern in local binding + --> $DIR/const-pattern-irrefutable.rs:22:9 | LL | pub const d: u8 = 2; | --------------- constant defined here @@ -35,7 +37,8 @@ LL | pub const d: u8 = 2; LL | let d = 4; | ^ | | - | interpreted as a constant pattern, not a new variable + | patterns `0_u8..=1_u8` and `3_u8..=u8::MAX` not covered + | missing patterns are not covered because `d` is interpreted as a constant pattern, not a new variable | help: introduce a variable instead: `d_var` | = note: the matched value is of type `u8` diff --git a/tests/ui/consts/const_let_refutable.stderr b/tests/ui/consts/const_let_refutable.stderr index d7e8c048f7dcc..d6119028f5b59 100644 --- a/tests/ui/consts/const_let_refutable.stderr +++ b/tests/ui/consts/const_let_refutable.stderr @@ -1,4 +1,4 @@ -error[E0005]: refutable pattern in function argument: `&[]`, `&[_]` and `&[_, _, _, ..]` not covered +error[E0005]: refutable pattern in function argument --> $DIR/const_let_refutable.rs:3:16 | LL | const fn slice(&[a, b]: &[i32]) -> i32 { diff --git a/tests/ui/empty/empty-never-array.rs b/tests/ui/empty/empty-never-array.rs index 3de2b1a78a3da..fd93346101d84 100644 --- a/tests/ui/empty/empty-never-array.rs +++ b/tests/ui/empty/empty-never-array.rs @@ -8,7 +8,8 @@ enum Helper { fn transmute(t: T) -> U { let Helper::U(u) = Helper::T(t, []); - //~^ ERROR refutable pattern in local binding: `Helper::T(_, _)` not covered + //~^ ERROR refutable pattern in local binding + //~| `Helper::T(_, _)` not covered u } diff --git a/tests/ui/empty/empty-never-array.stderr b/tests/ui/empty/empty-never-array.stderr index adf7827436805..9385382f860bc 100644 --- a/tests/ui/empty/empty-never-array.stderr +++ b/tests/ui/empty/empty-never-array.stderr @@ -1,4 +1,4 @@ -error[E0005]: refutable pattern in local binding: `Helper::T(_, _)` not covered +error[E0005]: refutable pattern in local binding --> $DIR/empty-never-array.rs:10:9 | LL | let Helper::U(u) = Helper::T(t, []); @@ -7,18 +7,18 @@ LL | let Helper::U(u) = Helper::T(t, []); = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html note: `Helper` defined here - --> $DIR/empty-never-array.rs:4:5 + --> $DIR/empty-never-array.rs:3:6 | LL | enum Helper { - | ------ + | ^^^^^^ LL | T(T, [!; 0]), - | ^ not covered + | - not covered = note: the matched value is of type `Helper` help: you might want to use `if let` to ignore the variant that isn't matched | LL | let u = if let Helper::U(u) = Helper::T(t, []) { u } else { todo!() }; | ++++++++++ ++++++++++++++++++++++ -help: alternatively, you might want to use let else to handle the variant that isn't matched +help: alternatively, you might want to use `let else` to handle the variant that isn't matched | LL | let Helper::U(u) = Helper::T(t, []) else { todo!() }; | ++++++++++++++++ diff --git a/tests/ui/error-codes/E0005.stderr b/tests/ui/error-codes/E0005.stderr index 0f179259356d5..3c27a7d4c064e 100644 --- a/tests/ui/error-codes/E0005.stderr +++ b/tests/ui/error-codes/E0005.stderr @@ -1,4 +1,4 @@ -error[E0005]: refutable pattern in local binding: `None` not covered +error[E0005]: refutable pattern in local binding --> $DIR/E0005.rs:3:9 | LL | let Some(y) = x; @@ -6,17 +6,12 @@ LL | let Some(y) = x; | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html -note: `Option` defined here - --> $SRC_DIR/core/src/option.rs:LL:COL - ::: $SRC_DIR/core/src/option.rs:LL:COL - | - = note: not covered = note: the matched value is of type `Option` help: you might want to use `if let` to ignore the variant that isn't matched | LL | let y = if let Some(y) = x { y } else { todo!() }; | ++++++++++ ++++++++++++++++++++++ -help: alternatively, you might want to use let else to handle the variant that isn't matched +help: alternatively, you might want to use `let else` to handle the variant that isn't matched | LL | let Some(y) = x else { todo!() }; | ++++++++++++++++ diff --git a/tests/ui/error-codes/E0297.stderr b/tests/ui/error-codes/E0297.stderr index 903422f3b9b8a..293028f5f68b1 100644 --- a/tests/ui/error-codes/E0297.stderr +++ b/tests/ui/error-codes/E0297.stderr @@ -1,14 +1,9 @@ -error[E0005]: refutable pattern in `for` loop binding: `None` not covered +error[E0005]: refutable pattern in `for` loop binding --> $DIR/E0297.rs:4:9 | LL | for Some(x) in xs {} | ^^^^^^^ pattern `None` not covered | -note: `Option` defined here - --> $SRC_DIR/core/src/option.rs:LL:COL - ::: $SRC_DIR/core/src/option.rs:LL:COL - | - = note: not covered = note: the matched value is of type `Option` error: aborting due to previous error diff --git a/tests/ui/feature-gates/feature-gate-exhaustive-patterns.stderr b/tests/ui/feature-gates/feature-gate-exhaustive-patterns.stderr index e253e4791e8bd..5e4df7422fcf0 100644 --- a/tests/ui/feature-gates/feature-gate-exhaustive-patterns.stderr +++ b/tests/ui/feature-gates/feature-gate-exhaustive-patterns.stderr @@ -1,4 +1,4 @@ -error[E0005]: refutable pattern in local binding: `Err(_)` not covered +error[E0005]: refutable pattern in local binding --> $DIR/feature-gate-exhaustive-patterns.rs:8:9 | LL | let Ok(_x) = foo(); @@ -6,17 +6,12 @@ LL | let Ok(_x) = foo(); | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered = note: the matched value is of type `Result` help: you might want to use `if let` to ignore the variant that isn't matched | LL | let _x = if let Ok(_x) = foo() { _x } else { todo!() }; | +++++++++++ +++++++++++++++++++++++ -help: alternatively, you might want to use let else to handle the variant that isn't matched +help: alternatively, you might want to use `let else` to handle the variant that isn't matched | LL | let Ok(_x) = foo() else { todo!() }; | ++++++++++++++++ diff --git a/tests/ui/for/for-loop-refutable-pattern-error-message.stderr b/tests/ui/for/for-loop-refutable-pattern-error-message.stderr index 20b689aa5e0af..49a82a6769dce 100644 --- a/tests/ui/for/for-loop-refutable-pattern-error-message.stderr +++ b/tests/ui/for/for-loop-refutable-pattern-error-message.stderr @@ -1,4 +1,4 @@ -error[E0005]: refutable pattern in `for` loop binding: `&i32::MIN..=0_i32` and `&2_i32..=i32::MAX` not covered +error[E0005]: refutable pattern in `for` loop binding --> $DIR/for-loop-refutable-pattern-error-message.rs:2:9 | LL | for &1 in [1].iter() {} diff --git a/tests/ui/issues/issue-15381.rs b/tests/ui/issues/issue-15381.rs index 392fb1b24ddc4..23b266bef1d91 100644 --- a/tests/ui/issues/issue-15381.rs +++ b/tests/ui/issues/issue-15381.rs @@ -2,7 +2,8 @@ fn main() { let values: Vec = vec![1,2,3,4,5,6,7,8]; for &[x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) { - //~^ ERROR refutable pattern in `for` loop binding: `&[]`, `&[_]`, `&[_, _]` and 1 more not + //~^ ERROR refutable pattern in `for` loop binding + //~| patterns `&[]`, `&[_]`, `&[_, _]` and 1 more not covered println!("y={}", y); } } diff --git a/tests/ui/issues/issue-15381.stderr b/tests/ui/issues/issue-15381.stderr index c4667ce1c8ba1..085958411ccb9 100644 --- a/tests/ui/issues/issue-15381.stderr +++ b/tests/ui/issues/issue-15381.stderr @@ -1,4 +1,4 @@ -error[E0005]: refutable pattern in `for` loop binding: `&[]`, `&[_]`, `&[_, _]` and 1 more not covered +error[E0005]: refutable pattern in `for` loop binding --> $DIR/issue-15381.rs:4:9 | LL | for &[x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) { diff --git a/tests/ui/never_type/exhaustive_patterns.stderr b/tests/ui/never_type/exhaustive_patterns.stderr index e41baf862180d..40c7c1d1067ff 100644 --- a/tests/ui/never_type/exhaustive_patterns.stderr +++ b/tests/ui/never_type/exhaustive_patterns.stderr @@ -1,4 +1,4 @@ -error[E0005]: refutable pattern in local binding: `Either::B(_)` not covered +error[E0005]: refutable pattern in local binding --> $DIR/exhaustive_patterns.rs:20:9 | LL | let Either::A(()) = foo(); @@ -7,13 +7,13 @@ LL | let Either::A(()) = foo(); = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html note: `Either<(), !>` defined here - --> $DIR/exhaustive_patterns.rs:12:5 + --> $DIR/exhaustive_patterns.rs:10:6 | LL | enum Either { - | ------ + | ^^^^^^ LL | A(A), LL | B(inner::Wrapper), - | ^ not covered + | - not covered = note: the matched value is of type `Either<(), !>` help: you might want to use `if let` to ignore the variant that isn't matched | diff --git a/tests/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr b/tests/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr index 95b22ac059482..4adcf4feee90d 100644 --- a/tests/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr +++ b/tests/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr @@ -1,4 +1,4 @@ -error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `3_i32..=i32::MAX` not covered +error[E0005]: refutable pattern in local binding --> $DIR/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs:2:10 | LL | let (0 | (1 | 2)) = 0; diff --git a/tests/ui/pattern/usefulness/issue-31561.rs b/tests/ui/pattern/usefulness/issue-31561.rs index 5b878851a3144..82414f0418bb2 100644 --- a/tests/ui/pattern/usefulness/issue-31561.rs +++ b/tests/ui/pattern/usefulness/issue-31561.rs @@ -6,5 +6,6 @@ enum Thing { fn main() { let Thing::Foo(y) = Thing::Foo(1); - //~^ ERROR refutable pattern in local binding: `Thing::Bar` and `Thing::Baz` not covered + //~^ ERROR refutable pattern in local binding + //~| `Thing::Bar` and `Thing::Baz` not covered } diff --git a/tests/ui/pattern/usefulness/issue-31561.stderr b/tests/ui/pattern/usefulness/issue-31561.stderr index 20f2f09500aa2..202fe9de5d3e6 100644 --- a/tests/ui/pattern/usefulness/issue-31561.stderr +++ b/tests/ui/pattern/usefulness/issue-31561.stderr @@ -1,4 +1,4 @@ -error[E0005]: refutable pattern in local binding: `Thing::Bar` and `Thing::Baz` not covered +error[E0005]: refutable pattern in local binding --> $DIR/issue-31561.rs:8:9 | LL | let Thing::Foo(y) = Thing::Foo(1); @@ -7,21 +7,21 @@ LL | let Thing::Foo(y) = Thing::Foo(1); = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html note: `Thing` defined here - --> $DIR/issue-31561.rs:3:5 + --> $DIR/issue-31561.rs:1:6 | LL | enum Thing { - | ----- + | ^^^^^ LL | Foo(u8), LL | Bar, - | ^^^ not covered + | --- not covered LL | Baz - | ^^^ not covered + | --- not covered = note: the matched value is of type `Thing` help: you might want to use `if let` to ignore the variants that aren't matched | LL | let y = if let Thing::Foo(y) = Thing::Foo(1) { y } else { todo!() }; | ++++++++++ ++++++++++++++++++++++ -help: alternatively, you might want to use let else to handle the variants that aren't matched +help: alternatively, you might want to use `let else` to handle the variants that aren't matched | LL | let Thing::Foo(y) = Thing::Foo(1) else { todo!() }; | ++++++++++++++++ diff --git a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs index af42fc1aeb463..5145f769075d9 100644 --- a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs +++ b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.rs @@ -15,9 +15,6 @@ enum E { //~^ NOTE `E` defined here //~| NOTE `E` defined here //~| NOTE `E` defined here - //~| NOTE `E` defined here - //~| NOTE `E` defined here - //~| NOTE `E` defined here //~| NOTE not covered //~| NOTE not covered //~| NOTE not covered @@ -41,37 +38,41 @@ fn by_val(e: E) { E::A => {} } - let E::A = e; //~ ERROR refutable pattern in local binding: `E::B` and `E::C` not covered - //~^ NOTE patterns `E::B` and `E::C` not covered + let E::A = e; + //~^ ERROR refutable pattern in local binding + //~| patterns `E::B` and `E::C` not covered //~| NOTE `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with //~| NOTE for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html //~| NOTE the matched value is of type `E` } fn by_ref_once(e: &E) { - match e { //~ ERROR non-exhaustive patterns: `&E::B` and `&E::C` not covered - //~^ NOTE patterns `&E::B` and `&E::C` not covered + match e { + //~^ ERROR non-exhaustive patterns + //~| patterns `&E::B` and `&E::C` not covered //~| NOTE the matched value is of type `&E` E::A => {} } - let E::A = e; //~ ERROR refutable pattern in local binding: `&E::B` and `&E::C` not covered - //~^ NOTE patterns `&E::B` and `&E::C` not covered + let E::A = e; + //~^ ERROR refutable pattern in local binding + //~| patterns `&E::B` and `&E::C` not covered //~| NOTE `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with //~| NOTE for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html //~| NOTE the matched value is of type `&E` } fn by_ref_thrice(e: & &mut &E) { - match e { //~ ERROR non-exhaustive patterns: `&&mut &E::B` and `&&mut &E::C` not covered - //~^ NOTE patterns `&&mut &E::B` and `&&mut &E::C` not covered + match e { + //~^ ERROR non-exhaustive patterns + //~| patterns `&&mut &E::B` and `&&mut &E::C` not covered //~| NOTE the matched value is of type `&&mut &E` E::A => {} } let E::A = e; - //~^ ERROR refutable pattern in local binding: `&&mut &E::B` and `&&mut &E::C` not covered - //~| NOTE patterns `&&mut &E::B` and `&&mut &E::C` not covered + //~^ ERROR refutable pattern in local binding + //~| patterns `&&mut &E::B` and `&&mut &E::C` not covered //~| NOTE `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with //~| NOTE for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html //~| NOTE the matched value is of type `&&mut &E` @@ -83,20 +84,21 @@ enum Opt { Some(u8), None, //~^ NOTE `Opt` defined here - //~| NOTE `Opt` defined here //~| NOTE not covered //~| NOTE not covered } fn ref_pat(e: Opt) { - match e {//~ ERROR non-exhaustive patterns: `Opt::None` not covered - //~^ NOTE pattern `Opt::None` not covered + match e { + //~^ ERROR non-exhaustive patterns + //~| pattern `Opt::None` not covered //~| NOTE the matched value is of type `Opt` Opt::Some(ref _x) => {} } - let Opt::Some(ref _x) = e; //~ ERROR refutable pattern in local binding: `Opt::None` not covered - //~^ NOTE the matched value is of type `Opt` + let Opt::Some(ref _x) = e; + //~^ ERROR refutable pattern in local binding + //~| NOTE the matched value is of type `Opt` //~| NOTE pattern `Opt::None` not covered //~| NOTE `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with //~| NOTE for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html diff --git a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr index 678c9b2ab58c3..3e375066f1b50 100644 --- a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr +++ b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `E::B` and `E::C` not covered - --> $DIR/non-exhaustive-defined-here.rs:38:11 + --> $DIR/non-exhaustive-defined-here.rs:35:11 | LL | match e1 { | ^^ patterns `E::B` and `E::C` not covered @@ -22,8 +22,8 @@ LL ~ E::A => {} LL + E::B | E::C => todo!() | -error[E0005]: refutable pattern in local binding: `E::B` and `E::C` not covered - --> $DIR/non-exhaustive-defined-here.rs:44:9 +error[E0005]: refutable pattern in local binding + --> $DIR/non-exhaustive-defined-here.rs:41:9 | LL | let E::A = e; | ^^^^ patterns `E::B` and `E::C` not covered @@ -31,16 +31,16 @@ LL | let E::A = e; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html note: `E` defined here - --> $DIR/non-exhaustive-defined-here.rs:14:5 + --> $DIR/non-exhaustive-defined-here.rs:6:6 | LL | enum E { - | - + | ^ ... LL | B, - | ^ not covered + | - not covered ... LL | C - | ^ not covered + | - not covered = note: the matched value is of type `E` help: you might want to use `if let` to ignore the variants that aren't matched | @@ -48,7 +48,7 @@ LL | if let E::A = e { todo!() } | ++ ~~~~~~~~~~~ error[E0004]: non-exhaustive patterns: `&E::B` and `&E::C` not covered - --> $DIR/non-exhaustive-defined-here.rs:52:11 + --> $DIR/non-exhaustive-defined-here.rs:50:11 | LL | match e { | ^ patterns `&E::B` and `&E::C` not covered @@ -71,8 +71,8 @@ LL ~ E::A => {} LL + &E::B | &E::C => todo!() | -error[E0005]: refutable pattern in local binding: `&E::B` and `&E::C` not covered - --> $DIR/non-exhaustive-defined-here.rs:58:9 +error[E0005]: refutable pattern in local binding + --> $DIR/non-exhaustive-defined-here.rs:57:9 | LL | let E::A = e; | ^^^^ patterns `&E::B` and `&E::C` not covered @@ -80,16 +80,16 @@ LL | let E::A = e; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html note: `E` defined here - --> $DIR/non-exhaustive-defined-here.rs:14:5 + --> $DIR/non-exhaustive-defined-here.rs:6:6 | LL | enum E { - | - + | ^ ... LL | B, - | ^ not covered + | - not covered ... LL | C - | ^ not covered + | - not covered = note: the matched value is of type `&E` help: you might want to use `if let` to ignore the variants that aren't matched | @@ -120,8 +120,8 @@ LL ~ E::A => {} LL + &&mut &E::B | &&mut &E::C => todo!() | -error[E0005]: refutable pattern in local binding: `&&mut &E::B` and `&&mut &E::C` not covered - --> $DIR/non-exhaustive-defined-here.rs:72:9 +error[E0005]: refutable pattern in local binding + --> $DIR/non-exhaustive-defined-here.rs:73:9 | LL | let E::A = e; | ^^^^ patterns `&&mut &E::B` and `&&mut &E::C` not covered @@ -129,16 +129,16 @@ LL | let E::A = e; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html note: `E` defined here - --> $DIR/non-exhaustive-defined-here.rs:14:5 + --> $DIR/non-exhaustive-defined-here.rs:6:6 | LL | enum E { - | - + | ^ ... LL | B, - | ^ not covered + | - not covered ... LL | C - | ^ not covered + | - not covered = note: the matched value is of type `&&mut &E` help: you might want to use `if let` to ignore the variants that aren't matched | @@ -152,7 +152,7 @@ LL | match e { | ^ pattern `Opt::None` not covered | note: `Opt` defined here - --> $DIR/non-exhaustive-defined-here.rs:84:5 + --> $DIR/non-exhaustive-defined-here.rs:85:5 | LL | enum Opt { | --- @@ -166,8 +166,8 @@ LL ~ Opt::Some(ref _x) => {} LL + Opt::None => todo!() | -error[E0005]: refutable pattern in local binding: `Opt::None` not covered - --> $DIR/non-exhaustive-defined-here.rs:98:9 +error[E0005]: refutable pattern in local binding + --> $DIR/non-exhaustive-defined-here.rs:99:9 | LL | let Opt::Some(ref _x) = e; | ^^^^^^^^^^^^^^^^^ pattern `Opt::None` not covered @@ -175,19 +175,19 @@ LL | let Opt::Some(ref _x) = e; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html note: `Opt` defined here - --> $DIR/non-exhaustive-defined-here.rs:84:5 + --> $DIR/non-exhaustive-defined-here.rs:81:6 | LL | enum Opt { - | --- + | ^^^ ... LL | None, - | ^^^^ not covered + | ---- not covered = note: the matched value is of type `Opt` help: you might want to use `if let` to ignore the variant that isn't matched | LL | let _x = if let Opt::Some(ref _x) = e { _x } else { todo!() }; | +++++++++++ +++++++++++++++++++++++ -help: alternatively, you might want to use let else to handle the variant that isn't matched +help: alternatively, you might want to use `let else` to handle the variant that isn't matched | LL | let Opt::Some(ref _x) = e else { todo!() }; | ++++++++++++++++ diff --git a/tests/ui/pattern/usefulness/refutable-pattern-errors.rs b/tests/ui/pattern/usefulness/refutable-pattern-errors.rs index 7c9aa51e7484c..7a3e991d59317 100644 --- a/tests/ui/pattern/usefulness/refutable-pattern-errors.rs +++ b/tests/ui/pattern/usefulness/refutable-pattern-errors.rs @@ -1,7 +1,9 @@ fn func((1, (Some(1), 2..=3)): (isize, (Option, isize))) { } -//~^ ERROR refutable pattern in function argument: `(_, _)` not covered +//~^ ERROR refutable pattern in function argument +//~| `(_, _)` not covered fn main() { let (1, (Some(1), 2..=3)) = (1, (None, 2)); - //~^ ERROR refutable pattern in local binding: `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` not covered + //~^ ERROR refutable pattern in local binding + //~| `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` not covered } diff --git a/tests/ui/pattern/usefulness/refutable-pattern-errors.stderr b/tests/ui/pattern/usefulness/refutable-pattern-errors.stderr index d1dacc822e942..c518de47740dd 100644 --- a/tests/ui/pattern/usefulness/refutable-pattern-errors.stderr +++ b/tests/ui/pattern/usefulness/refutable-pattern-errors.stderr @@ -1,4 +1,4 @@ -error[E0005]: refutable pattern in function argument: `(_, _)` not covered +error[E0005]: refutable pattern in function argument --> $DIR/refutable-pattern-errors.rs:1:9 | LL | fn func((1, (Some(1), 2..=3)): (isize, (Option, isize))) { } @@ -6,8 +6,8 @@ LL | fn func((1, (Some(1), 2..=3)): (isize, (Option, isize))) { } | = note: the matched value is of type `(isize, (Option, isize))` -error[E0005]: refutable pattern in local binding: `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` not covered - --> $DIR/refutable-pattern-errors.rs:5:9 +error[E0005]: refutable pattern in local binding + --> $DIR/refutable-pattern-errors.rs:6:9 | LL | let (1, (Some(1), 2..=3)) = (1, (None, 2)); | ^^^^^^^^^^^^^^^^^^^^^ patterns `(i32::MIN..=0_i32, _)` and `(2_i32..=i32::MAX, _)` not covered diff --git a/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs b/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs index a2d9e1935de74..17dc38ab25d92 100644 --- a/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs +++ b/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs @@ -1,5 +1,6 @@ fn main() { let f = |3: isize| println!("hello"); - //~^ ERROR refutable pattern in function argument: `_` not covered + //~^ ERROR refutable pattern in function argument + //~| `_` not covered f(4); } diff --git a/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr b/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr index c9d8cf43f95fd..55f0b2319fb7f 100644 --- a/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr +++ b/tests/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr @@ -1,4 +1,4 @@ -error[E0005]: refutable pattern in function argument: `_` not covered +error[E0005]: refutable pattern in function argument --> $DIR/refutable-pattern-in-fn-arg.rs:2:14 | LL | let f = |3: isize| println!("hello"); diff --git a/tests/ui/recursion/recursive-types-are-not-uninhabited.stderr b/tests/ui/recursion/recursive-types-are-not-uninhabited.stderr index 86ad6aa847c9f..b7521df88ec5a 100644 --- a/tests/ui/recursion/recursive-types-are-not-uninhabited.stderr +++ b/tests/ui/recursion/recursive-types-are-not-uninhabited.stderr @@ -1,4 +1,4 @@ -error[E0005]: refutable pattern in local binding: `Err(_)` not covered +error[E0005]: refutable pattern in local binding --> $DIR/recursive-types-are-not-uninhabited.rs:6:9 | LL | let Ok(x) = res; @@ -6,17 +6,12 @@ LL | let Ok(x) = res; | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html -note: `Result>` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered = note: the matched value is of type `Result>` help: you might want to use `if let` to ignore the variant that isn't matched | LL | let x = if let Ok(x) = res { x } else { todo!() }; | ++++++++++ ++++++++++++++++++++++ -help: alternatively, you might want to use let else to handle the variant that isn't matched +help: alternatively, you might want to use `let else` to handle the variant that isn't matched | LL | let Ok(x) = res else { todo!() }; | ++++++++++++++++ diff --git a/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.rs b/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.rs index ac819dce6db2b..15f08486f0f0e 100644 --- a/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.rs +++ b/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.rs @@ -1,7 +1,8 @@ fn main() { let A = 3; - //~^ ERROR refutable pattern in local binding: `i32::MIN..=1_i32` and - //~| interpreted as a constant pattern, not a new variable + //~^ ERROR refutable pattern in local binding + //~| patterns `i32::MIN..=1_i32` and `3_i32..=i32::MAX` not covered + //~| missing patterns are not covered because `a` is interpreted as a constant pattern, not a new variable //~| HELP introduce a variable instead //~| SUGGESTION a_var diff --git a/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr b/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr index 618bcaca14c4d..1c1cab25fbfaf 100644 --- a/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr +++ b/tests/ui/suggestions/const-pat-non-exaustive-let-new-var.stderr @@ -1,10 +1,11 @@ -error[E0005]: refutable pattern in local binding: `i32::MIN..=1_i32` and `3_i32..=i32::MAX` not covered +error[E0005]: refutable pattern in local binding --> $DIR/const-pat-non-exaustive-let-new-var.rs:2:9 | LL | let A = 3; | ^ | | - | interpreted as a constant pattern, not a new variable + | patterns `i32::MIN..=1_i32` and `3_i32..=i32::MAX` not covered + | missing patterns are not covered because `a` is interpreted as a constant pattern, not a new variable | help: introduce a variable instead: `a_var` ... LL | const A: i32 = 2; diff --git a/tests/ui/uninhabited/uninhabited-irrefutable.rs b/tests/ui/uninhabited/uninhabited-irrefutable.rs index 1a0f3c5e5504a..4b001aca2d1c7 100644 --- a/tests/ui/uninhabited/uninhabited-irrefutable.rs +++ b/tests/ui/uninhabited/uninhabited-irrefutable.rs @@ -24,5 +24,7 @@ enum Foo { fn main() { let x: Foo = Foo::D(123, 456); - let Foo::D(_y, _z) = x; //~ ERROR refutable pattern in local binding: `Foo::A(_)` not covered + let Foo::D(_y, _z) = x; + //~^ ERROR refutable pattern in local binding + //~| `Foo::A(_)` not covered } diff --git a/tests/ui/uninhabited/uninhabited-irrefutable.stderr b/tests/ui/uninhabited/uninhabited-irrefutable.stderr index 32f287a18188c..461863e112791 100644 --- a/tests/ui/uninhabited/uninhabited-irrefutable.stderr +++ b/tests/ui/uninhabited/uninhabited-irrefutable.stderr @@ -1,4 +1,4 @@ -error[E0005]: refutable pattern in local binding: `Foo::A(_)` not covered +error[E0005]: refutable pattern in local binding --> $DIR/uninhabited-irrefutable.rs:27:9 | LL | let Foo::D(_y, _z) = x; @@ -7,18 +7,18 @@ LL | let Foo::D(_y, _z) = x; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html note: `Foo` defined here - --> $DIR/uninhabited-irrefutable.rs:19:5 + --> $DIR/uninhabited-irrefutable.rs:18:6 | LL | enum Foo { - | --- + | ^^^ LL | A(foo::SecretlyEmpty), - | ^ not covered + | - not covered = note: the matched value is of type `Foo` help: you might want to use `if let` to ignore the variant that isn't matched | LL | let (_y, _z) = if let Foo::D(_y, _z) = x { (_y, _z) } else { todo!() }; | +++++++++++++++++ +++++++++++++++++++++++++++++ -help: alternatively, you might want to use let else to handle the variant that isn't matched +help: alternatively, you might want to use `let else` to handle the variant that isn't matched | LL | let Foo::D(_y, _z) = x else { todo!() }; | ++++++++++++++++ diff --git a/tests/ui/uninhabited/uninhabited-matches-feature-gated.stderr b/tests/ui/uninhabited/uninhabited-matches-feature-gated.stderr index d33a61ca8485c..2c67eac51f521 100644 --- a/tests/ui/uninhabited/uninhabited-matches-feature-gated.stderr +++ b/tests/ui/uninhabited/uninhabited-matches-feature-gated.stderr @@ -95,7 +95,7 @@ LL ~ Ok(x) => x, LL ~ Err(_) => todo!(), | -error[E0005]: refutable pattern in local binding: `Err(_)` not covered +error[E0005]: refutable pattern in local binding --> $DIR/uninhabited-matches-feature-gated.rs:37:9 | LL | let Ok(x) = x; @@ -103,17 +103,12 @@ LL | let Ok(x) = x; | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered = note: the matched value is of type `Result` help: you might want to use `if let` to ignore the variant that isn't matched | LL | let x = if let Ok(x) = x { x } else { todo!() }; | ++++++++++ ++++++++++++++++++++++ -help: alternatively, you might want to use let else to handle the variant that isn't matched +help: alternatively, you might want to use `let else` to handle the variant that isn't matched | LL | let Ok(x) = x else { todo!() }; | ++++++++++++++++ From 8476c517c08c21d770a80d4bf0fea28834ae45f0 Mon Sep 17 00:00:00 2001 From: mejrs <> Date: Fri, 23 Dec 2022 22:23:37 +0100 Subject: [PATCH 17/19] Don't recommend `if let` if `let else` works --- .../locales/en-US/mir_build.ftl | 2 +- compiler/rustc_mir_build/src/errors.rs | 42 +++++-------------- .../src/thir/pattern/check_match.rs | 23 ++++------ tests/ui/empty/empty-never-array.stderr | 6 +-- tests/ui/error-codes/E0005.stderr | 6 +-- .../feature-gate-exhaustive-patterns.stderr | 6 +-- .../ui/pattern/usefulness/issue-31561.stderr | 6 +-- .../non-exhaustive-defined-here.stderr | 6 +-- ...recursive-types-are-not-uninhabited.stderr | 6 +-- .../uninhabited-irrefutable.stderr | 6 +-- .../uninhabited-matches-feature-gated.stderr | 6 +-- 11 files changed, 26 insertions(+), 89 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl index b277942cdcca1..976614ecd9eba 100644 --- a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl +++ b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl @@ -358,7 +358,7 @@ mir_build_suggest_if_let = you might want to use `if let` to ignore the {$count *[other] variants that aren't } matched -mir_build_suggest_let_else = alternatively, you might want to use `let else` to handle the {$count -> +mir_build_suggest_let_else = you might want to use `let else` to handle the {$count -> [one] variant that isn't *[other] variants that aren't } matched diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index a3c58c31654fd..1b2fbae2cd5be 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -747,9 +747,7 @@ pub(crate) struct PatternNotCovered<'s, 'tcx> { pub _p: (), pub pattern_ty: Ty<'tcx>, #[subdiagnostic] - pub if_let_suggestion: Option, - #[subdiagnostic] - pub let_else_suggestion: Option, + pub let_suggestion: Option, #[subdiagnostic] pub res_defined_here: Option, } @@ -809,43 +807,23 @@ pub struct InterpretedAsConst { } #[derive(Subdiagnostic)] -pub enum SuggestIfLet { +pub enum SuggestLet { #[multipart_suggestion(mir_build_suggest_if_let, applicability = "has-placeholders")] - None { + If { #[suggestion_part(code = "if ")] start_span: Span, #[suggestion_part(code = " {{ todo!() }}")] semi_span: Span, count: usize, }, - #[multipart_suggestion(mir_build_suggest_if_let, applicability = "has-placeholders")] - One { - #[suggestion_part(code = "let {binding} = if ")] - start_span: Span, - #[suggestion_part(code = " {{ {binding} }} else {{ todo!() }}")] - end_span: Span, - binding: Ident, - count: usize, - }, - #[multipart_suggestion(mir_build_suggest_if_let, applicability = "has-placeholders")] - More { - #[suggestion_part(code = "let ({bindings}) = if ")] - start_span: Span, - #[suggestion_part(code = " {{ ({bindings}) }} else {{ todo!() }}")] + #[suggestion( + mir_build_suggest_let_else, + code = " else {{ todo!() }}", + applicability = "has-placeholders" + )] + Else { + #[primary_span] end_span: Span, - bindings: String, count: usize, }, } - -#[derive(Subdiagnostic)] -#[suggestion( - mir_build_suggest_let_else, - code = " else {{ todo!() }}", - applicability = "has-placeholders" -)] -pub struct SuggestLetElse { - #[primary_span] - pub end_span: Span, - pub count: usize, -} diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 422c2ff3ede0d..6948159289593 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -394,7 +394,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { return; } - let (inform, interpreted_as_const, res_defined_here, if_let_suggestion, let_else_suggestion) = + let (inform, interpreted_as_const, res_defined_here,let_suggestion) = if let hir::PatKind::Path(hir::QPath::Resolved( None, hir::Path { @@ -417,7 +417,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { res, } }, - None, None, + None, ) } else if let Some(span) = sp && self.tcx.sess.source_map().is_span_accessible(span) { let mut bindings = vec![]; @@ -430,19 +430,11 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { let start_span = span.shrink_to_lo(); let end_span = semi_span.shrink_to_lo(); let count = witnesses.len(); - let if_let = match *bindings { - [] => SuggestIfLet::None{start_span, semi_span, count}, - [binding] => SuggestIfLet::One{start_span, end_span, count, binding }, - _ => SuggestIfLet::More{start_span, end_span, count, bindings: bindings - .iter() - .map(|ident| ident.to_string()) - .collect::>() - .join(", ")}, - }; - let let_else = if bindings.is_empty() {None} else{Some( SuggestLetElse{end_span, count })}; - (sp.map(|_|Inform), None, None, Some(if_let), let_else) + + let let_suggestion = if bindings.is_empty() {SuggestLet::If{start_span, semi_span, count}} else{ SuggestLet::Else{end_span, count }}; + (sp.map(|_|Inform), None, None, Some(let_suggestion)) } else{ - (sp.map(|_|Inform), None, None, None, None) + (sp.map(|_|Inform), None, None, None) }; let adt_defined_here = try { @@ -465,8 +457,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { interpreted_as_const, _p: (), pattern_ty, - if_let_suggestion, - let_else_suggestion, + let_suggestion, res_defined_here, adt_defined_here, }); diff --git a/tests/ui/empty/empty-never-array.stderr b/tests/ui/empty/empty-never-array.stderr index 9385382f860bc..a488e484b2bf8 100644 --- a/tests/ui/empty/empty-never-array.stderr +++ b/tests/ui/empty/empty-never-array.stderr @@ -14,11 +14,7 @@ LL | enum Helper { LL | T(T, [!; 0]), | - not covered = note: the matched value is of type `Helper` -help: you might want to use `if let` to ignore the variant that isn't matched - | -LL | let u = if let Helper::U(u) = Helper::T(t, []) { u } else { todo!() }; - | ++++++++++ ++++++++++++++++++++++ -help: alternatively, you might want to use `let else` to handle the variant that isn't matched +help: you might want to use `let else` to handle the variant that isn't matched | LL | let Helper::U(u) = Helper::T(t, []) else { todo!() }; | ++++++++++++++++ diff --git a/tests/ui/error-codes/E0005.stderr b/tests/ui/error-codes/E0005.stderr index 3c27a7d4c064e..4692b66413dff 100644 --- a/tests/ui/error-codes/E0005.stderr +++ b/tests/ui/error-codes/E0005.stderr @@ -7,11 +7,7 @@ LL | let Some(y) = x; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `Option` -help: you might want to use `if let` to ignore the variant that isn't matched - | -LL | let y = if let Some(y) = x { y } else { todo!() }; - | ++++++++++ ++++++++++++++++++++++ -help: alternatively, you might want to use `let else` to handle the variant that isn't matched +help: you might want to use `let else` to handle the variant that isn't matched | LL | let Some(y) = x else { todo!() }; | ++++++++++++++++ diff --git a/tests/ui/feature-gates/feature-gate-exhaustive-patterns.stderr b/tests/ui/feature-gates/feature-gate-exhaustive-patterns.stderr index 5e4df7422fcf0..49e7ab6082c82 100644 --- a/tests/ui/feature-gates/feature-gate-exhaustive-patterns.stderr +++ b/tests/ui/feature-gates/feature-gate-exhaustive-patterns.stderr @@ -7,11 +7,7 @@ LL | let Ok(_x) = foo(); = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `Result` -help: you might want to use `if let` to ignore the variant that isn't matched - | -LL | let _x = if let Ok(_x) = foo() { _x } else { todo!() }; - | +++++++++++ +++++++++++++++++++++++ -help: alternatively, you might want to use `let else` to handle the variant that isn't matched +help: you might want to use `let else` to handle the variant that isn't matched | LL | let Ok(_x) = foo() else { todo!() }; | ++++++++++++++++ diff --git a/tests/ui/pattern/usefulness/issue-31561.stderr b/tests/ui/pattern/usefulness/issue-31561.stderr index 202fe9de5d3e6..5367de5e513c7 100644 --- a/tests/ui/pattern/usefulness/issue-31561.stderr +++ b/tests/ui/pattern/usefulness/issue-31561.stderr @@ -17,11 +17,7 @@ LL | Bar, LL | Baz | --- not covered = note: the matched value is of type `Thing` -help: you might want to use `if let` to ignore the variants that aren't matched - | -LL | let y = if let Thing::Foo(y) = Thing::Foo(1) { y } else { todo!() }; - | ++++++++++ ++++++++++++++++++++++ -help: alternatively, you might want to use `let else` to handle the variants that aren't matched +help: you might want to use `let else` to handle the variants that aren't matched | LL | let Thing::Foo(y) = Thing::Foo(1) else { todo!() }; | ++++++++++++++++ diff --git a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr index 3e375066f1b50..769d4070fb587 100644 --- a/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr +++ b/tests/ui/pattern/usefulness/non-exhaustive-defined-here.stderr @@ -183,11 +183,7 @@ LL | enum Opt { LL | None, | ---- not covered = note: the matched value is of type `Opt` -help: you might want to use `if let` to ignore the variant that isn't matched - | -LL | let _x = if let Opt::Some(ref _x) = e { _x } else { todo!() }; - | +++++++++++ +++++++++++++++++++++++ -help: alternatively, you might want to use `let else` to handle the variant that isn't matched +help: you might want to use `let else` to handle the variant that isn't matched | LL | let Opt::Some(ref _x) = e else { todo!() }; | ++++++++++++++++ diff --git a/tests/ui/recursion/recursive-types-are-not-uninhabited.stderr b/tests/ui/recursion/recursive-types-are-not-uninhabited.stderr index b7521df88ec5a..1b4d80d90571c 100644 --- a/tests/ui/recursion/recursive-types-are-not-uninhabited.stderr +++ b/tests/ui/recursion/recursive-types-are-not-uninhabited.stderr @@ -7,11 +7,7 @@ LL | let Ok(x) = res; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `Result>` -help: you might want to use `if let` to ignore the variant that isn't matched - | -LL | let x = if let Ok(x) = res { x } else { todo!() }; - | ++++++++++ ++++++++++++++++++++++ -help: alternatively, you might want to use `let else` to handle the variant that isn't matched +help: you might want to use `let else` to handle the variant that isn't matched | LL | let Ok(x) = res else { todo!() }; | ++++++++++++++++ diff --git a/tests/ui/uninhabited/uninhabited-irrefutable.stderr b/tests/ui/uninhabited/uninhabited-irrefutable.stderr index 461863e112791..8cafea555c172 100644 --- a/tests/ui/uninhabited/uninhabited-irrefutable.stderr +++ b/tests/ui/uninhabited/uninhabited-irrefutable.stderr @@ -14,11 +14,7 @@ LL | enum Foo { LL | A(foo::SecretlyEmpty), | - not covered = note: the matched value is of type `Foo` -help: you might want to use `if let` to ignore the variant that isn't matched - | -LL | let (_y, _z) = if let Foo::D(_y, _z) = x { (_y, _z) } else { todo!() }; - | +++++++++++++++++ +++++++++++++++++++++++++++++ -help: alternatively, you might want to use `let else` to handle the variant that isn't matched +help: you might want to use `let else` to handle the variant that isn't matched | LL | let Foo::D(_y, _z) = x else { todo!() }; | ++++++++++++++++ diff --git a/tests/ui/uninhabited/uninhabited-matches-feature-gated.stderr b/tests/ui/uninhabited/uninhabited-matches-feature-gated.stderr index 2c67eac51f521..466d7f2eadb92 100644 --- a/tests/ui/uninhabited/uninhabited-matches-feature-gated.stderr +++ b/tests/ui/uninhabited/uninhabited-matches-feature-gated.stderr @@ -104,11 +104,7 @@ LL | let Ok(x) = x; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `Result` -help: you might want to use `if let` to ignore the variant that isn't matched - | -LL | let x = if let Ok(x) = x { x } else { todo!() }; - | ++++++++++ ++++++++++++++++++++++ -help: alternatively, you might want to use `let else` to handle the variant that isn't matched +help: you might want to use `let else` to handle the variant that isn't matched | LL | let Ok(x) = x else { todo!() }; | ++++++++++++++++ From 3d260fa63c9730371b1898a73433499d2fc0b53e Mon Sep 17 00:00:00 2001 From: mejrs <> Date: Fri, 23 Dec 2022 22:26:12 +0100 Subject: [PATCH 18/19] Some cleanup, oops --- compiler/rustc_mir_build/src/thir/pattern/check_match.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 6948159289593..e13c0662ef85f 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1,8 +1,3 @@ -//#![allow(unused_imports, unused_variables)] - -//#![warn(rustc::untranslatable_diagnostic)] -//#![warn(rustc::diagnostic_outside_of_impl)] - use super::deconstruct_pat::{Constructor, DeconstructedPat}; use super::usefulness::{ compute_match_usefulness, MatchArm, MatchCheckCtxt, Reachability, UsefulnessReport, From 372ac9c1a29820f4393ba59eb49f81959bca2926 Mon Sep 17 00:00:00 2001 From: mejrs <> Date: Mon, 9 Jan 2023 22:37:56 +0100 Subject: [PATCH 19/19] Translate `Overlap` eagerly --- .../locales/en-US/mir_build.ftl | 2 -- compiler/rustc_mir_build/src/errors.rs | 19 +++++++++++++++---- .../src/thir/pattern/deconstruct_pat.rs | 9 +++------ .../overlapping_range_endpoints.stderr | 1 + 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl index 976614ecd9eba..801a5b31e02ff 100644 --- a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl +++ b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl @@ -323,8 +323,6 @@ mir_build_overlapping_range_endpoints = multiple patterns overlap on their endpo .range = ... with this range .note = you likely meant to write mutually exclusive ranges -mir_build_overlapping_range = this range overlaps on `{$range}`... - mir_build_non_exhaustive_omitted_pattern = some variants are not matched explicitly .help = ensure that all variants are matched explicitly by adding the suggested match arms .note = the matched value is of type `{$scrut_ty}` and the `non_exhaustive_omitted_patterns` attribute was found diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 1b2fbae2cd5be..39b67d995fa36 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -677,17 +677,28 @@ pub struct OverlappingRangeEndpoints<'tcx> { #[label(range)] pub range: Span, #[subdiagnostic] - pub overlap: Overlap<'tcx>, + pub overlap: Vec>, } -#[derive(Subdiagnostic)] -#[label(mir_build_overlapping_range)] pub struct Overlap<'tcx> { - #[primary_span] pub span: Span, pub range: Pat<'tcx>, } +impl<'tcx> AddToDiagnostic for Overlap<'tcx> { + fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) + where + F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, + { + let Overlap { span, range } = self; + + // FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]` + // does not support `#[subdiagnostic(eager)]`... + let message = format!("this range overlaps on `{range}`..."); + diag.span_label(span, message); + } +} + #[derive(LintDiagnostic)] #[diag(mir_build_non_exhaustive_omitted_pattern)] #[help] diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs index 323df1b8147d8..17b3c475f83c7 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -285,19 +285,16 @@ impl IntRange { return; } - // Get the first overlap. We get only the first rather than all of them - // because displaying multiple overlaps requires a way to eagerly translate - // lintdiagnostics, but that doesn't exist. - let overlap = pats + let overlap: Vec<_> = pats .filter_map(|pat| Some((pat.ctor().as_int_range()?, pat.span()))) .filter(|(range, _)| self.suspicious_intersection(range)) .map(|(range, span)| Overlap { range: self.intersection(&range).unwrap().to_pat(pcx.cx.tcx, pcx.ty), span, }) - .next(); + .collect(); - if let Some(overlap) = overlap { + if !overlap.is_empty() { pcx.cx.tcx.emit_spanned_lint( lint::builtin::OVERLAPPING_RANGE_ENDPOINTS, hir_id, diff --git a/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr index a1c802add170c..ea0e8f6e49e07 100644 --- a/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr +++ b/tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr @@ -59,6 +59,7 @@ error: multiple patterns overlap on their endpoints LL | 0..=10 => {} | ------ this range overlaps on `10_u8`... LL | 20..=30 => {} + | ------- this range overlaps on `20_u8`... LL | 10..=20 => {} | ^^^^^^^ ... with this range |