Skip to content

Commit 8f681d8

Browse files
committed
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 rust-lang#105259, particularly when match ergonomics is involved.
1 parent fdbc432 commit 8f681d8

File tree

51 files changed

+372
-69
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+372
-69
lines changed

compiler/rustc_ast/src/token.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -894,7 +894,7 @@ impl PartialEq for Nonterminal {
894894
fn eq(&self, rhs: &Self) -> bool {
895895
match (self, rhs) {
896896
(NtIdent(ident_lhs, is_raw_lhs), NtIdent(ident_rhs, is_raw_rhs)) => {
897-
ident_lhs == ident_rhs && is_raw_lhs == is_raw_rhs
897+
ident_lhs == ident_rhs && *is_raw_lhs == *is_raw_rhs
898898
}
899899
(NtLifetime(ident_lhs), NtLifetime(ident_rhs)) => ident_lhs == ident_rhs,
900900
// FIXME: Assume that all "complex" nonterminal are not equal, we can't compare them

compiler/rustc_ast/src/tokenstream.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ impl TokenTree {
6565
match (self, other) {
6666
(TokenTree::Token(token, _), TokenTree::Token(token2, _)) => token.kind == token2.kind,
6767
(TokenTree::Delimited(_, delim, tts), TokenTree::Delimited(_, delim2, tts2)) => {
68-
delim == delim2 && tts.eq_unspanned(tts2)
68+
*delim == *delim2 && tts.eq_unspanned(tts2)
6969
}
7070
_ => false,
7171
}

compiler/rustc_ast_lowering/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
617617
self.impl_trait_defs = current_impl_trait_defs;
618618
self.impl_trait_bounds = current_impl_trait_bounds;
619619

620-
debug_assert!(!self.children.iter().any(|(id, _)| id == &def_id));
620+
debug_assert!(!self.children.iter().any(|(id, _)| *id == def_id));
621621
self.children.push((def_id, hir::MaybeOwner::Owner(info)));
622622
}
623623

compiler/rustc_ast_passes/src/feature_gate.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -688,8 +688,9 @@ fn check_incompatible_features(sess: &Session) {
688688
.iter()
689689
.filter(|&&(f1, f2)| features.enabled(f1) && features.enabled(f2))
690690
{
691-
if let Some((f1_name, f1_span)) = declared_features.clone().find(|(name, _)| name == f1) {
692-
if let Some((f2_name, f2_span)) = declared_features.clone().find(|(name, _)| name == f2)
691+
if let Some((f1_name, f1_span)) = declared_features.clone().find(|(name, _)| *name == *f1) {
692+
if let Some((f2_name, f2_span)) =
693+
declared_features.clone().find(|(name, _)| *name == *f2)
693694
{
694695
let spans = vec![f1_span, f2_span];
695696
sess.struct_span_err(

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2825,7 +2825,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
28252825
let mut arguments = Vec::new();
28262826
for (index, argument) in sig.inputs().skip_binder().iter().enumerate() {
28272827
if let ty::Ref(argument_region, _, _) = argument.kind() {
2828-
if argument_region == return_region {
2828+
if *argument_region == *return_region {
28292829
// Need to use the `rustc_middle::ty` types to compare against the
28302830
// `return_region`. Then use the `rustc_hir` type to get only
28312831
// the lifetime span.

compiler/rustc_codegen_ssa/src/base.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
694694
let cgus: Vec<_> = cgu_reuse
695695
.iter()
696696
.enumerate()
697-
.filter(|&(_, reuse)| reuse == &CguReuse::No)
697+
.filter(|&(_, reuse)| *reuse == CguReuse::No)
698698
.take(tcx.sess.threads())
699699
.collect();
700700

compiler/rustc_const_eval/src/interpret/terminator.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -258,15 +258,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
258258
(PassMode::Pair(a1, b1), PassMode::Pair(a2, b2)) => {
259259
arg_attr_compat(a1, a2) && arg_attr_compat(b1, b2)
260260
}
261-
(PassMode::Cast(c1, pad1), PassMode::Cast(c2, pad2)) => c1 == c2 && pad1 == pad2,
261+
(PassMode::Cast(c1, pad1), PassMode::Cast(c2, pad2)) => c1 == c2 && *pad1 == *pad2,
262262
(
263263
PassMode::Indirect { attrs: a1, extra_attrs: None, on_stack: s1 },
264264
PassMode::Indirect { attrs: a2, extra_attrs: None, on_stack: s2 },
265-
) => arg_attr_compat(a1, a2) && s1 == s2,
265+
) => arg_attr_compat(a1, a2) && *s1 == *s2,
266266
(
267267
PassMode::Indirect { attrs: a1, extra_attrs: Some(e1), on_stack: s1 },
268268
PassMode::Indirect { attrs: a2, extra_attrs: Some(e2), on_stack: s2 },
269-
) => arg_attr_compat(a1, a2) && arg_attr_compat(e1, e2) && s1 == s2,
269+
) => arg_attr_compat(a1, a2) && arg_attr_compat(e1, e2) && *s1 == *s2,
270270
_ => false,
271271
};
272272

compiler/rustc_errors/src/emitter.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ pub trait Emitter: Translate {
351351
if let Some((macro_kind, name)) = has_macro_spans.first() {
352352
// Mark the actual macro this originates from
353353
let and_then = if let Some((macro_kind, last_name)) = has_macro_spans.last()
354-
&& last_name != name
354+
&& *last_name != *name
355355
{
356356
let descr = macro_kind.descr();
357357
format!(
@@ -2753,7 +2753,7 @@ pub fn is_case_difference(sm: &SourceMap, suggested: &str, sp: Span) -> bool {
27532753
let ascii_confusables = &['c', 'f', 'i', 'k', 'o', 's', 'u', 'v', 'w', 'x', 'y', 'z'];
27542754
// All the chars that differ in capitalization are confusable (above):
27552755
let confusable = iter::zip(found.chars(), suggested.chars())
2756-
.filter(|(f, s)| f != s)
2756+
.filter(|(f, s)| *f != *s)
27572757
.all(|(f, s)| (ascii_confusables.contains(&f) || ascii_confusables.contains(&s)));
27582758
confusable && found.to_lowercase() == suggested.to_lowercase()
27592759
// FIXME: We sometimes suggest the same thing we already have, which is a

compiler/rustc_expand/src/mbe/transcribe.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ pub(super) fn transcribe<'a>(
123123
if let Frame::Sequence { idx, sep, .. } = stack.last_mut().unwrap() {
124124
let (repeat_idx, repeat_len) = repeats.last_mut().unwrap();
125125
*repeat_idx += 1;
126-
if repeat_idx < repeat_len {
126+
if *repeat_idx < *repeat_len {
127127
*idx = 0;
128128
if let Some(sep) = sep {
129129
result.push(TokenTree::Token(sep.clone(), Spacing::Alone));

compiler/rustc_hir_analysis/src/check/wfcheck.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ fn gather_gat_bounds<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
579579
for (region_b, region_b_idx) in &regions {
580580
// Again, skip `'static` because it outlives everything. Also, we trivially
581581
// know that a region outlives itself.
582-
if ty::ReStatic == **region_b || region_a == region_b {
582+
if ty::ReStatic == **region_b || *region_a == *region_b {
583583
continue;
584584
}
585585
if region_known_to_outlive(

compiler/rustc_hir_pretty/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2092,7 +2092,7 @@ impl<'a> State<'a> {
20922092

20932093
match bound {
20942094
GenericBound::Trait(tref, modifier) => {
2095-
if modifier == &TraitBoundModifier::Maybe {
2095+
if *modifier == TraitBoundModifier::Maybe {
20962096
self.word("?");
20972097
}
20982098
self.print_poly_trait_ref(tref);

compiler/rustc_hir_typeck/src/demand.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
321321
// Remove one layer of references to account for `&mut self` and
322322
// `&self`, so that we can compare it against the binding.
323323
let (ty, def_self_ty) = match (ty.kind(), def_self_ty.kind()) {
324-
(ty::Ref(_, ty, a), ty::Ref(_, self_ty, b)) if a == b => (*ty, *self_ty),
324+
(ty::Ref(_, ty, a), ty::Ref(_, self_ty, b)) if *a == *b => (*ty, *self_ty),
325325
_ => (ty, def_self_ty),
326326
};
327327
let mut param_args = FxHashMap::default();

compiler/rustc_hir_typeck/src/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1739,7 +1739,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17391739
self.check_expr_has_type_or_error(base_expr, adt_ty, |_| {
17401740
let base_ty = self.typeck_results.borrow().expr_ty(*base_expr);
17411741
let same_adt = match (adt_ty.kind(), base_ty.kind()) {
1742-
(ty::Adt(adt, _), ty::Adt(base_adt, _)) if adt == base_adt => true,
1742+
(ty::Adt(adt, _), ty::Adt(base_adt, _)) if *adt == *base_adt => true,
17431743
_ => false,
17441744
};
17451745
if self.tcx.sess.is_nightly_build() && same_adt {

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -815,7 +815,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
815815
_ => None,
816816
})
817817
.map(|(ty, bounds)| match ty.kind() {
818-
ty::Param(param_ty) if param_ty == expected_ty_as_param => Ok(Some(bounds)),
818+
ty::Param(param_ty) if *param_ty == *expected_ty_as_param => Ok(Some(bounds)),
819819
// check whether there is any predicate that contains our `T`, like `Option<T>: Send`
820820
_ => match ty.contains(expected) {
821821
true => Err(()),
@@ -848,7 +848,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
848848

849849
let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| {
850850
let ty = self.astconv().ast_ty_to_ty( param);
851-
matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param)
851+
matches!(ty.kind(), ty::Param(fn_param_ty_param) if *expected_ty_as_param == *fn_param_ty_param)
852852
});
853853

854854
if ty_param_used_in_fn_params {
@@ -1008,7 +1008,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10081008
) -> bool {
10091009
let ty::Adt(adt_def, substs) = expr_ty.kind() else { return false; };
10101010
let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return false; };
1011-
if adt_def != expected_adt_def {
1011+
if *adt_def != *expected_adt_def {
10121012
return false;
10131013
}
10141014

compiler/rustc_index/src/interval.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ impl<I: Idx> IntervalSet<I> {
223223
fn check_invariants(&self) -> bool {
224224
let mut current: Option<u32> = None;
225225
for (start, end) in &self.map {
226-
if start > end || current.map_or(false, |x| x + 1 >= *start) {
226+
if *start > *end || current.map_or(false, |x| x + 1 >= *start) {
227227
return false;
228228
}
229229
current = Some(*end);

compiler/rustc_infer/src/infer/error_reporting/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1144,7 +1144,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
11441144
let remainder2: Vec<_> = sub2.types().skip(common_len).collect();
11451145
let common_default_params =
11461146
iter::zip(remainder1.iter().rev(), remainder2.iter().rev())
1147-
.filter(|(a, b)| a == b)
1147+
.filter(|(a, b)| **a == **b)
11481148
.count();
11491149
let len = sub1.len() - common_default_params;
11501150
let consts_offset = len - sub1.consts().count();
@@ -2028,7 +2028,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
20282028
}),
20292029
..
20302030
} = s
2031-
&& init_span == &self.span {
2031+
&& *init_span == self.span {
20322032
self.result = Some(*array_ty);
20332033
}
20342034
}

compiler/rustc_infer/src/infer/error_reporting/suggest.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
449449
(expected.kind(), found.kind())
450450
{
451451
if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
452-
if exp_def == &found_def {
452+
if *exp_def == found_def {
453453
let have_as_ref = &[
454454
(
455455
sym::Option,
@@ -581,7 +581,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
581581
(
582582
ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, .. }),
583583
ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, .. }),
584-
) if last_def_id == exp_def_id => StatementAsExpression::CorrectType,
584+
) if *last_def_id == *exp_def_id => StatementAsExpression::CorrectType,
585585
(
586586
ty::Alias(ty::Opaque, ty::AliasTy { def_id: last_def_id, substs: last_bounds, .. }),
587587
ty::Alias(ty::Opaque, ty::AliasTy { def_id: exp_def_id, substs: exp_bounds, .. }),
@@ -607,14 +607,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
607607
hir::GenericBound::Trait(tl, ml),
608608
hir::GenericBound::Trait(tr, mr),
609609
) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
610-
&& ml == mr =>
610+
&& *ml == *mr =>
611611
{
612612
true
613613
}
614614
(
615615
hir::GenericBound::LangItemTrait(langl, _, _, argsl),
616616
hir::GenericBound::LangItemTrait(langr, _, _, argsr),
617-
) if langl == langr => {
617+
) if *langl == *langr => {
618618
// FIXME: consider the bounds!
619619
debug!("{:?} {:?}", argsl, argsr);
620620
true

compiler/rustc_lint/locales/en-US.ftl

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ lint_array_into_iter =
55
.use_explicit_into_iter_suggestion =
66
or use `IntoIterator::into_iter(..)` instead of `.into_iter()` to explicitly iterate by value
77
8+
lint_ref_binop_on_copy_type =
9+
binary operation on reference to `Copy` type `{$ty}`
10+
.note = `{$ty}` takes `{$bytes}` bytes of memory; copying the value instead of referencing it might avoid unnecessary pointer indirections
11+
.suggestion = dereferencing the expressions will allow the compiler to more consistently optimize these binary operations
12+
813
lint_enum_intrinsics_mem_discriminant =
914
the return value of `mem::discriminant` is unspecified when called with a non-enum type
1015
.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.

compiler/rustc_lint/src/builtin.rs

+115-2
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ use crate::{
3838
BuiltinUngatedAsyncFnTrackCaller, BuiltinUnnameableTestItems, BuiltinUnpermittedTypeInit,
3939
BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe,
4040
BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
41-
BuiltinWhileTrue, SuggestChangingAssocTypes,
41+
BuiltinWhileTrue, RefBinopOnCopyTypeDiag, RefBinopOnCopyTypeSuggestion,
42+
SuggestChangingAssocTypes,
4243
},
4344
types::{transparent_newtype_field, CItemKind},
4445
EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext,
@@ -2888,7 +2889,7 @@ impl ClashingExternDeclarations {
28882889
}
28892890
(Ref(_a_region, a_ty, a_mut), Ref(_b_region, b_ty, b_mut)) => {
28902891
// For structural sameness, we don't need the region to be same.
2891-
a_mut == b_mut
2892+
*a_mut == *b_mut
28922893
&& structurally_same_type_impl(seen_types, cx, *a_ty, *b_ty, ckind)
28932894
}
28942895
(FnDef(..), FnDef(..)) => {
@@ -3307,6 +3308,118 @@ impl EarlyLintPass for SpecialModuleName {
33073308
}
33083309
}
33093310

3311+
declare_lint! {
3312+
/// The `ref_binop_on_copy_type` lint detects borrowed `Copy` types being passed to binary
3313+
/// operations that have unnecessary borrows.
3314+
///
3315+
/// ### Example
3316+
///
3317+
/// ```rust
3318+
/// # #![allow(unused)]
3319+
/// pub fn slice_of_ints(input: &[(usize, usize, usize, usize)]) -> usize {
3320+
/// input
3321+
/// .iter()
3322+
/// .filter(|(a, b, c, d)| a <= c && d <= b || c <= a && b <= d)
3323+
/// .count()
3324+
/// }
3325+
/// ```
3326+
///
3327+
/// {{produces}}
3328+
///
3329+
/// ### Explanation
3330+
///
3331+
/// When making comparisons (or other binary operations) between two reference values that can
3332+
/// be copied instead, the compiler will not always remove the references, making the execution
3333+
/// of the binary operation slower than it otherwise could be by making the machine code "chase
3334+
/// pointers" before actually finding the underlying value. If the `Copy` value is as small or
3335+
/// smaller than a 64 bit pointer, we suggest dereferencing the value so the compiler will have
3336+
/// a better chance of producing optimal instructions.
3337+
REF_BINOP_ON_COPY_TYPE,
3338+
Warn,
3339+
"detects binary operations on references to `Copy` types like `&42 < &50`",
3340+
}
3341+
3342+
declare_lint_pass!(RefBinopOnCopyType => [REF_BINOP_ON_COPY_TYPE]);
3343+
3344+
impl<'tcx> LateLintPass<'tcx> for RefBinopOnCopyType {
3345+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
3346+
let hir::ExprKind::Binary(op, left, right) = expr.kind else { return; };
3347+
let left_ty = cx.typeck_results().expr_ty_adjusted(left);
3348+
let right_ty = cx.typeck_results().expr_ty_adjusted(right);
3349+
if let ty::Ref(_, left_ty, _) = left_ty.kind()
3350+
&& let ty::Ref(_, left_ty, _) = left_ty.kind()
3351+
&& let left_ty = left_ty.peel_refs()
3352+
&& left_ty.is_copy_modulo_regions(cx.tcx, cx.param_env)
3353+
&& let Some(size) = cx.tcx.layout_of(cx.param_env.and(left_ty)).ok().map(|layout| {
3354+
layout.size.bytes()
3355+
})
3356+
&& let ty::Ref(_, right_ty, _) = right_ty.kind()
3357+
&& let ty::Ref(_, right_ty, _) = right_ty.kind()
3358+
&& let right_ty = right_ty.peel_refs()
3359+
&& right_ty.is_copy_modulo_regions(cx.tcx, cx.param_env)
3360+
&& let Some(size_r) = cx.tcx.layout_of(cx.param_env.and(right_ty)).ok().map(|layout| {
3361+
layout.size.bytes()
3362+
})
3363+
&& size <= 8
3364+
&& size_r <= 8
3365+
{
3366+
let left_start_base = left.span.shrink_to_lo();
3367+
let left_peeled = left.peel_borrows();
3368+
let left_start = left_start_base.to(left_peeled.span.shrink_to_lo());
3369+
let left_sugg = if left_start != left_start_base
3370+
&& !matches!(cx.typeck_results().expr_ty_adjusted(left_peeled).kind(), ty::Ref(..))
3371+
{
3372+
""
3373+
} else {
3374+
"*"
3375+
};
3376+
let (left_brace_start, left_brace_end, left_end) =
3377+
if left.precedence().order() < ast::ExprPrecedence::Unary.order() {
3378+
("(", ")", Some(left.span.shrink_to_hi()))
3379+
} else {
3380+
("", "", None)
3381+
};
3382+
let right_start_base = right.span.shrink_to_lo();
3383+
let right_peeled = right.peel_borrows();
3384+
let right_start = right_start_base.to(right_peeled.span.shrink_to_lo());
3385+
let right_sugg = if right_start != right_start_base
3386+
&& !matches!(cx.typeck_results().expr_ty_adjusted(right_peeled).kind(), ty::Ref(..))
3387+
{
3388+
""
3389+
} else {
3390+
"*"
3391+
};
3392+
let (right_brace_start, right_brace_end, right_end) =
3393+
if right.precedence().order() < ast::ExprPrecedence::Unary.order() {
3394+
("(", ")", Some(right.span.shrink_to_hi()))
3395+
} else {
3396+
("", "", None)
3397+
};
3398+
cx.emit_spanned_lint(
3399+
REF_BINOP_ON_COPY_TYPE,
3400+
op.span,
3401+
RefBinopOnCopyTypeDiag {
3402+
ty: with_no_trimmed_paths!(left_ty.to_string()),
3403+
bytes: size,
3404+
note: Some(()),
3405+
suggestion: RefBinopOnCopyTypeSuggestion {
3406+
left_brace_start,
3407+
left_brace_end,
3408+
left_sugg,
3409+
left_start,
3410+
left_end,
3411+
right_brace_start,
3412+
right_brace_end,
3413+
right_sugg,
3414+
right_start,
3415+
right_end,
3416+
},
3417+
},
3418+
);
3419+
}
3420+
}
3421+
}
3422+
33103423
pub use rustc_session::lint::builtin::UNEXPECTED_CFGS;
33113424

33123425
declare_lint_pass!(UnexpectedCfgs => [UNEXPECTED_CFGS]);

compiler/rustc_lint/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ late_lint_methods!(
204204
ImproperCTypesDefinitions: ImproperCTypesDefinitions,
205205
VariantSizeDifferences: VariantSizeDifferences,
206206
BoxPointers: BoxPointers,
207+
RefBinopOnCopyType: RefBinopOnCopyType,
207208
PathStatements: PathStatements,
208209
LetUnderscore: LetUnderscore,
209210
// Depends on referenced function signatures in expressions

0 commit comments

Comments
 (0)