From b078564fe627922790917d8d8a9b28a8877a8028 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 28 Mar 2025 10:01:29 -0300 Subject: [PATCH 1/8] Make use generated TerminatorKind::Call have call_source Use --- compiler/rustc_middle/src/mir/syntax.rs | 2 ++ compiler/rustc_mir_build/src/builder/expr/into.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 707c8d04d557f..ff9d32ebb7192 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -652,6 +652,8 @@ pub enum CallSource { /// Other types of desugaring that did not come from the HIR, but we don't care about /// for diagnostics (yet). Misc, + /// Use of value, generating a clone function call + Use, /// Normal function call, no special source Normal, } diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 333e69475c508..a9a07997410c0 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -328,7 +328,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { destination, target: Some(success), unwind: UnwindAction::Unreachable, - call_source: CallSource::Misc, + call_source: CallSource::Use, fn_span: expr_span, }, ); From b9159e09a7c2bba670fee45b554c7c2e7aba9dd8 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Tue, 1 Apr 2025 14:14:13 -0300 Subject: [PATCH 2/8] Use a local var for tcx --- compiler/rustc_codegen_ssa/src/mir/mod.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 0758e5d045673..ea47d32ea03ce 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -170,6 +170,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( ) { assert!(!instance.args.has_infer()); + let tcx = cx.tcx(); let llfn = cx.get_fn(instance); let mir = cx.tcx().instance_mir(instance.def); @@ -177,7 +178,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty()); debug!("fn_abi: {:?}", fn_abi); - if cx.tcx().codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) { + if tcx.codegen_fn_attrs(instance.def_id()).flags.contains(CodegenFnAttrFlags::NAKED) { crate::mir::naked_asm::codegen_naked_asm::(cx, &mir, instance); return; } @@ -194,7 +195,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } let cleanup_kinds = - base::wants_new_eh_instructions(cx.tcx().sess).then(|| analyze::cleanup_kinds(mir)); + base::wants_new_eh_instructions(tcx.sess).then(|| analyze::cleanup_kinds(mir)); let cached_llbbs: IndexVec> = mir.basic_blocks @@ -217,7 +218,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( cleanup_kinds, landing_pads: IndexVec::from_elem(None, &mir.basic_blocks), funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()), - cold_blocks: find_cold_blocks(cx.tcx(), mir), + cold_blocks: find_cold_blocks(tcx, mir), locals: locals::Locals::empty(), debug_context, per_local_var_debug_info: None, @@ -233,7 +234,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( fx.compute_per_local_var_debug_info(&mut start_bx).unzip(); fx.per_local_var_debug_info = per_local_var_debug_info; - let traversal_order = traversal::mono_reachable_reverse_postorder(mir, cx.tcx(), instance); + let traversal_order = traversal::mono_reachable_reverse_postorder(mir, tcx, instance); let memory_locals = analyze::non_ssa_locals(&fx, &traversal_order); // Allocate variable and temp allocas From 9f69c66a47d80cbced17d4d74d6e5c349db952d2 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 28 Mar 2025 10:03:00 -0300 Subject: [PATCH 3/8] Optimize codegen of use values that are copy post monomorphization --- compiler/rustc_codegen_ssa/src/mir/mod.rs | 70 +++++++++++++++++++++-- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index ea47d32ea03ce..fbbf93e2dc4d9 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -3,7 +3,7 @@ use std::iter; use rustc_index::IndexVec; use rustc_index::bit_set::DenseBitSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; -use rustc_middle::mir::{Local, UnwindTerminateReason, traversal}; +use rustc_middle::mir::{Body, Local, UnwindTerminateReason, traversal}; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_middle::{bug, mir, span_bug}; @@ -173,7 +173,12 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let tcx = cx.tcx(); let llfn = cx.get_fn(instance); - let mir = cx.tcx().instance_mir(instance.def); + let mir = tcx.instance_mir(instance.def); + let mir = instance.instantiate_mir_and_normalize_erasing_regions( + tcx, + ty::TypingEnv::fully_monomorphized(), + ty::EarlyBinder::bind(mir.clone()), + ); let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty()); debug!("fn_abi: {:?}", fn_abi); @@ -183,7 +188,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( return; } - let debug_context = cx.create_function_debug_context(instance, fn_abi, llfn, mir); + let debug_context = cx.create_function_debug_context(instance, fn_abi, llfn, &mir); let start_llbb = Bx::append_block(cx, llfn, "start"); let mut start_bx = Bx::build(cx, start_llbb); @@ -195,7 +200,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } let cleanup_kinds = - base::wants_new_eh_instructions(tcx.sess).then(|| analyze::cleanup_kinds(mir)); + base::wants_new_eh_instructions(tcx.sess).then(|| analyze::cleanup_kinds(&mir)); let cached_llbbs: IndexVec> = mir.basic_blocks @@ -205,6 +210,8 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( }) .collect(); + let mir = tcx.arena.alloc(optimize_use_clone::(cx, mir)); + let mut fx = FunctionCx { instance, mir, @@ -311,6 +318,61 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } +// FIXME: Move this function to mir::transform when post-mono MIR passes land. +fn optimize_use_clone<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + cx: &'a Bx::CodegenCx, + mut mir: Body<'tcx>, +) -> Body<'tcx> { + let tcx = cx.tcx(); + + if tcx.features().ergonomic_clones() { + for bb in mir.basic_blocks.as_mut() { + let mir::TerminatorKind::Call { + args, + destination, + target, + call_source: mir::CallSource::Use, + .. + } = &bb.terminator().kind + else { + continue; + }; + + // CallSource::Use calls always use 1 argument. + assert_eq!(args.len(), 1); + let arg = &args[0]; + + // These types are easily available from locals, so check that before + // doing DefId lookups to figure out what we're actually calling. + let arg_ty = arg.node.ty(&mir.local_decls, tcx); + + let ty::Ref(_region, inner_ty, mir::Mutability::Not) = *arg_ty.kind() else { continue }; + + if !tcx.type_is_copy_modulo_regions(cx.typing_env(), inner_ty) { + continue; + } + + let Some(arg_place) = arg.node.place() else { continue }; + + let destination_block = target.unwrap(); + + bb.statements.push(mir::Statement { + source_info: bb.terminator().source_info, + kind: mir::StatementKind::Assign(Box::new(( + *destination, + mir::Rvalue::Use(mir::Operand::Copy( + arg_place.project_deeper(&[mir::ProjectionElem::Deref], tcx), + )), + ))), + }); + + bb.terminator_mut().kind = mir::TerminatorKind::Goto { target: destination_block }; + } + } + + mir +} + /// Produces, for each argument, a `Value` pointing at the /// argument's value. As arguments are places, these are always /// indirect. From a0856eaff6dcf69456dd186bb310a88af210865a Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 28 Mar 2025 15:20:18 -0300 Subject: [PATCH 4/8] Add mir opt tests to be sure we generate copy, clones and moves when corresponds --- tests/mir-opt/ergonomic-clones/closure.rs | 55 +++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 tests/mir-opt/ergonomic-clones/closure.rs diff --git a/tests/mir-opt/ergonomic-clones/closure.rs b/tests/mir-opt/ergonomic-clones/closure.rs new file mode 100644 index 0000000000000..682f484498410 --- /dev/null +++ b/tests/mir-opt/ergonomic-clones/closure.rs @@ -0,0 +1,55 @@ +#![crate_type = "lib"] +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +use std::clone::UseCloned; + +pub fn ergonomic_clone_closure_move() -> String { + // CHECK-LABEL: fn ergonomic_clone_closure_move( + // CHECK: _0 = move (_1.0: std::string::String); + // CHECK-NOT: ::clone + let s = String::from("hi"); + + let cl = use || s; + cl() +} + +#[derive(Clone)] +struct Foo; + +impl UseCloned for Foo {} + +pub fn ergonomic_clone_closure_use_cloned() -> Foo { + // CHECK-LABEL: fn ergonomic_clone_closure_use_cloned( + // CHECK: ::clone + let f = Foo; + + let f1 = use || f; + + let f2 = use || f; + + f +} + +pub fn ergonomic_clone_closure_copy() -> i32 { + // CHECK-LABEL: fn ergonomic_clone_closure_copy( + // CHECK: _0 = copy ((*_1).0: i32); + // CHECK-NOT: ::clone + let i = 1; + + let i1 = use || i; + + let i2 = use || i; + + i +} + +pub fn ergonomic_clone_closure_use_cloned_generics(f: T) -> T { + // CHECK-LABEL: fn ergonomic_clone_closure_use_cloned_generics( + // CHECK: ::clone + let f1 = use || f; + + let f2 = use || f; + + f +} From 20f93c9e8e15f956b4f31afd0d5be42c1c0eea3f Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Fri, 28 Mar 2025 16:33:45 -0300 Subject: [PATCH 5/8] Add codegen test to be sure we get rid of uneeded clones after monomorphization --- tests/codegen/ergonomic-clones/closure.rs | 55 +++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 tests/codegen/ergonomic-clones/closure.rs diff --git a/tests/codegen/ergonomic-clones/closure.rs b/tests/codegen/ergonomic-clones/closure.rs new file mode 100644 index 0000000000000..b6fc81726419a --- /dev/null +++ b/tests/codegen/ergonomic-clones/closure.rs @@ -0,0 +1,55 @@ +//@ compile-flags: -C no-prepopulate-passes -Copt-level=0 -Zmir-opt-level=0 + +#![crate_type = "lib"] + +#![feature(ergonomic_clones)] +#![allow(incomplete_features)] + +use std::clone::UseCloned; + +pub fn ergonomic_clone_closure_move() -> String { + let s = String::from("hi"); + + // CHECK-NOT: ; call core::clone::impls::::clone + let cl = use || s; + cl() +} + +#[derive(Clone)] +struct Foo; + +impl UseCloned for Foo {} + +pub fn ergonomic_clone_closure_use_cloned() -> Foo { + let f = Foo; + + // CHECK: ; call ::clone + let f1 = use || f; + + // CHECK: ; call ::clone + let f2 = use || f; + + f +} + +pub fn ergonomic_clone_closure_copy() -> i32 { + let i = 1; + + // CHECK-NOT: ; call core::clone::impls::::clone + let i1 = use || i; + + // CHECK-NOT: ; call core::clone::impls::::clone + let i2 = use || i; + + i +} + +pub fn ergonomic_clone_closure_use_cloned_generics(f: T) -> T { + // CHECK-NOT: ; call core::clone::impls::::clone + let f1 = use || f; + + // CHECK-NOT: ; call core::clone::impls::::clone + let f2 = use || f; + + f +} From dfa58e39c812348f74624eb8ff9f8a7c6bafeb4b Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 31 Mar 2025 14:36:09 -0300 Subject: [PATCH 6/8] rustfmt does not support use closures yet --- rustfmt.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rustfmt.toml b/rustfmt.toml index c884a33729c44..7c384b876bf7d 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -53,4 +53,8 @@ ignore = [ # Code automatically generated and included. "compiler/rustc_codegen_gcc/src/intrinsic/archs.rs", "compiler/rustc_codegen_gcc/example", + + # Rustfmt doesn't support use closures yet + "tests/mir-opt/ergonomic-clones/closure.rs", + "tests/codegen/ergonomic-clones/closure.rs", ] From d6ad9e6191cd70234a9fd20a1e6e4f5e90cd1985 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 31 Mar 2025 12:03:39 -0300 Subject: [PATCH 7/8] Remove crash test that do not ICE anymore --- tests/crashes/129372.rs | 52 ----------------------------------------- 1 file changed, 52 deletions(-) delete mode 100644 tests/crashes/129372.rs diff --git a/tests/crashes/129372.rs b/tests/crashes/129372.rs deleted file mode 100644 index 43be01b35df29..0000000000000 --- a/tests/crashes/129372.rs +++ /dev/null @@ -1,52 +0,0 @@ -//@ known-bug: #129372 -//@ compile-flags: -Cdebuginfo=2 -Copt-level=0 - -pub struct Wrapper(T); -struct Struct; - -pub trait TraitA { - type AssocA<'t>; -} -pub trait TraitB { - type AssocB; -} - -pub fn helper(v: impl MethodTrait) { - let _local_that_causes_ice = v.method(); -} - -pub fn main() { - helper(Wrapper(Struct)); -} - -pub trait MethodTrait { - type Assoc<'a>; - - fn method(self) -> impl for<'a> FnMut(&'a ()) -> Self::Assoc<'a>; -} - -impl MethodTrait for T -where - ::AssocB: TraitA, -{ - type Assoc<'a> = ::AssocA<'a>; - - fn method(self) -> impl for<'a> FnMut(&'a ()) -> Self::Assoc<'a> { - move |_| loop {} - } -} - -impl TraitB for Wrapper -where - B: TraitB, -{ - type AssocB = T; -} - -impl TraitB for Struct { - type AssocB = Struct; -} - -impl TraitA for Struct { - type AssocA<'t> = Self; -} From 4a0ea02e3a8a799bdb58ac62078c56434cc51a27 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 7 Apr 2025 16:47:22 -0300 Subject: [PATCH 8/8] Only clone mir body if tcx.features().ergonomic_clones() --- compiler/rustc_codegen_ssa/src/mir/mod.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index fbbf93e2dc4d9..6a37889217ab1 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -173,12 +173,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let tcx = cx.tcx(); let llfn = cx.get_fn(instance); - let mir = tcx.instance_mir(instance.def); - let mir = instance.instantiate_mir_and_normalize_erasing_regions( - tcx, - ty::TypingEnv::fully_monomorphized(), - ty::EarlyBinder::bind(mir.clone()), - ); + let mut mir = tcx.instance_mir(instance.def); let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty()); debug!("fn_abi: {:?}", fn_abi); @@ -188,6 +183,15 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( return; } + if tcx.features().ergonomic_clones() { + let monomorphized_mir = instance.instantiate_mir_and_normalize_erasing_regions( + tcx, + ty::TypingEnv::fully_monomorphized(), + ty::EarlyBinder::bind(mir.clone()), + ); + mir = tcx.arena.alloc(optimize_use_clone::(cx, monomorphized_mir)); + } + let debug_context = cx.create_function_debug_context(instance, fn_abi, llfn, &mir); let start_llbb = Bx::append_block(cx, llfn, "start"); @@ -210,8 +214,6 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( }) .collect(); - let mir = tcx.arena.alloc(optimize_use_clone::(cx, mir)); - let mut fx = FunctionCx { instance, mir,