From 789451b43a068b10b7073b8a3194c1c2128b48b2 Mon Sep 17 00:00:00 2001
From: Martin Nordholts <enselic@gmail.com>
Date: Sun, 3 Sep 2023 08:15:25 +0200
Subject: [PATCH] Allow `large_assignments` for Box/Arc/Rc initialization

Does the `stop linting in box/arc initialization` task of 83518.
---
 compiler/rustc_monomorphize/src/collector.rs  | 101 ++++++++++++++++--
 .../async-await/large_moves.attribute.stderr  |  22 +++-
 .../ui/async-await/large_moves.option.stderr  |  22 +++-
 tests/ui/async-await/large_moves.rs           |  18 ++++
 4 files changed, 147 insertions(+), 16 deletions(-)

diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index bf88360a8c1f9..77f1942331845 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -179,8 +179,8 @@ use rustc_middle::query::TyCtxtAt;
 use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCoercion};
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{
-    self, GenericParamDefKind, Instance, InstanceDef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt,
-    VtblEntry,
+    self, AssocKind, GenericParamDefKind, Instance, InstanceDef, Ty, TyCtxt, TypeFoldable,
+    TypeVisitableExt, VtblEntry,
 };
 use rustc_middle::ty::{GenericArgKind, GenericArgs};
 use rustc_middle::{middle::codegen_fn_attrs::CodegenFnAttrFlags, mir::visit::TyContext};
@@ -188,6 +188,7 @@ use rustc_session::config::EntryFnType;
 use rustc_session::lint::builtin::LARGE_ASSIGNMENTS;
 use rustc_session::Limit;
 use rustc_span::source_map::{dummy_spanned, respan, Span, Spanned, DUMMY_SP};
+use rustc_span::symbol::{sym, Ident};
 use rustc_target::abi::Size;
 use std::path::PathBuf;
 
@@ -431,7 +432,7 @@ fn collect_items_rec<'tcx>(
                         hir::InlineAsmOperand::SymFn { anon_const } => {
                             let fn_ty =
                                 tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id);
-                            visit_fn_use(tcx, fn_ty, false, *op_sp, &mut used_items);
+                            visit_fn_use(tcx, fn_ty, false, *op_sp, &mut used_items, &[]);
                         }
                         hir::InlineAsmOperand::SymStatic { path: _, def_id } => {
                             let instance = Instance::mono(tcx, *def_id);
@@ -592,6 +593,11 @@ struct MirUsedCollector<'a, 'tcx> {
     instance: Instance<'tcx>,
     /// Spans for move size lints already emitted. Helps avoid duplicate lints.
     move_size_spans: Vec<Span>,
+    /// If true, we should temporarily skip move size checks, because we are
+    /// processing an operand to a `skip_move_check_fns` function call.
+    skip_move_size_check: bool,
+    /// Set of functions for which it is OK to move large data into.
+    skip_move_check_fns: Vec<DefId>,
 }
 
 impl<'a, 'tcx> MirUsedCollector<'a, 'tcx> {
@@ -690,7 +696,14 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
             ) => {
                 let fn_ty = operand.ty(self.body, self.tcx);
                 let fn_ty = self.monomorphize(fn_ty);
-                visit_fn_use(self.tcx, fn_ty, false, span, &mut self.output);
+                visit_fn_use(
+                    self.tcx,
+                    fn_ty,
+                    false,
+                    span,
+                    &mut self.output,
+                    &self.skip_move_check_fns,
+                );
             }
             mir::Rvalue::Cast(
                 mir::CastKind::PointerCoercion(PointerCoercion::ClosureFnPointer(_)),
@@ -789,7 +802,14 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
             mir::TerminatorKind::Call { ref func, .. } => {
                 let callee_ty = func.ty(self.body, tcx);
                 let callee_ty = self.monomorphize(callee_ty);
-                visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output)
+                self.skip_move_size_check = visit_fn_use(
+                    self.tcx,
+                    callee_ty,
+                    true,
+                    source,
+                    &mut self.output,
+                    &self.skip_move_check_fns,
+                )
             }
             mir::TerminatorKind::Drop { ref place, .. } => {
                 let ty = place.ty(self.body, self.tcx).ty;
@@ -801,7 +821,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
                     match *op {
                         mir::InlineAsmOperand::SymFn { ref value } => {
                             let fn_ty = self.monomorphize(value.literal.ty());
-                            visit_fn_use(self.tcx, fn_ty, false, source, &mut self.output);
+                            visit_fn_use(self.tcx, fn_ty, false, source, &mut self.output, &[]);
                         }
                         mir::InlineAsmOperand::SymStatic { def_id } => {
                             let instance = Instance::mono(self.tcx, def_id);
@@ -840,12 +860,13 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
         }
 
         self.super_terminator(terminator, location);
+        self.skip_move_size_check = false;
     }
 
     fn visit_operand(&mut self, operand: &mir::Operand<'tcx>, location: Location) {
         self.super_operand(operand, location);
         let move_size_limit = self.tcx.move_size_limit().0;
-        if move_size_limit > 0 {
+        if move_size_limit > 0 && !self.skip_move_size_check {
             self.check_move_size(move_size_limit, operand, location);
         }
     }
@@ -876,8 +897,11 @@ fn visit_fn_use<'tcx>(
     is_direct_call: bool,
     source: Span,
     output: &mut MonoItems<'tcx>,
-) {
+    skip_move_check_fns: &[DefId],
+) -> bool {
+    let mut skip_move_size_check = false;
     if let ty::FnDef(def_id, args) = *ty.kind() {
+        skip_move_size_check = skip_move_check_fns.contains(&def_id);
         let instance = if is_direct_call {
             ty::Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args)
         } else {
@@ -888,6 +912,7 @@ fn visit_fn_use<'tcx>(
         };
         visit_instance_use(tcx, instance, is_direct_call, source, output);
     }
+    skip_move_size_check
 }
 
 fn visit_instance_use<'tcx>(
@@ -1365,6 +1390,31 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt
     }
 }
 
+fn add_assoc_fn<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: Option<DefId>,
+    fn_ident: Ident,
+    skip_move_check_fns: &mut Vec<DefId>,
+) {
+    if let Some(def_id) = def_id.and_then(|def_id| assoc_fn_of_type(tcx, def_id, fn_ident)) {
+        skip_move_check_fns.push(def_id);
+    }
+}
+
+fn assoc_fn_of_type<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, fn_ident: Ident) -> Option<DefId> {
+    for impl_def_id in tcx.inherent_impls(def_id) {
+        if let Some(new) = tcx.associated_items(impl_def_id).find_by_name_and_kind(
+            tcx,
+            fn_ident,
+            AssocKind::Fn,
+            def_id,
+        ) {
+            return Some(new.def_id);
+        }
+    }
+    return None;
+}
+
 /// Scans the MIR in order to find function calls, closures, and drop-glue.
 #[instrument(skip(tcx, output), level = "debug")]
 fn collect_used_items<'tcx>(
@@ -1373,8 +1423,39 @@ fn collect_used_items<'tcx>(
     output: &mut MonoItems<'tcx>,
 ) {
     let body = tcx.instance_mir(instance.def);
-    MirUsedCollector { tcx, body: &body, output, instance, move_size_spans: vec![] }
-        .visit_body(&body);
+
+    let mut skip_move_check_fns = vec![];
+    if tcx.move_size_limit().0 > 0 {
+        add_assoc_fn(
+            tcx,
+            tcx.lang_items().owned_box(),
+            Ident::from_str("new"),
+            &mut skip_move_check_fns,
+        );
+        add_assoc_fn(
+            tcx,
+            tcx.get_diagnostic_item(sym::Arc),
+            Ident::from_str("new"),
+            &mut skip_move_check_fns,
+        );
+        add_assoc_fn(
+            tcx,
+            tcx.get_diagnostic_item(sym::Rc),
+            Ident::from_str("new"),
+            &mut skip_move_check_fns,
+        );
+    }
+
+    MirUsedCollector {
+        tcx,
+        body: &body,
+        output,
+        instance,
+        move_size_spans: vec![],
+        skip_move_size_check: false,
+        skip_move_check_fns,
+    }
+    .visit_body(&body);
 }
 
 #[instrument(skip(tcx, output), level = "debug")]
diff --git a/tests/ui/async-await/large_moves.attribute.stderr b/tests/ui/async-await/large_moves.attribute.stderr
index ef9fd78ffe362..1d1999462cedd 100644
--- a/tests/ui/async-await/large_moves.attribute.stderr
+++ b/tests/ui/async-await/large_moves.attribute.stderr
@@ -1,5 +1,5 @@
 error: moving 10024 bytes
-  --> $DIR/large_moves.rs:19:14
+  --> $DIR/large_moves.rs:21:14
    |
 LL |     let z = (x, 42);
    |              ^ value moved from here
@@ -12,12 +12,28 @@ LL | #![deny(large_assignments)]
    |         ^^^^^^^^^^^^^^^^^
 
 error: moving 10024 bytes
-  --> $DIR/large_moves.rs:20:13
+  --> $DIR/large_moves.rs:22:13
    |
 LL |     let a = z.0;
    |             ^^^ value moved from here
    |
    = note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
 
-error: aborting due to 2 previous errors
+error: moving 9999 bytes
+  --> $DIR/large_moves.rs:27:13
+   |
+LL |     let _ = NotBox::new([0; 9999]);
+   |             ^^^^^^^^^^^^^^^^^^^^^^ value moved from here
+   |
+   = note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
+
+error: moving 9999 bytes
+  --> $DIR/large_moves.rs:41:13
+   |
+LL |             data,
+   |             ^^^^ value moved from here
+   |
+   = note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
+
+error: aborting due to 4 previous errors
 
diff --git a/tests/ui/async-await/large_moves.option.stderr b/tests/ui/async-await/large_moves.option.stderr
index ef9fd78ffe362..1d1999462cedd 100644
--- a/tests/ui/async-await/large_moves.option.stderr
+++ b/tests/ui/async-await/large_moves.option.stderr
@@ -1,5 +1,5 @@
 error: moving 10024 bytes
-  --> $DIR/large_moves.rs:19:14
+  --> $DIR/large_moves.rs:21:14
    |
 LL |     let z = (x, 42);
    |              ^ value moved from here
@@ -12,12 +12,28 @@ LL | #![deny(large_assignments)]
    |         ^^^^^^^^^^^^^^^^^
 
 error: moving 10024 bytes
-  --> $DIR/large_moves.rs:20:13
+  --> $DIR/large_moves.rs:22:13
    |
 LL |     let a = z.0;
    |             ^^^ value moved from here
    |
    = note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
 
-error: aborting due to 2 previous errors
+error: moving 9999 bytes
+  --> $DIR/large_moves.rs:27:13
+   |
+LL |     let _ = NotBox::new([0; 9999]);
+   |             ^^^^^^^^^^^^^^^^^^^^^^ value moved from here
+   |
+   = note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
+
+error: moving 9999 bytes
+  --> $DIR/large_moves.rs:41:13
+   |
+LL |             data,
+   |             ^^^^ value moved from here
+   |
+   = note: The current maximum size is 1000, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
+
+error: aborting due to 4 previous errors
 
diff --git a/tests/ui/async-await/large_moves.rs b/tests/ui/async-await/large_moves.rs
index faf6c66c6124e..62b1210469410 100644
--- a/tests/ui/async-await/large_moves.rs
+++ b/tests/ui/async-await/large_moves.rs
@@ -9,6 +9,8 @@
 // edition:2018
 // compile-flags: -Zmir-opt-level=0
 
+use std::{sync::Arc, rc::Rc};
+
 fn main() {
     let x = async {
         let y = [0; 9999];
@@ -19,8 +21,24 @@ fn main() {
     let z = (x, 42); //~ ERROR large_assignments
     let a = z.0; //~ ERROR large_assignments
     let b = z.1;
+    let _ = Arc::new([0; 9999]); // OK!
+    let _ = Box::new([0; 9999]); // OK!
+    let _ = Rc::new([0; 9999]); // OK!
+    let _ = NotBox::new([0; 9999]); //~ ERROR large_assignments
 }
 
 async fn thing(y: &[u8]) {
     dbg!(y);
 }
+
+struct NotBox {
+    data: [u8; 9999],
+}
+
+impl NotBox {
+    fn new(data: [u8; 9999]) -> Self {
+        Self {
+            data, //~ ERROR large_assignments
+        }
+    }
+}