rustc_monomorphize/mono_checks/
move_check.rs1use rustc_abi::Size;
2use rustc_hir::def::DefKind;
3use rustc_hir::def_id::DefId;
4use rustc_lint_defs::builtin::LARGE_ASSIGNMENTS;
5use rustc_middle::mir::visit::Visitor as MirVisitor;
6use rustc_middle::mir::{self, Location, traversal};
7use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable};
8use rustc_span::{Span, Spanned, sym};
9use rustc_structures::Limit;
10use tracing::{debug, trace};
11
12use crate::diagnostics::LargeAssignmentsLint;
13
14struct MoveCheckVisitor<'tcx> {
15 tcx: TyCtxt<'tcx>,
16 instance: Instance<'tcx>,
17 body: &'tcx mir::Body<'tcx>,
18 move_size_spans: Vec<Span>,
20}
21
22pub(crate) fn check_moves<'tcx>(
23 tcx: TyCtxt<'tcx>,
24 instance: Instance<'tcx>,
25 body: &'tcx mir::Body<'tcx>,
26) {
27 if tcx.move_size_limit().0 == 0 {
28 return;
29 }
30
31 let mut visitor = MoveCheckVisitor { tcx, instance, body, move_size_spans: ::alloc::vec::Vec::new()vec![] };
32 for (bb, data) in traversal::mono_reachable(body, tcx, instance) {
33 visitor.visit_basic_block_data(bb, data)
34 }
35}
36
37impl<'tcx> MirVisitor<'tcx> for MoveCheckVisitor<'tcx> {
38 fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
39 match terminator.kind {
40 mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. }
41 | mir::TerminatorKind::TailCall { ref func, ref args, ref fn_span } => {
42 let callee_ty = func.ty(self.body, self.tcx);
43 let callee_ty = self.monomorphize(callee_ty);
44 self.check_fn_args_move_size(callee_ty, args, *fn_span, location);
45 }
46 _ => {}
47 }
48
49 }
52
53 fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) {
54 self.check_operand_move_size(operand, location);
55 }
56}
57
58impl<'tcx> MoveCheckVisitor<'tcx> {
59 fn monomorphize<T>(&self, value: T) -> T
60 where
61 T: TypeFoldable<TyCtxt<'tcx>>,
62 {
63 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/5d8fec0669a64607db9ccbf663e7c23125bf4748/compiler/rustc_monomorphize/src/mono_checks/move_check.rs:63",
"rustc_monomorphize::mono_checks::move_check",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("/rustc-dev/5d8fec0669a64607db9ccbf663e7c23125bf4748/compiler/rustc_monomorphize/src/mono_checks/move_check.rs"),
::tracing_core::__macro_support::Option::Some(63u32),
::tracing_core::__macro_support::Option::Some("rustc_monomorphize::mono_checks::move_check"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("monomorphize: self.instance={0:?}",
self.instance) as &dyn ::tracing::field::Value))])
});
} else { ; }
};trace!("monomorphize: self.instance={:?}", self.instance);
64 self.instance.instantiate_mir_and_normalize_erasing_regions(
65 self.tcx,
66 ty::TypingEnv::fully_monomorphized(),
67 ty::EarlyBinder::bind(self.tcx, value),
68 )
69 }
70
71 fn check_operand_move_size(&mut self, operand: &mir::Operand<'tcx>, location: Location) {
72 let limit = self.tcx.move_size_limit();
73 if limit.0 == 0 {
74 return;
75 }
76
77 let source_info = self.body.source_info(location);
78 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/5d8fec0669a64607db9ccbf663e7c23125bf4748/compiler/rustc_monomorphize/src/mono_checks/move_check.rs:78",
"rustc_monomorphize::mono_checks::move_check",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/5d8fec0669a64607db9ccbf663e7c23125bf4748/compiler/rustc_monomorphize/src/mono_checks/move_check.rs"),
::tracing_core::__macro_support::Option::Some(78u32),
::tracing_core::__macro_support::Option::Some("rustc_monomorphize::mono_checks::move_check"),
::tracing_core::field::FieldSet::new(&[{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("source_info")
}> =
::tracing::__macro_support::FieldName::new("source_info");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&source_info)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!(?source_info);
79
80 if let Some(too_large_size) = self.operand_size_if_too_large(limit, operand) {
81 self.lint_large_assignment(limit.0, too_large_size, location, source_info.span);
82 };
83 }
84
85 pub(super) fn check_fn_args_move_size(
86 &mut self,
87 callee_ty: Ty<'tcx>,
88 args: &[Spanned<mir::Operand<'tcx>>],
89 fn_span: Span,
90 location: Location,
91 ) {
92 let limit = self.tcx.move_size_limit();
93 if limit.0 == 0 {
94 return;
95 }
96
97 if args.is_empty() {
98 return;
99 }
100
101 let ty::FnDef(def_id, _) = *callee_ty.kind() else {
103 return;
104 };
105 if should_skip_fn(self.tcx, def_id) {
106 return;
107 }
108
109 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/5d8fec0669a64607db9ccbf663e7c23125bf4748/compiler/rustc_monomorphize/src/mono_checks/move_check.rs:109",
"rustc_monomorphize::mono_checks::move_check",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/5d8fec0669a64607db9ccbf663e7c23125bf4748/compiler/rustc_monomorphize/src/mono_checks/move_check.rs"),
::tracing_core::__macro_support::Option::Some(109u32),
::tracing_core::__macro_support::Option::Some("rustc_monomorphize::mono_checks::move_check"),
::tracing_core::field::FieldSet::new(&[{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("def_id")
}> =
::tracing::__macro_support::FieldName::new("def_id");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("fn_span")
}> =
::tracing::__macro_support::FieldName::new("fn_span");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&def_id)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&fn_span)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!(?def_id, ?fn_span);
110
111 for arg in args {
112 let operand: &mir::Operand<'tcx> = &arg.node;
116 if let mir::Operand::Move(_) = operand {
117 continue;
118 }
119
120 if let Some(too_large_size) = self.operand_size_if_too_large(limit, operand) {
121 self.lint_large_assignment(limit.0, too_large_size, location, arg.span);
122 };
123 }
124 }
125
126 fn operand_size_if_too_large(
127 &mut self,
128 limit: Limit,
129 operand: &mir::Operand<'tcx>,
130 ) -> Option<Size> {
131 let ty = operand.ty(self.body, self.tcx);
132 let ty = self.monomorphize(ty);
133 let Ok(layout) =
134 self.tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty))
135 else {
136 return None;
137 };
138 if layout.size.bytes_usize() > limit.0 {
139 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/5d8fec0669a64607db9ccbf663e7c23125bf4748/compiler/rustc_monomorphize/src/mono_checks/move_check.rs:139",
"rustc_monomorphize::mono_checks::move_check",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/5d8fec0669a64607db9ccbf663e7c23125bf4748/compiler/rustc_monomorphize/src/mono_checks/move_check.rs"),
::tracing_core::__macro_support::Option::Some(139u32),
::tracing_core::__macro_support::Option::Some("rustc_monomorphize::mono_checks::move_check"),
::tracing_core::field::FieldSet::new(&[{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("layout")
}> =
::tracing::__macro_support::FieldName::new("layout");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&layout)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!(?layout);
140 Some(layout.size)
141 } else {
142 None
143 }
144 }
145
146 fn lint_large_assignment(
147 &mut self,
148 limit: usize,
149 too_large_size: Size,
150 location: Location,
151 span: Span,
152 ) {
153 let source_info = self.body.source_info(location);
154
155 let lint_root = source_info.scope.lint_root(&self.body.source_scopes);
156 let Some(lint_root) = lint_root else {
157 return;
163 };
164
165 let reported_span = self
167 .body
168 .source_scopes
169 .get(source_info.scope)
170 .and_then(|source_scope_data| source_scope_data.inlined)
171 .map(|(_, call_site)| call_site)
172 .unwrap_or(span);
173
174 for previously_reported_span in &self.move_size_spans {
175 if previously_reported_span.overlaps(reported_span) {
176 return;
177 }
178 }
179
180 self.tcx.emit_node_span_lint(
181 LARGE_ASSIGNMENTS,
182 lint_root,
183 reported_span,
184 LargeAssignmentsLint {
185 span: reported_span,
186 size: too_large_size.bytes(),
187 limit: limit as u64,
188 },
189 );
190
191 self.move_size_spans.push(reported_span);
192 }
193}
194
195fn should_skip_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
197 if let DefKind::AssocFn = tcx.def_kind(def_id)
198 && tcx.item_name(def_id) == sym::new
199 && let parent = tcx.parent(def_id)
200 && let DefKind::Impl { of_trait: false } = tcx.def_kind(parent)
201 && let ty::Adt(adt_def, ..) =
202 tcx.type_of(parent).instantiate_identity().skip_normalization().kind()
203 {
204 return Some(adt_def.did()) == tcx.lang_items().owned_box()
205 || Some(adt_def.did()) == tcx.get_diagnostic_item(sym::Rc)
206 || Some(adt_def.did()) == tcx.get_diagnostic_item(sym::Arc);
207 }
208 false
209}