about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSantiago Pastorino <spastorino@gmail.com>2024-12-11 18:00:56 -0300
committerSantiago Pastorino <spastorino@gmail.com>2025-03-06 17:58:32 -0300
commitdcdfd551f05073ececfa437be002ce7804b31c91 (patch)
tree813604b622c9bb09c15e1724a6cebfd9d340b53d
parent57cb498989ac29fbe9938ed2f4a31a3eab6850dc (diff)
downloadrust-dcdfd551f05073ececfa437be002ce7804b31c91.tar.gz
rust-dcdfd551f05073ececfa437be002ce7804b31c91.zip
Add UseCloned trait related code
-rw-r--r--compiler/rustc_hir/src/lang_items.rs1
-rw-r--r--compiler/rustc_mir_build/src/builder/expr/into.rs72
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_trait_selection/src/infer.rs10
-rw-r--r--library/alloc/src/lib.rs1
-rw-r--r--library/alloc/src/rc.rs7
-rw-r--r--library/alloc/src/sync.rs7
-rw-r--r--library/core/src/clone.rs34
-rw-r--r--library/core/src/num/bignum.rs2
-rw-r--r--library/core/src/num/nonzero.rs4
-rw-r--r--library/core/src/option.rs3
-rw-r--r--library/core/src/result.rs8
-rw-r--r--tests/ui/ergonomic-clones/closure.rs30
-rw-r--r--tests/ui/ergonomic-clones/dotuse.rs9
-rw-r--r--tests/ui/feature-gates/feature-gate-ergonomic-clones.rs19
-rw-r--r--tests/ui/feature-gates/feature-gate-ergonomic-clones.stderr32
16 files changed, 197 insertions, 43 deletions
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index f5626937ec4..2c73ea6a11a 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -171,6 +171,7 @@ language_item_table! {
     Copy,                    sym::copy,                copy_trait,                 Target::Trait,          GenericRequirement::Exact(0);
     Clone,                   sym::clone,               clone_trait,                Target::Trait,          GenericRequirement::None;
     CloneFn,                 sym::clone_fn,            clone_fn,                   Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+    UseCloned,               sym::use_cloned,          use_cloned_trait,           Target::Trait,          GenericRequirement::None;
     Sync,                    sym::sync,                sync_trait,                 Target::Trait,          GenericRequirement::Exact(0);
     DiscriminantKind,        sym::discriminant_kind,   discriminant_kind_trait,    Target::Trait,          GenericRequirement::None;
     /// The associated item of the `DiscriminantKind` trait.
diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs
index d89f3773e4a..333e69475c5 100644
--- a/compiler/rustc_mir_build/src/builder/expr/into.rs
+++ b/compiler/rustc_mir_build/src/builder/expr/into.rs
@@ -11,6 +11,7 @@ use rustc_middle::thir::*;
 use rustc_middle::ty::{CanonicalUserTypeAnnotation, Ty};
 use rustc_span::DUMMY_SP;
 use rustc_span::source_map::Spanned;
+use rustc_trait_selection::infer::InferCtxtExt;
 use tracing::{debug, instrument};
 
 use crate::builder::expr::category::{Category, RvalueFunc};
@@ -295,33 +296,52 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 let place = unpack!(block = this.as_place(block, expr));
                 let ty = place.ty(&this.local_decls, this.tcx).ty;
 
-                // Convert `expr.use` to a call like `Clone::clone(&expr)`
-                let success = this.cfg.start_new_block();
-                let clone_trait = this.tcx.require_lang_item(LangItem::Clone, None);
-                let clone_fn = this.tcx.associated_item_def_ids(clone_trait)[0];
-                let func = Operand::function_handle(this.tcx, clone_fn, [ty.into()], expr_span);
-                let ref_ty = Ty::new_imm_ref(this.tcx, this.tcx.lifetimes.re_erased, ty);
-                let ref_place = this.temp(ref_ty, span);
-                this.cfg.push_assign(
-                    block,
-                    source_info,
-                    ref_place,
-                    Rvalue::Ref(this.tcx.lifetimes.re_erased, BorrowKind::Shared, place),
-                );
-                this.cfg.terminate(
-                    block,
-                    source_info,
-                    TerminatorKind::Call {
-                        func,
-                        args: [Spanned { node: Operand::Move(ref_place), span: DUMMY_SP }].into(),
+                if this.tcx.type_is_copy_modulo_regions(this.infcx.typing_env(this.param_env), ty) {
+                    this.cfg.push_assign(
+                        block,
+                        source_info,
                         destination,
-                        target: Some(success),
-                        unwind: UnwindAction::Unreachable,
-                        call_source: CallSource::Misc,
-                        fn_span: expr_span,
-                    },
-                );
-                success.unit()
+                        Rvalue::Use(Operand::Copy(place)),
+                    );
+                    block.unit()
+                } else if this.infcx.type_is_use_cloned_modulo_regions(this.param_env, ty) {
+                    // Convert `expr.use` to a call like `Clone::clone(&expr)`
+                    let success = this.cfg.start_new_block();
+                    let clone_trait = this.tcx.require_lang_item(LangItem::Clone, None);
+                    let clone_fn = this.tcx.associated_item_def_ids(clone_trait)[0];
+                    let func = Operand::function_handle(this.tcx, clone_fn, [ty.into()], expr_span);
+                    let ref_ty = Ty::new_imm_ref(this.tcx, this.tcx.lifetimes.re_erased, ty);
+                    let ref_place = this.temp(ref_ty, span);
+                    this.cfg.push_assign(
+                        block,
+                        source_info,
+                        ref_place,
+                        Rvalue::Ref(this.tcx.lifetimes.re_erased, BorrowKind::Shared, place),
+                    );
+                    this.cfg.terminate(
+                        block,
+                        source_info,
+                        TerminatorKind::Call {
+                            func,
+                            args: [Spanned { node: Operand::Move(ref_place), span: DUMMY_SP }]
+                                .into(),
+                            destination,
+                            target: Some(success),
+                            unwind: UnwindAction::Unreachable,
+                            call_source: CallSource::Misc,
+                            fn_span: expr_span,
+                        },
+                    );
+                    success.unit()
+                } else {
+                    this.cfg.push_assign(
+                        block,
+                        source_info,
+                        destination,
+                        Rvalue::Use(Operand::Move(place)),
+                    );
+                    block.unit()
+                }
             }
             ExprKind::Use { source } => this.expr_into_dest(destination, block, source),
             ExprKind::Borrow { arg, borrow_kind } => {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 3357e085a7d..a8c38d8244d 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -2182,6 +2182,7 @@ symbols! {
         unwrap,
         unwrap_binder,
         unwrap_or,
+        use_cloned,
         use_extern_macros,
         use_nested_groups,
         used,
diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs
index f373706b296..5cf0600ade8 100644
--- a/compiler/rustc_trait_selection/src/infer.rs
+++ b/compiler/rustc_trait_selection/src/infer.rs
@@ -53,6 +53,16 @@ impl<'tcx> InferCtxt<'tcx> {
         traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, clone_def_id)
     }
 
+    fn type_is_use_cloned_modulo_regions(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        ty: Ty<'tcx>,
+    ) -> bool {
+        let ty = self.resolve_vars_if_possible(ty);
+        let use_cloned_def_id = self.tcx.require_lang_item(LangItem::UseCloned, None);
+        traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, use_cloned_def_id)
+    }
+
     fn type_is_sized_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
         let lang_item = self.tcx.require_lang_item(LangItem::Sized, None);
         traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, lang_item)
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index cb93100f56c..aac0eefc519 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -113,6 +113,7 @@
 #![feature(deprecated_suggestion)]
 #![feature(deref_pure_trait)]
 #![feature(dispatch_from_dyn)]
+#![feature(ergonomic_clones)]
 #![feature(error_generic_member_access)]
 #![feature(exact_size_is_empty)]
 #![feature(extend_one)]
diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs
index 09206c2f8b2..5847bd8f281 100644
--- a/library/alloc/src/rc.rs
+++ b/library/alloc/src/rc.rs
@@ -245,6 +245,7 @@ use core::any::Any;
 use core::cell::Cell;
 #[cfg(not(no_global_oom_handling))]
 use core::clone::CloneToUninit;
+use core::clone::UseCloned;
 use core::cmp::Ordering;
 use core::hash::{Hash, Hasher};
 use core::intrinsics::abort;
@@ -2333,6 +2334,9 @@ impl<T: ?Sized, A: Allocator + Clone> Clone for Rc<T, A> {
     }
 }
 
+#[unstable(feature = "ergonomic_clones", issue = "132290")]
+impl<T: ?Sized, A: Allocator + Clone> UseCloned for Rc<T, A> {}
+
 #[cfg(not(no_global_oom_handling))]
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: Default> Default for Rc<T> {
@@ -3496,6 +3500,9 @@ impl<T: ?Sized, A: Allocator + Clone> Clone for Weak<T, A> {
     }
 }
 
+#[unstable(feature = "ergonomic_clones", issue = "132290")]
+impl<T: ?Sized, A: Allocator + Clone> UseCloned for Weak<T, A> {}
+
 #[stable(feature = "rc_weak", since = "1.4.0")]
 impl<T: ?Sized, A: Allocator> fmt::Debug for Weak<T, A> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs
index dba1449347a..ddd0677ee35 100644
--- a/library/alloc/src/sync.rs
+++ b/library/alloc/src/sync.rs
@@ -11,6 +11,7 @@
 use core::any::Any;
 #[cfg(not(no_global_oom_handling))]
 use core::clone::CloneToUninit;
+use core::clone::UseCloned;
 use core::cmp::Ordering;
 use core::hash::{Hash, Hasher};
 use core::intrinsics::abort;
@@ -2197,6 +2198,9 @@ impl<T: ?Sized, A: Allocator + Clone> Clone for Arc<T, A> {
     }
 }
 
+#[unstable(feature = "ergonomic_clones", issue = "132290")]
+impl<T: ?Sized, A: Allocator + Clone> UseCloned for Arc<T, A> {}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: ?Sized, A: Allocator> Deref for Arc<T, A> {
     type Target = T;
@@ -3158,6 +3162,9 @@ impl<T: ?Sized, A: Allocator + Clone> Clone for Weak<T, A> {
     }
 }
 
+#[unstable(feature = "ergonomic_clones", issue = "132290")]
+impl<T: ?Sized, A: Allocator + Clone> UseCloned for Weak<T, A> {}
+
 #[stable(feature = "downgraded_weak", since = "1.10.0")]
 impl<T> Default for Weak<T> {
     /// Constructs a new `Weak<T>`, without allocating memory.
diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs
index 00300328b64..249ca6c091f 100644
--- a/library/core/src/clone.rs
+++ b/library/core/src/clone.rs
@@ -184,6 +184,40 @@ pub macro Clone($item:item) {
     /* compiler built-in */
 }
 
+/// Trait for objects whose [`Clone`] impl is lightweight (e.g. reference-counted)
+///
+/// Cloning an object implementing this trait should in general:
+/// - be O(1) (constant) time regardless of the amount of data managed by the object,
+/// - not require a memory allocation,
+/// - not require copying more than roughly 64 bytes (a typical cache line size),
+/// - not block the current thread,
+/// - not have any semantic side effects (e.g. allocating a file descriptor), and
+/// - not have overhead larger than a couple of atomic operations.
+///
+/// The `UseCloned` trait does not provide a method; instead, it indicates that
+/// `Clone::clone` is lightweight, and allows the use of the `.use` syntax.
+#[unstable(feature = "ergonomic_clones", issue = "132290")]
+#[cfg_attr(not(bootstrap), lang = "use_cloned")]
+pub trait UseCloned: Clone {
+    // Empty.
+}
+
+macro_rules! impl_use_cloned {
+    ($($t:ty)*) => {
+        $(
+            #[unstable(feature = "ergonomic_clones", issue = "132290")]
+            impl UseCloned for $t {}
+        )*
+    }
+}
+
+impl_use_cloned! {
+    usize u8 u16 u32 u64 u128
+    isize i8 i16 i32 i64 i128
+             f16 f32 f64 f128
+    bool char
+}
+
 // FIXME(aburka): these structs are used solely by #[derive] to
 // assert that every component of a type implements Clone or Copy.
 //
diff --git a/library/core/src/num/bignum.rs b/library/core/src/num/bignum.rs
index 2a47c89e2ae..9de6b349d94 100644
--- a/library/core/src/num/bignum.rs
+++ b/library/core/src/num/bignum.rs
@@ -405,6 +405,8 @@ macro_rules! define_bignum {
             }
         }
 
+        impl crate::clone::UseCloned for $name {}
+
         impl crate::fmt::Debug for $name {
             fn fmt(&self, f: &mut crate::fmt::Formatter<'_>) -> crate::fmt::Result {
                 let sz = if self.size < 1 { 1 } else { self.size };
diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs
index a967b72c4fa..5b997d44278 100644
--- a/library/core/src/num/nonzero.rs
+++ b/library/core/src/num/nonzero.rs
@@ -1,6 +1,7 @@
 //! Definitions of integer that is known not to equal zero.
 
 use super::{IntErrorKind, ParseIntError};
+use crate::clone::UseCloned;
 use crate::cmp::Ordering;
 use crate::hash::{Hash, Hasher};
 use crate::marker::{Freeze, StructuralPartialEq};
@@ -183,6 +184,9 @@ where
     }
 }
 
+#[unstable(feature = "ergonomic_clones", issue = "132290")]
+impl<T> UseCloned for NonZero<T> where T: ZeroablePrimitive {}
+
 #[stable(feature = "nonzero", since = "1.28.0")]
 impl<T> Copy for NonZero<T> where T: ZeroablePrimitive {}
 
diff --git a/library/core/src/option.rs b/library/core/src/option.rs
index a9f06b92ad5..f668c6f0672 100644
--- a/library/core/src/option.rs
+++ b/library/core/src/option.rs
@@ -2050,6 +2050,9 @@ where
     }
 }
 
+#[unstable(feature = "ergonomic_clones", issue = "132290")]
+impl<T> crate::clone::UseCloned for Option<T> where T: crate::clone::UseCloned {}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T> Default for Option<T> {
     /// Returns [`None`][Option::None].
diff --git a/library/core/src/result.rs b/library/core/src/result.rs
index 92b5cba1531..ee98a47523f 100644
--- a/library/core/src/result.rs
+++ b/library/core/src/result.rs
@@ -1744,6 +1744,14 @@ where
     }
 }
 
+#[unstable(feature = "ergonomic_clones", issue = "132290")]
+impl<T, E> crate::clone::UseCloned for Result<T, E>
+where
+    T: crate::clone::UseCloned,
+    E: crate::clone::UseCloned,
+{
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T, E> IntoIterator for Result<T, E> {
     type Item = T;
diff --git a/tests/ui/ergonomic-clones/closure.rs b/tests/ui/ergonomic-clones/closure.rs
index 340b9ece50e..19ef6f4e13d 100644
--- a/tests/ui/ergonomic-clones/closure.rs
+++ b/tests/ui/ergonomic-clones/closure.rs
@@ -3,6 +3,9 @@
 
 #![feature(ergonomic_clones)]
 
+use std::clone::UseCloned;
+use std::future::Future;
+
 fn ergonomic_clone_closure_no_captures() -> i32 {
     let cl = use || {
         1
@@ -10,7 +13,7 @@ fn ergonomic_clone_closure_no_captures() -> i32 {
     cl()
 }
 
-fn ergonomic_clone_closure_with_captures() -> String {
+fn ergonomic_clone_closure_move() -> String {
     let s = String::from("hi");
 
     let cl = use || {
@@ -19,14 +22,31 @@ fn ergonomic_clone_closure_with_captures() -> String {
     cl()
 }
 
-fn ergonomic_clone_async_closures() -> String {
+#[derive(Clone)]
+struct Foo;
+
+impl UseCloned for Foo {}
+
+fn ergonomic_clone_closure_use_cloned() -> Foo {
+    let f = Foo;
+
+    let f1 = use || {
+        f
+    };
+
+    let f2 = use || {
+        f
+    };
+
+    f
+}
+
+fn ergonomic_clone_async_closures() -> impl Future<Output = String> {
     let s = String::from("hi");
 
     async use {
         s
-    };
-
-    s
+    }
 }
 
 fn main() {}
diff --git a/tests/ui/ergonomic-clones/dotuse.rs b/tests/ui/ergonomic-clones/dotuse.rs
index 928c27296b1..4bab400fef2 100644
--- a/tests/ui/ergonomic-clones/dotuse.rs
+++ b/tests/ui/ergonomic-clones/dotuse.rs
@@ -2,11 +2,18 @@
 
 #![feature(ergonomic_clones)]
 
+use std::clone::UseCloned;
+
 fn basic_test(x: i32) -> i32 {
     x.use.use.abs()
 }
 
-fn do_not_move_test(x: String) -> String {
+#[derive(Clone)]
+struct Foo;
+
+impl UseCloned for Foo {}
+
+fn do_not_move_test(x: Foo) -> Foo {
     let s = x.use;
     x
 }
diff --git a/tests/ui/feature-gates/feature-gate-ergonomic-clones.rs b/tests/ui/feature-gates/feature-gate-ergonomic-clones.rs
index 0ffc3925005..8ae78e27d0e 100644
--- a/tests/ui/feature-gates/feature-gate-ergonomic-clones.rs
+++ b/tests/ui/feature-gates/feature-gate-ergonomic-clones.rs
@@ -1,19 +1,28 @@
+use std::clone::UseCloned;
+//~^ ERROR use of unstable library feature `ergonomic_clones` [E0658]
+
 fn ergonomic_clone(x: i32) -> i32 {
     x.use
     //~^ ERROR `.use` calls are experimental [E0658]
 }
 
+#[derive(Clone)]
+struct Foo;
+
+impl UseCloned for Foo {}
+//~^ ERROR use of unstable library feature `ergonomic_clones` [E0658]
+
 fn ergonomic_closure_clone() {
-    let s1 = String::from("hi!");
+    let f1 = Foo;
 
-    let s2 = use || {
+    let f2 = use || {
         //~^ ERROR `.use` calls are experimental [E0658]
-        s1
+        f1
     };
 
-    let s3 = use || {
+    let f3 = use || {
         //~^ ERROR `.use` calls are experimental [E0658]
-        s1
+        f1
     };
 }
 
diff --git a/tests/ui/feature-gates/feature-gate-ergonomic-clones.stderr b/tests/ui/feature-gates/feature-gate-ergonomic-clones.stderr
index 366151c91a6..68d64715620 100644
--- a/tests/ui/feature-gates/feature-gate-ergonomic-clones.stderr
+++ b/tests/ui/feature-gates/feature-gate-ergonomic-clones.stderr
@@ -1,5 +1,5 @@
 error[E0658]: `.use` calls are experimental
-  --> $DIR/feature-gate-ergonomic-clones.rs:2:7
+  --> $DIR/feature-gate-ergonomic-clones.rs:5:7
    |
 LL |     x.use
    |       ^^^
@@ -9,9 +9,9 @@ LL |     x.use
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: `.use` calls are experimental
-  --> $DIR/feature-gate-ergonomic-clones.rs:9:14
+  --> $DIR/feature-gate-ergonomic-clones.rs:18:14
    |
-LL |     let s2 = use || {
+LL |     let f2 = use || {
    |              ^^^
    |
    = note: see issue #132290 <https://github.com/rust-lang/rust/issues/132290> for more information
@@ -19,15 +19,35 @@ LL |     let s2 = use || {
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: `.use` calls are experimental
-  --> $DIR/feature-gate-ergonomic-clones.rs:14:14
+  --> $DIR/feature-gate-ergonomic-clones.rs:23:14
    |
-LL |     let s3 = use || {
+LL |     let f3 = use || {
    |              ^^^
    |
    = note: see issue #132290 <https://github.com/rust-lang/rust/issues/132290> for more information
    = help: add `#![feature(ergonomic_clones)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-error: aborting due to 3 previous errors
+error[E0658]: use of unstable library feature `ergonomic_clones`
+  --> $DIR/feature-gate-ergonomic-clones.rs:1:5
+   |
+LL | use std::clone::UseCloned;
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #132290 <https://github.com/rust-lang/rust/issues/132290> for more information
+   = help: add `#![feature(ergonomic_clones)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error[E0658]: use of unstable library feature `ergonomic_clones`
+  --> $DIR/feature-gate-ergonomic-clones.rs:12:6
+   |
+LL | impl UseCloned for Foo {}
+   |      ^^^^^^^^^
+   |
+   = note: see issue #132290 <https://github.com/rust-lang/rust/issues/132290> for more information
+   = help: add `#![feature(ergonomic_clones)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+
+error: aborting due to 5 previous errors
 
 For more information about this error, try `rustc --explain E0658`.