Skip to content

Commit 8f440f0

Browse files
committed
large_assignments: Lint on specific large args passed to functions
1 parent 16ba56c commit 8f440f0

File tree

5 files changed

+134
-41
lines changed

5 files changed

+134
-41
lines changed

compiler/rustc_middle/src/mir/tcx.rs

+12
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,18 @@ impl<'tcx> Operand<'tcx> {
229229
Operand::Constant(c) => c.const_.ty(),
230230
}
231231
}
232+
233+
pub fn span<D: ?Sized>(&self, local_decls: &D) -> Span
234+
where
235+
D: HasLocalDecls<'tcx>,
236+
{
237+
match self {
238+
&Operand::Copy(ref l) | &Operand::Move(ref l) => {
239+
local_decls.local_decls()[l.local].source_info.span
240+
}
241+
Operand::Constant(c) => c.span,
242+
}
243+
}
232244
}
233245

234246
impl<'tcx> BinOp {

compiler/rustc_monomorphize/src/collector.rs

+65-39
Original file line numberDiff line numberDiff line change
@@ -614,8 +614,8 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
614614
}
615615

616616
fn check_operand_move_size(&mut self, operand: &mir::Operand<'tcx>, location: Location) {
617-
let limit = self.tcx.move_size_limit().0;
618-
if limit == 0 {
617+
let limit = self.tcx.move_size_limit();
618+
if limit.0 == 0 {
619619
return;
620620
}
621621

@@ -627,48 +627,19 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
627627
return;
628628
}
629629

630-
let limit = Size::from_bytes(limit);
631-
let ty = operand.ty(self.body, self.tcx);
632-
let ty = self.monomorphize(ty);
633-
let Ok(layout) = self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)) else { return };
634-
if layout.size <= limit {
635-
return;
636-
}
637-
debug!(?layout);
638630
let source_info = self.body.source_info(location);
639631
debug!(?source_info);
640-
for span in &self.move_size_spans {
641-
if span.overlaps(source_info.span) {
642-
return;
643-
}
644-
}
645-
let lint_root = source_info.scope.lint_root(&self.body.source_scopes);
646-
debug!(?lint_root);
647-
let Some(lint_root) = lint_root else {
648-
// This happens when the issue is in a function from a foreign crate that
649-
// we monomorphized in the current crate. We can't get a `HirId` for things
650-
// in other crates.
651-
// FIXME: Find out where to report the lint on. Maybe simply crate-level lint root
652-
// but correct span? This would make the lint at least accept crate-level lint attributes.
653-
return;
632+
633+
if let Some(too_large_size) = self.operand_size_if_too_large(limit, operand) {
634+
self.lint_large_assignment(limit.0, too_large_size, location, source_info.span);
654635
};
655-
self.tcx.emit_spanned_lint(
656-
LARGE_ASSIGNMENTS,
657-
lint_root,
658-
source_info.span,
659-
LargeAssignmentsLint {
660-
span: source_info.span,
661-
size: layout.size.bytes(),
662-
limit: limit.bytes(),
663-
},
664-
);
665-
self.move_size_spans.push(source_info.span);
666636
}
667637

668638
fn check_fn_args_move_size(
669639
&mut self,
670640
callee_ty: Ty<'tcx>,
671641
args: &[Spanned<mir::Operand<'tcx>>],
642+
fn_span: Span,
672643
location: Location,
673644
) {
674645
let limit = self.tcx.move_size_limit();
@@ -692,10 +663,65 @@ impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
692663
return;
693664
}
694665

666+
debug!(?def_id, ?fn_span);
667+
695668
for arg in args {
696-
self.check_operand_move_size(&arg.node, location);
669+
if let Some(too_large_size) = self.operand_size_if_too_large(limit, &arg.node) {
670+
self.lint_large_assignment(limit.0, too_large_size, location, arg.span);
671+
};
672+
}
673+
}
674+
675+
fn operand_size_if_too_large(
676+
&mut self,
677+
limit: Limit,
678+
operand: &mir::Operand<'tcx>,
679+
) -> Option<Size> {
680+
let ty = operand.ty(self.body, self.tcx);
681+
let ty = self.monomorphize(ty);
682+
let Ok(layout) = self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)) else {
683+
return None;
684+
};
685+
if layout.size.bytes_usize() > limit.0 {
686+
debug!(?layout);
687+
Some(layout.size)
688+
} else {
689+
None
697690
}
698691
}
692+
693+
fn lint_large_assignment(
694+
&mut self,
695+
limit: usize,
696+
too_large_size: Size,
697+
location: Location,
698+
span: Span,
699+
) {
700+
let source_info = self.body.source_info(location);
701+
debug!(?source_info);
702+
for reported_span in &self.move_size_spans {
703+
if reported_span.overlaps(span) {
704+
return;
705+
}
706+
}
707+
let lint_root = source_info.scope.lint_root(&self.body.source_scopes);
708+
debug!(?lint_root);
709+
let Some(lint_root) = lint_root else {
710+
// This happens when the issue is in a function from a foreign crate that
711+
// we monomorphized in the current crate. We can't get a `HirId` for things
712+
// in other crates.
713+
// FIXME: Find out where to report the lint on. Maybe simply crate-level lint root
714+
// but correct span? This would make the lint at least accept crate-level lint attributes.
715+
return;
716+
};
717+
self.tcx.emit_spanned_lint(
718+
LARGE_ASSIGNMENTS,
719+
lint_root,
720+
span,
721+
LargeAssignmentsLint { span, size: too_large_size.bytes(), limit: limit as u64 },
722+
);
723+
self.move_size_spans.push(span);
724+
}
699725
}
700726

701727
impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
@@ -813,11 +839,11 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
813839
};
814840

815841
match terminator.kind {
816-
mir::TerminatorKind::Call { ref func, ref args, .. } => {
842+
mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } => {
817843
let callee_ty = func.ty(self.body, tcx);
818844
let callee_ty = self.monomorphize(callee_ty);
819-
self.check_fn_args_move_size(callee_ty, args, location);
820-
visit_fn_use(self.tcx, callee_ty, true, source, self.output)
845+
self.check_fn_args_move_size(callee_ty, args, *fn_span, location);
846+
visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output)
821847
}
822848
mir::TerminatorKind::Drop { ref place, .. } => {
823849
let ty = place.ty(self.body, self.tcx).ty;

tests/ui/lint/large_assignments/box_rc_arc_allowed.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: moving 9999 bytes
2-
--> $DIR/box_rc_arc_allowed.rs:16:13
2+
--> $DIR/box_rc_arc_allowed.rs:16:25
33
|
44
LL | let _ = NotBox::new([0; 9999]);
5-
| ^^^^^^^^^^^^^^^^^^^^^^ value moved from here
5+
| ^^^^^^^^^ value moved from here
66
|
77
= note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
88
note: the lint level is defined here
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// build-fail
2+
3+
#![feature(large_assignments)]
4+
#![move_size_limit = "1000"]
5+
#![deny(large_assignments)]
6+
#![allow(unused)]
7+
8+
// We want copy semantics, because moving data into functions generally do not
9+
// translate to actual `memcpy`s.
10+
#[derive(Copy, Clone)]
11+
struct Data([u8; 9999]);
12+
13+
fn main() {
14+
one_arg(Data([0; 9999])); //~ ERROR large_assignments
15+
16+
// each individual large arg shall have its own span
17+
many_args(Data([0; 9999]), true, Data([0; 9999]));
18+
//~^ ERROR large_assignments
19+
//~| ERROR large_assignments
20+
}
21+
22+
fn one_arg(a: Data) {}
23+
24+
fn many_args(a: Data, b: bool, c: Data) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
error: moving 9999 bytes
2+
--> $DIR/copy_into_fn.rs:14:13
3+
|
4+
LL | one_arg(Data([0; 9999]));
5+
| ^^^^^^^^^^^^^^^ value moved from here
6+
|
7+
= note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
8+
note: the lint level is defined here
9+
--> $DIR/copy_into_fn.rs:5:9
10+
|
11+
LL | #![deny(large_assignments)]
12+
| ^^^^^^^^^^^^^^^^^
13+
14+
error: moving 9999 bytes
15+
--> $DIR/copy_into_fn.rs:17:15
16+
|
17+
LL | many_args(Data([0; 9999]), true, Data([0; 9999]));
18+
| ^^^^^^^^^^^^^^^ value moved from here
19+
|
20+
= note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
21+
22+
error: moving 9999 bytes
23+
--> $DIR/copy_into_fn.rs:17:38
24+
|
25+
LL | many_args(Data([0; 9999]), true, Data([0; 9999]));
26+
| ^^^^^^^^^^^^^^^ value moved from here
27+
|
28+
= note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
29+
30+
error: aborting due to 3 previous errors
31+

0 commit comments

Comments
 (0)