From 8f681d892f44db05f9dfead2a293f8a77d38c4c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 22 Feb 2023 23:44:12 +0000 Subject: [PATCH] When encountering `&SmallImplCopy < &SmallImplCopy`, suggest copying When encountering a binary operation for two `T: Copy` where `T` is as small as a 64bit pointer, suggest dereferencing the expressions so the binary operation is inlined. Mitigate the effects of #105259, particularly when match ergonomics is involved. --- compiler/rustc_ast/src/token.rs | 2 +- compiler/rustc_ast/src/tokenstream.rs | 2 +- compiler/rustc_ast_lowering/src/lib.rs | 2 +- compiler/rustc_ast_passes/src/feature_gate.rs | 5 +- .../src/diagnostics/conflict_errors.rs | 2 +- compiler/rustc_codegen_ssa/src/base.rs | 2 +- .../src/interpret/terminator.rs | 6 +- compiler/rustc_errors/src/emitter.rs | 4 +- compiler/rustc_expand/src/mbe/transcribe.rs | 2 +- .../rustc_hir_analysis/src/check/wfcheck.rs | 2 +- compiler/rustc_hir_pretty/src/lib.rs | 2 +- compiler/rustc_hir_typeck/src/demand.rs | 2 +- compiler/rustc_hir_typeck/src/expr.rs | 2 +- .../src/fn_ctxt/suggestions.rs | 6 +- compiler/rustc_index/src/interval.rs | 2 +- .../src/infer/error_reporting/mod.rs | 4 +- .../src/infer/error_reporting/suggest.rs | 8 +- compiler/rustc_lint/locales/en-US.ftl | 5 + compiler/rustc_lint/src/builtin.rs | 117 +++++++++++++++++- compiler/rustc_lint/src/lib.rs | 1 + compiler/rustc_lint/src/lints.rs | 30 +++++ compiler/rustc_middle/src/middle/privacy.rs | 2 +- compiler/rustc_middle/src/ty/closure.rs | 2 +- compiler/rustc_middle/src/ty/mod.rs | 4 +- compiler/rustc_middle/src/ty/util.rs | 2 +- .../src/build/expr/as_place.rs | 2 +- .../src/thir/pattern/deconstruct_pat.rs | 6 +- .../src/lower_slice_len.rs | 2 +- compiler/rustc_parse/src/lexer/tokentrees.rs | 2 +- .../rustc_parse/src/parser/diagnostics.rs | 2 +- compiler/rustc_resolve/src/diagnostics.rs | 2 +- compiler/rustc_span/src/edit_distance.rs | 4 +- compiler/rustc_span/src/lib.rs | 2 +- .../src/traits/error_reporting/mod.rs | 8 +- .../src/traits/error_reporting/suggestions.rs | 2 +- .../src/traits/select/mod.rs | 2 +- .../rustc_trait_selection/src/traits/wf.rs | 2 +- compiler/rustc_traits/src/chalk/lowering.rs | 2 +- compiler/rustc_type_ir/src/sty.rs | 14 +-- library/core/src/ffi/c_str.rs | 2 +- library/std/src/net/ip_addr.rs | 8 +- .../generic_const_exprs/issue-100360.rs | 2 +- tests/ui/issues/issue-27949.rs | 1 + tests/ui/issues/issue-54696.rs | 1 + tests/ui/lint/ref_binop_on_copy_type.fixed | 41 ++++++ tests/ui/lint/ref_binop_on_copy_type.rs | 41 ++++++ tests/ui/lint/ref_binop_on_copy_type.stderr | 68 ++++++++++ .../all-expr-kinds.rs | 2 +- ...535-allow-mutable-borrow-in-match-guard.rs | 1 + .../or-patterns-slice-patterns.rs | 2 +- .../bindings-after-at/slice-patterns.rs | 2 +- 51 files changed, 372 insertions(+), 69 deletions(-) create mode 100644 tests/ui/lint/ref_binop_on_copy_type.fixed create mode 100644 tests/ui/lint/ref_binop_on_copy_type.rs create mode 100644 tests/ui/lint/ref_binop_on_copy_type.stderr diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index f947ae4d05732..268204e95cee7 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -894,7 +894,7 @@ impl PartialEq for Nonterminal { fn eq(&self, rhs: &Self) -> bool { match (self, rhs) { (NtIdent(ident_lhs, is_raw_lhs), NtIdent(ident_rhs, is_raw_rhs)) => { - ident_lhs == ident_rhs && is_raw_lhs == is_raw_rhs + ident_lhs == ident_rhs && *is_raw_lhs == *is_raw_rhs } (NtLifetime(ident_lhs), NtLifetime(ident_rhs)) => ident_lhs == ident_rhs, // FIXME: Assume that all "complex" nonterminal are not equal, we can't compare them diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index f0a6a5e072586..3035e7d35916e 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -65,7 +65,7 @@ impl TokenTree { match (self, other) { (TokenTree::Token(token, _), TokenTree::Token(token2, _)) => token.kind == token2.kind, (TokenTree::Delimited(_, delim, tts), TokenTree::Delimited(_, delim2, tts2)) => { - delim == delim2 && tts.eq_unspanned(tts2) + *delim == *delim2 && tts.eq_unspanned(tts2) } _ => false, } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index a726fbb72e9cb..56a666b64550b 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -617,7 +617,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.impl_trait_defs = current_impl_trait_defs; self.impl_trait_bounds = current_impl_trait_bounds; - debug_assert!(!self.children.iter().any(|(id, _)| id == &def_id)); + debug_assert!(!self.children.iter().any(|(id, _)| *id == def_id)); self.children.push((def_id, hir::MaybeOwner::Owner(info))); } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index d69c84bf4d1d2..a704470471521 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -688,8 +688,9 @@ fn check_incompatible_features(sess: &Session) { .iter() .filter(|&&(f1, f2)| features.enabled(f1) && features.enabled(f2)) { - if let Some((f1_name, f1_span)) = declared_features.clone().find(|(name, _)| name == f1) { - if let Some((f2_name, f2_span)) = declared_features.clone().find(|(name, _)| name == f2) + if let Some((f1_name, f1_span)) = declared_features.clone().find(|(name, _)| *name == *f1) { + if let Some((f2_name, f2_span)) = + declared_features.clone().find(|(name, _)| *name == *f2) { let spans = vec![f1_span, f2_span]; sess.struct_span_err( diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index b2d72654a2ac9..e44f65646f999 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -2825,7 +2825,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let mut arguments = Vec::new(); for (index, argument) in sig.inputs().skip_binder().iter().enumerate() { if let ty::Ref(argument_region, _, _) = argument.kind() { - if argument_region == return_region { + if *argument_region == *return_region { // Need to use the `rustc_middle::ty` types to compare against the // `return_region`. Then use the `rustc_hir` type to get only // the lifetime span. diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 4e13d4dbcb7a4..3720468946a09 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -694,7 +694,7 @@ pub fn codegen_crate( let cgus: Vec<_> = cgu_reuse .iter() .enumerate() - .filter(|&(_, reuse)| reuse == &CguReuse::No) + .filter(|&(_, reuse)| *reuse == CguReuse::No) .take(tcx.sess.threads()) .collect(); diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index d934cfbbb84ea..89e4598fa4c73 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -258,15 +258,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { (PassMode::Pair(a1, b1), PassMode::Pair(a2, b2)) => { arg_attr_compat(a1, a2) && arg_attr_compat(b1, b2) } - (PassMode::Cast(c1, pad1), PassMode::Cast(c2, pad2)) => c1 == c2 && pad1 == pad2, + (PassMode::Cast(c1, pad1), PassMode::Cast(c2, pad2)) => c1 == c2 && *pad1 == *pad2, ( PassMode::Indirect { attrs: a1, extra_attrs: None, on_stack: s1 }, PassMode::Indirect { attrs: a2, extra_attrs: None, on_stack: s2 }, - ) => arg_attr_compat(a1, a2) && s1 == s2, + ) => arg_attr_compat(a1, a2) && *s1 == *s2, ( PassMode::Indirect { attrs: a1, extra_attrs: Some(e1), on_stack: s1 }, PassMode::Indirect { attrs: a2, extra_attrs: Some(e2), on_stack: s2 }, - ) => arg_attr_compat(a1, a2) && arg_attr_compat(e1, e2) && s1 == s2, + ) => arg_attr_compat(a1, a2) && arg_attr_compat(e1, e2) && *s1 == *s2, _ => false, }; diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 211bbf4f50e68..faff3be9a0ca2 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -351,7 +351,7 @@ pub trait Emitter: Translate { if let Some((macro_kind, name)) = has_macro_spans.first() { // Mark the actual macro this originates from let and_then = if let Some((macro_kind, last_name)) = has_macro_spans.last() - && last_name != name + && *last_name != *name { let descr = macro_kind.descr(); format!( @@ -2753,7 +2753,7 @@ pub fn is_case_difference(sm: &SourceMap, suggested: &str, sp: Span) -> bool { let ascii_confusables = &['c', 'f', 'i', 'k', 'o', 's', 'u', 'v', 'w', 'x', 'y', 'z']; // All the chars that differ in capitalization are confusable (above): let confusable = iter::zip(found.chars(), suggested.chars()) - .filter(|(f, s)| f != s) + .filter(|(f, s)| *f != *s) .all(|(f, s)| (ascii_confusables.contains(&f) || ascii_confusables.contains(&s))); confusable && found.to_lowercase() == suggested.to_lowercase() // FIXME: We sometimes suggest the same thing we already have, which is a diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index b79835be73a7e..78c69e283d02f 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -123,7 +123,7 @@ pub(super) fn transcribe<'a>( if let Frame::Sequence { idx, sep, .. } = stack.last_mut().unwrap() { let (repeat_idx, repeat_len) = repeats.last_mut().unwrap(); *repeat_idx += 1; - if repeat_idx < repeat_len { + if *repeat_idx < *repeat_len { *idx = 0; if let Some(sep) = sep { result.push(TokenTree::Token(sep.clone(), Spacing::Alone)); diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 25be62534a5da..9db2235b377ca 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -579,7 +579,7 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable>>( for (region_b, region_b_idx) in ®ions { // Again, skip `'static` because it outlives everything. Also, we trivially // know that a region outlives itself. - if ty::ReStatic == **region_b || region_a == region_b { + if ty::ReStatic == **region_b || *region_a == *region_b { continue; } if region_known_to_outlive( diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 7dcf9d8299f14..76f99689668c9 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -2092,7 +2092,7 @@ impl<'a> State<'a> { match bound { GenericBound::Trait(tref, modifier) => { - if modifier == &TraitBoundModifier::Maybe { + if *modifier == TraitBoundModifier::Maybe { self.word("?"); } self.print_poly_trait_ref(tref); diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 34d62987c3b0a..8b03fefe4d70b 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -321,7 +321,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Remove one layer of references to account for `&mut self` and // `&self`, so that we can compare it against the binding. let (ty, def_self_ty) = match (ty.kind(), def_self_ty.kind()) { - (ty::Ref(_, ty, a), ty::Ref(_, self_ty, b)) if a == b => (*ty, *self_ty), + (ty::Ref(_, ty, a), ty::Ref(_, self_ty, b)) if *a == *b => (*ty, *self_ty), _ => (ty, def_self_ty), }; let mut param_args = FxHashMap::default(); diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 4a7bb1b04391e..493f5bb4e12e6 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1739,7 +1739,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_has_type_or_error(base_expr, adt_ty, |_| { let base_ty = self.typeck_results.borrow().expr_ty(*base_expr); let same_adt = match (adt_ty.kind(), base_ty.kind()) { - (ty::Adt(adt, _), ty::Adt(base_adt, _)) if adt == base_adt => true, + (ty::Adt(adt, _), ty::Adt(base_adt, _)) if *adt == *base_adt => true, _ => false, }; if self.tcx.sess.is_nightly_build() && same_adt { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 1c7f131f3d7e3..489e9ca5e0a4c 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -815,7 +815,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }) .map(|(ty, bounds)| match ty.kind() { - ty::Param(param_ty) if param_ty == expected_ty_as_param => Ok(Some(bounds)), + ty::Param(param_ty) if *param_ty == *expected_ty_as_param => Ok(Some(bounds)), // check whether there is any predicate that contains our `T`, like `Option: Send` _ => match ty.contains(expected) { true => Err(()), @@ -848,7 +848,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty_param_used_in_fn_params = fn_parameters.iter().any(|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) + matches!(ty.kind(), ty::Param(fn_param_ty_param) if *expected_ty_as_param == *fn_param_ty_param) }); if ty_param_used_in_fn_params { @@ -1008,7 +1008,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> bool { let ty::Adt(adt_def, substs) = expr_ty.kind() else { return false; }; let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return false; }; - if adt_def != expected_adt_def { + if *adt_def != *expected_adt_def { return false; } diff --git a/compiler/rustc_index/src/interval.rs b/compiler/rustc_index/src/interval.rs index d809740c6ab31..187791034c183 100644 --- a/compiler/rustc_index/src/interval.rs +++ b/compiler/rustc_index/src/interval.rs @@ -223,7 +223,7 @@ impl IntervalSet { fn check_invariants(&self) -> bool { let mut current: Option = None; for (start, end) in &self.map { - if start > end || current.map_or(false, |x| x + 1 >= *start) { + if *start > *end || current.map_or(false, |x| x + 1 >= *start) { return false; } current = Some(*end); diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 95635405f894d..e910ae0356745 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1144,7 +1144,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let remainder2: Vec<_> = sub2.types().skip(common_len).collect(); let common_default_params = iter::zip(remainder1.iter().rev(), remainder2.iter().rev()) - .filter(|(a, b)| a == b) + .filter(|(a, b)| **a == **b) .count(); let len = sub1.len() - common_default_params; let consts_offset = len - sub1.consts().count(); @@ -2028,7 +2028,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }), .. } = s - && init_span == &self.span { + && *init_span == self.span { self.result = Some(*array_ty); } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index 55dcfd05e0ad3..750c8e10005e3 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -449,7 +449,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { (expected.kind(), found.kind()) { if let ty::Adt(found_def, found_substs) = *found_ty.kind() { - if exp_def == &found_def { + if *exp_def == found_def { let have_as_ref = &[ ( sym::Option, @@ -581,7 +581,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ( ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, .. }), ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, .. }), - ) if last_def_id == exp_def_id => StatementAsExpression::CorrectType, + ) if *last_def_id == *exp_def_id => StatementAsExpression::CorrectType, ( ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, substs: last_bounds, .. }), ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, substs: exp_bounds, .. }), @@ -607,14 +607,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { hir::GenericBound::Trait(tl, ml), hir::GenericBound::Trait(tr, mr), ) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id() - && ml == mr => + && *ml == *mr => { true } ( hir::GenericBound::LangItemTrait(langl, _, _, argsl), hir::GenericBound::LangItemTrait(langr, _, _, argsr), - ) if langl == langr => { + ) if *langl == *langr => { // FIXME: consider the bounds! debug!("{:?} {:?}", argsl, argsr); true diff --git a/compiler/rustc_lint/locales/en-US.ftl b/compiler/rustc_lint/locales/en-US.ftl index b1e7cc69a809b..0227b1cf8d4c5 100644 --- a/compiler/rustc_lint/locales/en-US.ftl +++ b/compiler/rustc_lint/locales/en-US.ftl @@ -5,6 +5,11 @@ lint_array_into_iter = .use_explicit_into_iter_suggestion = or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value +lint_ref_binop_on_copy_type = + binary operation on reference to `Copy` type `{$ty}` + .note = `{$ty}` takes `{$bytes}` bytes of memory; copying the value instead of referencing it might avoid unnecessary pointer indirections + .suggestion = dereferencing the expressions will allow the compiler to more consistently optimize these binary operations + lint_enum_intrinsics_mem_discriminant = the return value of `mem::discriminant` is unspecified when called with a non-enum type .note = the argument to `discriminant` should be a reference to an enum, but it was passed a reference to a `{$ty_param}`, which is not an enum. diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 4da7f3f502f12..1c2c38d8e6239 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -38,7 +38,8 @@ use crate::{ BuiltinUngatedAsyncFnTrackCaller, BuiltinUnnameableTestItems, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, - BuiltinWhileTrue, SuggestChangingAssocTypes, + BuiltinWhileTrue, RefBinopOnCopyTypeDiag, RefBinopOnCopyTypeSuggestion, + SuggestChangingAssocTypes, }, types::{transparent_newtype_field, CItemKind}, EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext, @@ -2888,7 +2889,7 @@ impl ClashingExternDeclarations { } (Ref(_a_region, a_ty, a_mut), Ref(_b_region, b_ty, b_mut)) => { // For structural sameness, we don't need the region to be same. - a_mut == b_mut + *a_mut == *b_mut && structurally_same_type_impl(seen_types, cx, *a_ty, *b_ty, ckind) } (FnDef(..), FnDef(..)) => { @@ -3307,6 +3308,118 @@ impl EarlyLintPass for SpecialModuleName { } } +declare_lint! { + /// The `ref_binop_on_copy_type` lint detects borrowed `Copy` types being passed to binary + /// operations that have unnecessary borrows. + /// + /// ### Example + /// + /// ```rust + /// # #![allow(unused)] + /// pub fn slice_of_ints(input: &[(usize, usize, usize, usize)]) -> usize { + /// input + /// .iter() + /// .filter(|(a, b, c, d)| a <= c && d <= b || c <= a && b <= d) + /// .count() + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// When making comparisons (or other binary operations) between two reference values that can + /// be copied instead, the compiler will not always remove the references, making the execution + /// of the binary operation slower than it otherwise could be by making the machine code "chase + /// pointers" before actually finding the underlying value. If the `Copy` value is as small or + /// smaller than a 64 bit pointer, we suggest dereferencing the value so the compiler will have + /// a better chance of producing optimal instructions. + REF_BINOP_ON_COPY_TYPE, + Warn, + "detects binary operations on references to `Copy` types like `&42 < &50`", +} + +declare_lint_pass!(RefBinopOnCopyType => [REF_BINOP_ON_COPY_TYPE]); + +impl<'tcx> LateLintPass<'tcx> for RefBinopOnCopyType { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + let hir::ExprKind::Binary(op, left, right) = expr.kind else { return; }; + let left_ty = cx.typeck_results().expr_ty_adjusted(left); + let right_ty = cx.typeck_results().expr_ty_adjusted(right); + if let ty::Ref(_, left_ty, _) = left_ty.kind() + && let ty::Ref(_, left_ty, _) = left_ty.kind() + && let left_ty = left_ty.peel_refs() + && left_ty.is_copy_modulo_regions(cx.tcx, cx.param_env) + && let Some(size) = cx.tcx.layout_of(cx.param_env.and(left_ty)).ok().map(|layout| { + layout.size.bytes() + }) + && let ty::Ref(_, right_ty, _) = right_ty.kind() + && let ty::Ref(_, right_ty, _) = right_ty.kind() + && let right_ty = right_ty.peel_refs() + && right_ty.is_copy_modulo_regions(cx.tcx, cx.param_env) + && let Some(size_r) = cx.tcx.layout_of(cx.param_env.and(right_ty)).ok().map(|layout| { + layout.size.bytes() + }) + && size <= 8 + && size_r <= 8 + { + let left_start_base = left.span.shrink_to_lo(); + let left_peeled = left.peel_borrows(); + let left_start = left_start_base.to(left_peeled.span.shrink_to_lo()); + let left_sugg = if left_start != left_start_base + && !matches!(cx.typeck_results().expr_ty_adjusted(left_peeled).kind(), ty::Ref(..)) + { + "" + } else { + "*" + }; + let (left_brace_start, left_brace_end, left_end) = + if left.precedence().order() < ast::ExprPrecedence::Unary.order() { + ("(", ")", Some(left.span.shrink_to_hi())) + } else { + ("", "", None) + }; + let right_start_base = right.span.shrink_to_lo(); + let right_peeled = right.peel_borrows(); + let right_start = right_start_base.to(right_peeled.span.shrink_to_lo()); + let right_sugg = if right_start != right_start_base + && !matches!(cx.typeck_results().expr_ty_adjusted(right_peeled).kind(), ty::Ref(..)) + { + "" + } else { + "*" + }; + let (right_brace_start, right_brace_end, right_end) = + if right.precedence().order() < ast::ExprPrecedence::Unary.order() { + ("(", ")", Some(right.span.shrink_to_hi())) + } else { + ("", "", None) + }; + cx.emit_spanned_lint( + REF_BINOP_ON_COPY_TYPE, + op.span, + RefBinopOnCopyTypeDiag { + ty: with_no_trimmed_paths!(left_ty.to_string()), + bytes: size, + note: Some(()), + suggestion: RefBinopOnCopyTypeSuggestion { + left_brace_start, + left_brace_end, + left_sugg, + left_start, + left_end, + right_brace_start, + right_brace_end, + right_sugg, + right_start, + right_end, + }, + }, + ); + } + } +} + pub use rustc_session::lint::builtin::UNEXPECTED_CFGS; declare_lint_pass!(UnexpectedCfgs => [UNEXPECTED_CFGS]); diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 2070ffea4d99e..a733eb9e158f9 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -204,6 +204,7 @@ late_lint_methods!( ImproperCTypesDefinitions: ImproperCTypesDefinitions, VariantSizeDifferences: VariantSizeDifferences, BoxPointers: BoxPointers, + RefBinopOnCopyType: RefBinopOnCopyType, PathStatements: PathStatements, LetUnderscore: LetUnderscore, // Depends on referenced function signatures in expressions diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 2d9aa9074be79..7eee1ae201a2d 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -49,6 +49,36 @@ pub enum ArrayIntoIterDiagSub { }, } +#[derive(LintDiagnostic)] +#[diag(lint_ref_binop_on_copy_type)] +pub struct RefBinopOnCopyTypeDiag<'a> { + pub ty: String, + pub bytes: u64, + #[subdiagnostic] + pub suggestion: RefBinopOnCopyTypeSuggestion<'a>, + #[note] + pub note: Option<()>, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")] +pub struct RefBinopOnCopyTypeSuggestion<'a> { + pub left_brace_start: &'a str, + pub left_brace_end: &'a str, + pub left_sugg: &'a str, + #[suggestion_part(code = "{left_sugg}{left_brace_start}")] + pub left_start: Span, + #[suggestion_part(code = "{left_brace_end}")] + pub left_end: Option, + pub right_brace_start: &'a str, + pub right_brace_end: &'a str, + pub right_sugg: &'a str, + #[suggestion_part(code = "{right_sugg}{right_brace_start}")] + pub right_start: Span, + #[suggestion_part(code = "{right_brace_end}")] + pub right_end: Option, +} + // builtin.rs #[derive(LintDiagnostic)] #[diag(lint_builtin_while_true)] diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 893bf54b8660b..64b0ada7ea3b9 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -121,7 +121,7 @@ impl EffectiveVisibilities { for l in Level::all_levels() { let vis_at_level = eff_vis.at_level(l); let old_vis_at_level = old_eff_vis.at_level_mut(l); - if vis_at_level != old_vis_at_level + if *vis_at_level != *old_vis_at_level && vis_at_level.is_at_least(*old_vis_at_level, tree) { *old_vis_at_level = *vis_at_level diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 6ade8935fc84f..29932b063f2e5 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -267,7 +267,7 @@ pub fn is_ancestor_or_same_capture( return false; } - proj_possible_ancestor.iter().zip(proj_capture).all(|(a, b)| a == b) + proj_possible_ancestor.iter().zip(proj_capture).all(|(a, b)| *a == *b) } /// Part of `MinCaptureInformationMap`; describes the capture kind (&, &mut, move) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 7dcc3ff4e7b33..5103c84644fc5 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1941,7 +1941,7 @@ impl PartialEq for VariantDef { let Self { def_id: lhs_def_id, ctor: _, name: _, discr: _, fields: _, flags: _ } = &self; let Self { def_id: rhs_def_id, ctor: _, name: _, discr: _, fields: _, flags: _ } = other; - lhs_def_id == rhs_def_id + *lhs_def_id == *rhs_def_id } } @@ -1996,7 +1996,7 @@ impl PartialEq for FieldDef { let Self { did: rhs_did, name: _, vis: _ } = other; - lhs_did == rhs_did + *lhs_did == *rhs_did } } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 2ac3adda80b94..d42759d47b458 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -315,7 +315,7 @@ impl<'tcx> TyCtxt<'tcx> { loop { match (&a.kind(), &b.kind()) { (&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs)) - if a_def == b_def && a_def.is_struct() => + if *a_def == *b_def && a_def.is_struct() => { if let Some(f) = a_def.non_enum_variant().fields.last() { a = f.ty(self, a_substs); diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index e22fa6365dcb4..de39ccbb4171e 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -143,7 +143,7 @@ fn is_ancestor_or_same_capture( return false; } - iter::zip(proj_possible_ancestor, proj_capture).all(|(a, b)| a == b) + iter::zip(proj_possible_ancestor, proj_capture).all(|(a, b)| *a == *b) } /// Given a closure, returns the index of a capture within the desugared closure struct and the 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 e5b7d685c499b..f441b39b5168d 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs @@ -803,7 +803,7 @@ impl<'tcx> Constructor<'tcx> { (Missing { .. } | Wildcard, _) => false, (Single, Single) => true, - (Variant(self_id), Variant(other_id)) => self_id == other_id, + (Variant(self_id), Variant(other_id)) => *self_id == *other_id, (IntRange(self_range), IntRange(other_range)) => self_range.is_covered_by(other_range), ( @@ -817,7 +817,7 @@ impl<'tcx> Constructor<'tcx> { (Some(to), Some(from)) => { (from == Ordering::Greater || from == Ordering::Equal) && (to == Ordering::Less - || (other_end == self_end && to == Ordering::Equal)) + || (*other_end == *self_end && to == Ordering::Equal)) } _ => false, } @@ -859,7 +859,7 @@ impl<'tcx> Constructor<'tcx> { match self { // If `self` is `Single`, `used_ctors` cannot contain anything else than `Single`s. Single => !used_ctors.is_empty(), - Variant(vid) => used_ctors.iter().any(|c| matches!(c, Variant(i) if i == vid)), + Variant(vid) => used_ctors.iter().any(|c| matches!(c, Variant(i) if *i == *vid)), IntRange(range) => used_ctors .iter() .filter_map(|c| c.as_int_range()) diff --git a/compiler/rustc_mir_transform/src/lower_slice_len.rs b/compiler/rustc_mir_transform/src/lower_slice_len.rs index 2f02d00ec9fb0..d2a0a6fd3bb3f 100644 --- a/compiler/rustc_mir_transform/src/lower_slice_len.rs +++ b/compiler/rustc_mir_transform/src/lower_slice_len.rs @@ -65,7 +65,7 @@ fn lower_slice_len_call<'tcx>( let Some(arg) = args[0].place() else { return }; let func_ty = func.ty(local_decls, tcx); match func_ty.kind() { - ty::FnDef(fn_def_id, _) if fn_def_id == &slice_len_fn_item_def_id => { + ty::FnDef(fn_def_id, _) if *fn_def_id == slice_len_fn_item_def_id => { // perform modifications // from something like `_5 = core::slice::::len(move _6) -> bb1` // into `_5 = Len(*_6) diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index 0de8f79112c65..fc2e01b7f12e7 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -153,7 +153,7 @@ impl<'a> TokenTreesReader<'a> { }; for (brace, brace_span) in &self.diag_info.open_braces { if same_identation_level(&sm, self.token.span, *brace_span) - && brace == &close_delim + && *brace == close_delim { // high likelihood of these two corresponding candidate = Some(*brace_span); diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index d235b8a8176a8..8a6e103081f55 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -414,7 +414,7 @@ impl<'a> Parser<'a> { fn is_ident_eq_keyword(found: &TokenKind, expected: &TokenType) -> bool { if let TokenKind::Ident(current_sym, _) = found { if let TokenType::Keyword(suggested_sym) = expected { - return current_sym == suggested_sym; + return *current_sym == *suggested_sym; } } false diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 80bc0a2653d9f..34379a46cf228 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -207,7 +207,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { (ident.name, self.tcx.sess.source_map().guess_head_span(new_binding.span)); if let Some(s) = self.name_already_seen.get(&name) { - if s == &span { + if *s == span { return; } } diff --git a/compiler/rustc_span/src/edit_distance.rs b/compiler/rustc_span/src/edit_distance.rs index 89f0386e3e97f..6d05c5d68db5e 100644 --- a/compiler/rustc_span/src/edit_distance.rs +++ b/compiler/rustc_span/src/edit_distance.rs @@ -37,14 +37,14 @@ pub fn edit_distance(a: &str, b: &str, limit: usize) -> Option { // Strip common prefix. while let Some(((b_char, b_rest), (a_char, a_rest))) = b.split_first().zip(a.split_first()) - && a_char == b_char + && *a_char == *b_char { a = a_rest; b = b_rest; } // Strip common suffix. while let Some(((b_char, b_rest), (a_char, a_rest))) = b.split_last().zip(a.split_last()) - && a_char == b_char + && *a_char == *b_char { a = a_rest; b = b_rest; diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index e112100aa5fcd..368043721a52c 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -1655,7 +1655,7 @@ impl SourceFile { /// number. If the source_file is empty or the position is located before the /// first line, `None` is returned. pub fn lookup_line(&self, pos: BytePos) -> Option { - self.lines(|lines| lines.partition_point(|x| x <= &pos).checked_sub(1)) + self.lines(|lines| lines.partition_point(|x| *x <= pos).checked_sub(1)) } pub fn line_bounds(&self, line_index: usize) -> Range { diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index a844a1494e262..20cd9216a8f34 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -1965,8 +1965,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { Some(CandidateSimilarity::Exact { ignoring_lifetimes }) } else if cat_a == cat_b { match (a.kind(), b.kind()) { - (ty::Adt(def_a, _), ty::Adt(def_b, _)) => def_a == def_b, - (ty::Foreign(def_a), ty::Foreign(def_b)) => def_a == def_b, + (ty::Adt(def_a, _), ty::Adt(def_b, _)) => *def_a == *def_b, + (ty::Foreign(def_a), ty::Foreign(def_b)) => *def_a == *def_b, // Matching on references results in a lot of unhelpful // suggestions, so let's just not do that for now. // @@ -2849,14 +2849,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { if let ObligationCauseCode::BuiltinDerivedObligation(ref data) = cause_code { let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); let self_ty = parent_trait_ref.skip_binder().self_ty(); - if obligated_types.iter().any(|ot| ot == &self_ty) { + if obligated_types.iter().any(|ot| *ot == self_ty) { return true; } if let ty::Adt(def, substs) = self_ty.kind() && let [arg] = &substs[..] && let ty::subst::GenericArgKind::Type(ty) = arg.unpack() && let ty::Adt(inner_def, _) = ty.kind() - && inner_def == def + && *inner_def == *def { return true; } 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 84945ff28fe57..17198e74bbb65 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2100,7 +2100,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .find(|(other_idx, (pred, _))| match pred.kind().skip_binder() { ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) if self.tcx.is_fn_trait(trait_pred.def_id()) - && other_idx != idx + && *other_idx != *idx // Make sure that the self type matches // (i.e. constraining this closure) && expected_self diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index e7e0f8838a432..8a37a11b8d00a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1967,7 +1967,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | (ObjectCandidate(i), ObjectCandidate(j)) => { // Arbitrarily pick the lower numbered candidate for backwards // compatibility reasons. Don't let this affect inference. - i < j && !needs_infer + *i < *j && !needs_infer } (ObjectCandidate(_), ProjectionCandidate(..)) | (ProjectionCandidate(..), ObjectCandidate(_)) => { diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index d498af359c584..8089a3960315c 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -951,7 +951,7 @@ pub(crate) fn required_region_bounds<'tcx>( // it's kind of a moot point since you could never // construct such an object, but this seems // correct even if that code changes). - if t == &erased_self_ty && !r.has_escaping_bound_vars() { + if *t == erased_self_ty && !r.has_escaping_bound_vars() { Some(*r) } else { None diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index fe80de5a06907..a42802d2dac09 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -1086,7 +1086,7 @@ impl<'tcx> TypeFolder> for ParamsSubstitutor<'tcx> { fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { match *t.kind() { - ty::Param(param) => match self.list.iter().position(|r| r == ¶m) { + ty::Param(param) => match self.list.iter().position(|r| *r == param) { Some(idx) => self.tcx.mk_placeholder(ty::PlaceholderType { universe: ty::UniverseIndex::from_usize(0), name: ty::BoundTyKind::Anon(idx as u32), diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs index ebe2b76aef335..6a8c6a6da4a55 100644 --- a/compiler/rustc_type_ir/src/sty.rs +++ b/compiler/rustc_type_ir/src/sty.rs @@ -315,9 +315,9 @@ impl PartialEq for TyKind { // but the data patterns in practice are such that a comparison // succeeds 99%+ of the time, and it's faster to omit it. match (self, other) { - (Int(a_i), Int(b_i)) => a_i == b_i, - (Uint(a_u), Uint(b_u)) => a_u == b_u, - (Float(a_f), Float(b_f)) => a_f == b_f, + (Int(a_i), Int(b_i)) => *a_i == *b_i, + (Uint(a_u), Uint(b_u)) => *a_u == *b_u, + (Float(a_f), Float(b_f)) => *a_f == *b_f, (Adt(a_d, a_s), Adt(b_d, b_s)) => a_d == b_d && a_s == b_s, (Foreign(a_d), Foreign(b_d)) => a_d == b_d, (Array(a_t, a_c), Array(b_t, b_c)) => a_t == b_t && a_c == b_c, @@ -327,7 +327,7 @@ impl PartialEq for TyKind { (FnDef(a_d, a_s), FnDef(b_d, b_s)) => a_d == b_d && a_s == b_s, (FnPtr(a_s), FnPtr(b_s)) => a_s == b_s, (Dynamic(a_p, a_r, a_repr), Dynamic(b_p, b_r, b_repr)) => { - a_p == b_p && a_r == b_r && a_repr == b_repr + a_p == b_p && a_r == b_r && *a_repr == *b_repr } (Closure(a_d, a_s), Closure(b_d, b_s)) => a_d == b_d && a_s == b_s, (Generator(a_d, a_s, a_m), Generator(b_d, b_s, b_m)) => { @@ -338,9 +338,9 @@ impl PartialEq for TyKind { a_d == b_d && a_s == b_s } (Tuple(a_t), Tuple(b_t)) => a_t == b_t, - (Alias(a_i, a_p), Alias(b_i, b_p)) => a_i == b_i && a_p == b_p, + (Alias(a_i, a_p), Alias(b_i, b_p)) => *a_i == *b_i && *a_p == *b_p, (Param(a_p), Param(b_p)) => a_p == b_p, - (Bound(a_d, a_b), Bound(b_d, b_b)) => a_d == b_d && a_b == b_b, + (Bound(a_d, a_b), Bound(b_d, b_b)) => *a_d == *b_d && *a_b == *b_b, (Placeholder(a_p), Placeholder(b_p)) => a_p == b_p, (Infer(a_t), Infer(b_t)) => a_t == b_t, (Error(a_e), Error(b_e)) => a_e == b_e, @@ -1014,7 +1014,7 @@ impl PartialEq for RegionKind { regionkind_discriminant(self) == regionkind_discriminant(other) && match (self, other) { (ReEarlyBound(a_r), ReEarlyBound(b_r)) => a_r == b_r, - (ReLateBound(a_d, a_r), ReLateBound(b_d, b_r)) => a_d == b_d && a_r == b_r, + (ReLateBound(a_d, a_r), ReLateBound(b_d, b_r)) => *a_d == *b_d && *a_r == *b_r, (ReFree(a_r), ReFree(b_r)) => a_r == b_r, (ReStatic, ReStatic) => true, (ReVar(a_r), ReVar(b_r)) => a_r == b_r, diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 87f077325f8be..1b4c9ee9dcb91 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -535,7 +535,7 @@ impl CStr { pub const fn is_empty(&self) -> bool { // SAFETY: We know there is at least one byte; for empty strings it // is the NUL terminator. - (unsafe { self.inner.get_unchecked(0) }) == &0 + *(unsafe { self.inner.get_unchecked(0) }) == 0 } /// Converts this C string to a byte slice. diff --git a/library/std/src/net/ip_addr.rs b/library/std/src/net/ip_addr.rs index 07f08c1b5869d..05d014cf81ddf 100644 --- a/library/std/src/net/ip_addr.rs +++ b/library/std/src/net/ip_addr.rs @@ -1018,7 +1018,7 @@ impl PartialEq for IpAddr { #[inline] fn eq(&self, other: &Ipv4Addr) -> bool { match self { - IpAddr::V4(v4) => v4 == other, + IpAddr::V4(v4) => *v4 == *other, IpAddr::V6(_) => false, } } @@ -1029,7 +1029,7 @@ impl PartialEq for Ipv4Addr { #[inline] fn eq(&self, other: &IpAddr) -> bool { match other { - IpAddr::V4(v4) => self == v4, + IpAddr::V4(v4) => *self == *v4, IpAddr::V6(_) => false, } } @@ -1875,7 +1875,7 @@ impl PartialEq for Ipv6Addr { fn eq(&self, other: &IpAddr) -> bool { match other { IpAddr::V4(_) => false, - IpAddr::V6(v6) => self == v6, + IpAddr::V6(v6) => *self == *v6, } } } @@ -1886,7 +1886,7 @@ impl PartialEq for IpAddr { fn eq(&self, other: &Ipv6Addr) -> bool { match self { IpAddr::V4(_) => false, - IpAddr::V6(v6) => v6 == other, + IpAddr::V6(v6) => *v6 == *other, } } } diff --git a/tests/ui/const-generics/generic_const_exprs/issue-100360.rs b/tests/ui/const-generics/generic_const_exprs/issue-100360.rs index 5572f1f88df4c..02ed67bcae23e 100644 --- a/tests/ui/const-generics/generic_const_exprs/issue-100360.rs +++ b/tests/ui/const-generics/generic_const_exprs/issue-100360.rs @@ -2,7 +2,7 @@ // (this requires debug assertions) #![feature(adt_const_params)] -#![allow(incomplete_features)] +#![allow(incomplete_features, ref_binop_on_copy_type)] fn foo(arg: &'static bool) -> bool { B == arg diff --git a/tests/ui/issues/issue-27949.rs b/tests/ui/issues/issue-27949.rs index e905da72aad70..d85cf5e99ed0a 100644 --- a/tests/ui/issues/issue-27949.rs +++ b/tests/ui/issues/issue-27949.rs @@ -5,6 +5,7 @@ // LHS and RHS to be exactly identical--i.e. to have the same lifetimes. // // This was fixed in 1a7fb7dc78439a704f024609ce3dc0beb1386552. +#![allow(ref_binop_on_copy_type)] #[derive(Copy, Clone)] struct Input<'a> { diff --git a/tests/ui/issues/issue-54696.rs b/tests/ui/issues/issue-54696.rs index 15355d30db6a5..3748d01c58420 100644 --- a/tests/ui/issues/issue-54696.rs +++ b/tests/ui/issues/issue-54696.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(ref_binop_on_copy_type)] fn main() { // We shouldn't promote this diff --git a/tests/ui/lint/ref_binop_on_copy_type.fixed b/tests/ui/lint/ref_binop_on_copy_type.fixed new file mode 100644 index 0000000000000..bc8b61426293e --- /dev/null +++ b/tests/ui/lint/ref_binop_on_copy_type.fixed @@ -0,0 +1,41 @@ +// run-rustfix +#![deny(ref_binop_on_copy_type)] +// #105259 + +fn main() { + let input = vec![ + (2, 4, 6, 8), + (2, 3, 4, 5), + (5, 7, 7, 9), + (2, 8, 3, 7), + (6, 6, 4, 6), + (2, 6, 4, 8), // .. 500000000 lines of random data, read from disk with real code (~12GB) + ]; + + // 1761ms on my machine + let _variant_a_result = variant_a(&input); + + // 656ms on my machine + let _variant_b_result = variant_b(&input); + + let _ = 42 <= 0; + //~^ ERROR binary operation on reference +} + +pub fn variant_a(input: &[(usize, usize, usize, usize)]) -> usize { + input + .iter() + .filter(|(a, b, c, d)| *a <= *c && *(d as &usize) <= *b || *c <= *a && *b <= *d) + //~^ ERROR binary operation on reference + //~| ERROR binary operation on reference + //~| ERROR binary operation on reference + //~| ERROR binary operation on reference + .count() +} + +pub fn variant_b(input: &[(usize, usize, usize, usize)]) -> usize { + input + .iter() + .filter(|&&(a, b, c, d)| a <= c && d + &2usize <= b || c <= a && b <= d) + .count() +} diff --git a/tests/ui/lint/ref_binop_on_copy_type.rs b/tests/ui/lint/ref_binop_on_copy_type.rs new file mode 100644 index 0000000000000..40ce74ffe06ca --- /dev/null +++ b/tests/ui/lint/ref_binop_on_copy_type.rs @@ -0,0 +1,41 @@ +// run-rustfix +#![deny(ref_binop_on_copy_type)] +// #105259 + +fn main() { + let input = vec![ + (2, 4, 6, 8), + (2, 3, 4, 5), + (5, 7, 7, 9), + (2, 8, 3, 7), + (6, 6, 4, 6), + (2, 6, 4, 8), // .. 500000000 lines of random data, read from disk with real code (~12GB) + ]; + + // 1761ms on my machine + let _variant_a_result = variant_a(&input); + + // 656ms on my machine + let _variant_b_result = variant_b(&input); + + let _ = &42 <= &0; + //~^ ERROR binary operation on reference +} + +pub fn variant_a(input: &[(usize, usize, usize, usize)]) -> usize { + input + .iter() + .filter(|(a, b, c, d)| a <= c && d as &usize <= b || c <= a && &b <= &d) + //~^ ERROR binary operation on reference + //~| ERROR binary operation on reference + //~| ERROR binary operation on reference + //~| ERROR binary operation on reference + .count() +} + +pub fn variant_b(input: &[(usize, usize, usize, usize)]) -> usize { + input + .iter() + .filter(|&&(a, b, c, d)| a <= c && d + &2usize <= b || c <= a && b <= d) + .count() +} diff --git a/tests/ui/lint/ref_binop_on_copy_type.stderr b/tests/ui/lint/ref_binop_on_copy_type.stderr new file mode 100644 index 0000000000000..c53cafcc984e1 --- /dev/null +++ b/tests/ui/lint/ref_binop_on_copy_type.stderr @@ -0,0 +1,68 @@ +error: binary operation on reference to `Copy` type `i32` + --> $DIR/ref_binop_on_copy_type.rs:21:17 + | +LL | let _ = &42 <= &0; + | ^^ + | + = note: `i32` takes `4` bytes of memory; copying the value instead of referencing it might avoid unnecessary pointer indirections +note: the lint level is defined here + --> $DIR/ref_binop_on_copy_type.rs:2:9 + | +LL | #![deny(ref_binop_on_copy_type)] + | ^^^^^^^^^^^^^^^^^^^^^^ +help: dereferencing the expressions will allow the compiler to more consistently optimize these binary operations + | +LL - let _ = &42 <= &0; +LL + let _ = 42 <= 0; + | + +error: binary operation on reference to `Copy` type `usize` + --> $DIR/ref_binop_on_copy_type.rs:28:34 + | +LL | .filter(|(a, b, c, d)| a <= c && d as &usize <= b || c <= a && &b <= &d) + | ^^ + | + = note: `usize` takes `8` bytes of memory; copying the value instead of referencing it might avoid unnecessary pointer indirections +help: dereferencing the expressions will allow the compiler to more consistently optimize these binary operations + | +LL | .filter(|(a, b, c, d)| *a <= *c && d as &usize <= b || c <= a && &b <= &d) + | + + + +error: binary operation on reference to `Copy` type `usize` + --> $DIR/ref_binop_on_copy_type.rs:28:54 + | +LL | .filter(|(a, b, c, d)| a <= c && d as &usize <= b || c <= a && &b <= &d) + | ^^ + | + = note: `usize` takes `8` bytes of memory; copying the value instead of referencing it might avoid unnecessary pointer indirections +help: dereferencing the expressions will allow the compiler to more consistently optimize these binary operations + | +LL | .filter(|(a, b, c, d)| a <= c && *(d as &usize) <= *b || c <= a && &b <= &d) + | ++ + + + +error: binary operation on reference to `Copy` type `usize` + --> $DIR/ref_binop_on_copy_type.rs:28:64 + | +LL | .filter(|(a, b, c, d)| a <= c && d as &usize <= b || c <= a && &b <= &d) + | ^^ + | + = note: `usize` takes `8` bytes of memory; copying the value instead of referencing it might avoid unnecessary pointer indirections +help: dereferencing the expressions will allow the compiler to more consistently optimize these binary operations + | +LL | .filter(|(a, b, c, d)| a <= c && d as &usize <= b || *c <= *a && &b <= &d) + | + + + +error: binary operation on reference to `Copy` type `usize` + --> $DIR/ref_binop_on_copy_type.rs:28:75 + | +LL | .filter(|(a, b, c, d)| a <= c && d as &usize <= b || c <= a && &b <= &d) + | ^^ + | + = note: `usize` takes `8` bytes of memory; copying the value instead of referencing it might avoid unnecessary pointer indirections +help: dereferencing the expressions will allow the compiler to more consistently optimize these binary operations + | +LL | .filter(|(a, b, c, d)| a <= c && d as &usize <= b || c <= a && *b <= *d) + | ~ ~ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs b/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs index b8b6f0846bb44..2ccc71f702cd7 100644 --- a/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs +++ b/tests/ui/macros/rfc-2011-nicer-assert-messages/all-expr-kinds.rs @@ -4,7 +4,7 @@ // run-pass // needs-unwind Asserting on contents of error message -#![allow(path_statements, unused_allocation)] +#![allow(path_statements, unused_allocation, ref_binop_on_copy_type)] #![feature(box_syntax, core_intrinsics, generic_assert, generic_assert_internals)] macro_rules! test { diff --git a/tests/ui/nll/issue-24535-allow-mutable-borrow-in-match-guard.rs b/tests/ui/nll/issue-24535-allow-mutable-borrow-in-match-guard.rs index ccfc8937fd72e..777aa18ff6fe6 100644 --- a/tests/ui/nll/issue-24535-allow-mutable-borrow-in-match-guard.rs +++ b/tests/ui/nll/issue-24535-allow-mutable-borrow-in-match-guard.rs @@ -6,6 +6,7 @@ // rust-lang/rfcs#1006, and rust-lang/rfcs#107 #![feature(if_let_guard)] +#![allow(ref_binop_on_copy_type)] fn main() { rust_issue_24535(); diff --git a/tests/ui/pattern/bindings-after-at/or-patterns-slice-patterns.rs b/tests/ui/pattern/bindings-after-at/or-patterns-slice-patterns.rs index d315f7ee3b68a..5d670d62b4d81 100644 --- a/tests/ui/pattern/bindings-after-at/or-patterns-slice-patterns.rs +++ b/tests/ui/pattern/bindings-after-at/or-patterns-slice-patterns.rs @@ -1,7 +1,7 @@ // Test bindings-after-at with or-patterns and slice-patterns // run-pass - +#![allow(ref_binop_on_copy_type)] #[derive(Debug, PartialEq)] enum MatchArm { diff --git a/tests/ui/pattern/bindings-after-at/slice-patterns.rs b/tests/ui/pattern/bindings-after-at/slice-patterns.rs index 4f4c96e450b64..31e6039f401e9 100644 --- a/tests/ui/pattern/bindings-after-at/slice-patterns.rs +++ b/tests/ui/pattern/bindings-after-at/slice-patterns.rs @@ -1,7 +1,7 @@ // Test bindings-after-at with slice-patterns // run-pass - +#![allow(ref_binop_on_copy_type)] #[derive(Debug, PartialEq)] enum MatchArm {