rustc_monomorphize/mono_checks/
move_check.rs1use rustc_abi::Size;
2use rustc_data_structures::fx::FxIndexSet;
3use rustc_hir::def_id::DefId;
4use rustc_hir::limit::Limit;
5use rustc_middle::mir::visit::Visitor as MirVisitor;
6use rustc_middle::mir::{self, Location, traversal};
7use rustc_middle::ty::{self, AssocTag, Instance, Ty, TyCtxt, TypeFoldable};
8use rustc_session::lint::builtin::LARGE_ASSIGNMENTS;
9use rustc_span::{Ident, Span, Spanned, sym};
10use tracing::{debug, trace};
11
12use crate::errors::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 let mut visitor = MoveCheckVisitor { tcx, instance, body, move_size_spans: ::alloc::vec::Vec::new()vec![] };
28 for (bb, data) in traversal::mono_reachable(body, tcx, instance) {
29 visitor.visit_basic_block_data(bb, data)
30 }
31}
32
33impl<'tcx> MirVisitor<'tcx> for MoveCheckVisitor<'tcx> {
34 fn visit_terminator(&mut self, terminator: &mir::Terminator<'tcx>, location: Location) {
35 match terminator.kind {
36 mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. }
37 | mir::TerminatorKind::TailCall { ref func, ref args, ref fn_span } => {
38 let callee_ty = func.ty(self.body, self.tcx);
39 let callee_ty = self.monomorphize(callee_ty);
40 self.check_fn_args_move_size(callee_ty, args, *fn_span, location);
41 }
42 _ => {}
43 }
44
45 }
48
49 fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) {
50 self.check_operand_move_size(operand, location);
51 }
52}
53
54impl<'tcx> MoveCheckVisitor<'tcx> {
55 fn monomorphize<T>(&self, value: T) -> T
56 where
57 T: TypeFoldable<TyCtxt<'tcx>>,
58 {
59 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_monomorphize/src/mono_checks/move_check.rs:59",
"rustc_monomorphize::mono_checks::move_check",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("compiler/rustc_monomorphize/src/mono_checks/move_check.rs"),
::tracing_core::__macro_support::Option::Some(59u32),
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("monomorphize: self.instance={0:?}",
self.instance) as &dyn Value))])
});
} else { ; }
};trace!("monomorphize: self.instance={:?}", self.instance);
60 self.instance.instantiate_mir_and_normalize_erasing_regions(
61 self.tcx,
62 ty::TypingEnv::fully_monomorphized(),
63 ty::EarlyBinder::bind(value),
64 )
65 }
66
67 fn check_operand_move_size(&mut self, operand: &mir::Operand<'tcx>, location: Location) {
68 let limit = self.tcx.move_size_limit();
69 if limit.0 == 0 {
70 return;
71 }
72
73 let source_info = self.body.source_info(location);
74 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_monomorphize/src/mono_checks/move_check.rs:74",
"rustc_monomorphize::mono_checks::move_check",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_monomorphize/src/mono_checks/move_check.rs"),
::tracing_core::__macro_support::Option::Some(74u32),
::tracing_core::__macro_support::Option::Some("rustc_monomorphize::mono_checks::move_check"),
::tracing_core::field::FieldSet::new(&["source_info"],
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&source_info)
as &dyn Value))])
});
} else { ; }
};debug!(?source_info);
75
76 if let Some(too_large_size) = self.operand_size_if_too_large(limit, operand) {
77 self.lint_large_assignment(limit.0, too_large_size, location, source_info.span);
78 };
79 }
80
81 pub(super) fn check_fn_args_move_size(
82 &mut self,
83 callee_ty: Ty<'tcx>,
84 args: &[Spanned<mir::Operand<'tcx>>],
85 fn_span: Span,
86 location: Location,
87 ) {
88 let limit = self.tcx.move_size_limit();
89 if limit.0 == 0 {
90 return;
91 }
92
93 if args.is_empty() {
94 return;
95 }
96
97 let ty::FnDef(def_id, _) = *callee_ty.kind() else {
99 return;
100 };
101 if self.tcx.skip_move_check_fns(()).contains(&def_id) {
102 return;
103 }
104
105 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_monomorphize/src/mono_checks/move_check.rs:105",
"rustc_monomorphize::mono_checks::move_check",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_monomorphize/src/mono_checks/move_check.rs"),
::tracing_core::__macro_support::Option::Some(105u32),
::tracing_core::__macro_support::Option::Some("rustc_monomorphize::mono_checks::move_check"),
::tracing_core::field::FieldSet::new(&["def_id", "fn_span"],
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&def_id) as
&dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&fn_span) as
&dyn Value))])
});
} else { ; }
};debug!(?def_id, ?fn_span);
106
107 for arg in args {
108 let operand: &mir::Operand<'tcx> = &arg.node;
112 if let mir::Operand::Move(_) = operand {
113 continue;
114 }
115
116 if let Some(too_large_size) = self.operand_size_if_too_large(limit, operand) {
117 self.lint_large_assignment(limit.0, too_large_size, location, arg.span);
118 };
119 }
120 }
121
122 fn operand_size_if_too_large(
123 &mut self,
124 limit: Limit,
125 operand: &mir::Operand<'tcx>,
126 ) -> Option<Size> {
127 let ty = operand.ty(self.body, self.tcx);
128 let ty = self.monomorphize(ty);
129 let Ok(layout) =
130 self.tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty))
131 else {
132 return None;
133 };
134 if layout.size.bytes_usize() > limit.0 {
135 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_monomorphize/src/mono_checks/move_check.rs:135",
"rustc_monomorphize::mono_checks::move_check",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_monomorphize/src/mono_checks/move_check.rs"),
::tracing_core::__macro_support::Option::Some(135u32),
::tracing_core::__macro_support::Option::Some("rustc_monomorphize::mono_checks::move_check"),
::tracing_core::field::FieldSet::new(&["layout"],
::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};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&layout) as
&dyn Value))])
});
} else { ; }
};debug!(?layout);
136 Some(layout.size)
137 } else {
138 None
139 }
140 }
141
142 fn lint_large_assignment(
143 &mut self,
144 limit: usize,
145 too_large_size: Size,
146 location: Location,
147 span: Span,
148 ) {
149 let source_info = self.body.source_info(location);
150
151 let lint_root = source_info.scope.lint_root(&self.body.source_scopes);
152 let Some(lint_root) = lint_root else {
153 return;
159 };
160
161 let reported_span = self
163 .body
164 .source_scopes
165 .get(source_info.scope)
166 .and_then(|source_scope_data| source_scope_data.inlined)
167 .map(|(_, call_site)| call_site)
168 .unwrap_or(span);
169
170 for previously_reported_span in &self.move_size_spans {
171 if previously_reported_span.overlaps(reported_span) {
172 return;
173 }
174 }
175
176 self.tcx.emit_node_span_lint(
177 LARGE_ASSIGNMENTS,
178 lint_root,
179 reported_span,
180 LargeAssignmentsLint {
181 span: reported_span,
182 size: too_large_size.bytes(),
183 limit: limit as u64,
184 },
185 );
186
187 self.move_size_spans.push(reported_span);
188 }
189}
190
191fn assoc_fn_of_type<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, fn_ident: Ident) -> Option<DefId> {
192 for &impl_def_id in tcx.inherent_impls(def_id) {
193 if let Some(new) = tcx.associated_items(impl_def_id).find_by_ident_and_kind(
194 tcx,
195 fn_ident,
196 AssocTag::Fn,
197 def_id,
198 ) {
199 return Some(new.def_id);
200 }
201 }
202 None
203}
204
205pub(crate) fn skip_move_check_fns(tcx: TyCtxt<'_>, _: ()) -> FxIndexSet<DefId> {
206 let fns = [
207 (tcx.lang_items().owned_box(), "new"),
208 (tcx.get_diagnostic_item(sym::Rc), "new"),
209 (tcx.get_diagnostic_item(sym::Arc), "new"),
210 ];
211 fns.into_iter()
212 .filter_map(|(def_id, fn_name)| {
213 def_id.and_then(|def_id| assoc_fn_of_type(tcx, def_id, Ident::from_str(fn_name)))
214 })
215 .collect()
216}