Skip to content

Commit

Permalink
Add UseCloned trait related code
Browse files Browse the repository at this point in the history
  • Loading branch information
spastorino committed Dec 26, 2024
1 parent e3c69ef commit 94d4dde
Show file tree
Hide file tree
Showing 15 changed files with 175 additions and 36 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
63 changes: 41 additions & 22 deletions compiler/rustc_mir_build/src/builder/expr/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -287,28 +288,46 @@ 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;

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()
if this.tcx.type_is_copy_modulo_regions(this.infcx.typing_env(this.param_env), ty) {
this.cfg.push_assign(
block,
source_info,
destination,
Rvalue::Use(Operand::Copy(place)),
);
block.unit()
} else if this.infcx.type_is_use_cloned_modulo_regions(this.param_env, ty) {
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 } => {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2150,6 +2150,7 @@ symbols! {
unwrap_binder,
unwrap_or,
unwrap_unsafe_binder,
use_cloned,
use_extern_macros,
use_nested_groups,
used,
Expand Down
16 changes: 16 additions & 0 deletions compiler/rustc_trait_selection/src/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,22 @@ impl<'tcx> InferCtxt<'tcx> {
traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_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);

// This can get called from typeck (by euv), and `moves_by_default`
// rightly refuses to work with inference variables, but
// moves_by_default has a cache, which we want to use in other
// cases.
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)
Expand Down
1 change: 1 addition & 0 deletions library/alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,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)]
Expand Down
8 changes: 7 additions & 1 deletion library/alloc/src/rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@
use core::any::Any;
use core::cell::Cell;
#[cfg(not(no_global_oom_handling))]
use core::clone::CloneToUninit;
use core::clone::{CloneToUninit, UseCloned};
use core::cmp::Ordering;
use core::hash::{Hash, Hasher};
use core::intrinsics::abort;
Expand Down Expand Up @@ -2312,6 +2312,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> {
Expand Down Expand Up @@ -3484,6 +3487,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 {
Expand Down
8 changes: 7 additions & 1 deletion library/alloc/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use core::any::Any;
#[cfg(not(no_global_oom_handling))]
use core::clone::CloneToUninit;
use core::clone::{CloneToUninit, UseCloned};
use core::cmp::Ordering;
use core::hash::{Hash, Hasher};
use core::intrinsics::abort;
Expand Down Expand Up @@ -2172,6 +2172,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;
Expand Down Expand Up @@ -3143,6 +3146,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.
Expand Down
34 changes: 34 additions & 0 deletions library/core/src/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,24 @@ pub trait Clone: Sized {
}
}

/// 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,
/// - not have any semantic side effects (e.g. allocating a file descriptor), and
/// - not have overhead larger than a couple of atomic operations.
///
/// The `Use` 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")]
#[rustc_diagnostic_item = "UseCloned"]
#[rustc_trivial_field_reads]
pub trait UseCloned: Clone {}

/// Derive macro generating an impl of the trait `Clone`.
#[rustc_builtin_macro]
#[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
Expand Down Expand Up @@ -338,6 +356,22 @@ mod impls {
bool char
}

macro_rules! impl_use_cloned {
($($t:ty)*) => {
$(
#[unstable(feature = "ergonomic_clones", issue = "132290")]
impl crate::clone::UseCloned for $t {}
)*
}
}

impl_use_cloned! {
usize u8 u16 u32 u64 u128
isize i8 i16 i32 i64 i128
f16 f32 f64 f128
bool char
}

#[unstable(feature = "never_type", issue = "35121")]
impl Clone for ! {
#[inline]
Expand Down
2 changes: 2 additions & 0 deletions library/core/src/num/bignum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down
4 changes: 4 additions & 0 deletions library/core/src/num/nonzero.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -192,6 +193,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 {}

Expand Down
4 changes: 4 additions & 0 deletions library/core/src/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,7 @@
#![stable(feature = "rust1", since = "1.0.0")]

use crate::clone::UseCloned;
use crate::iter::{self, FusedIterator, TrustedLen};
use crate::ops::{self, ControlFlow, Deref, DerefMut};
use crate::panicking::{panic, panic_display};
Expand Down Expand Up @@ -2050,6 +2051,9 @@ where
}
}

#[unstable(feature = "ergonomic_clones", issue = "132290")]
impl<T> UseCloned for Option<T> where T: UseCloned {}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T> Default for Option<T> {
/// Returns [`None`][Option::None].
Expand Down
9 changes: 9 additions & 0 deletions library/core/src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,7 @@
#![stable(feature = "rust1", since = "1.0.0")]

use crate::clone::UseCloned;
use crate::iter::{self, FusedIterator, TrustedLen};
use crate::ops::{self, ControlFlow, Deref, DerefMut};
use crate::{convert, fmt, hint};
Expand Down Expand Up @@ -1744,6 +1745,14 @@ where
}
}

#[unstable(feature = "ergonomic_clones", issue = "132290")]
impl<T, E> UseCloned for Result<T, E>
where
T: UseCloned,
E: UseCloned,
{
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<T, E> IntoIterator for Result<T, E> {
type Item = T;
Expand Down
9 changes: 8 additions & 1 deletion tests/ui/ergonomic-clones/dotuse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
19 changes: 14 additions & 5 deletions tests/ui/feature-gates/feature-gate-ergonomic-clones.rs
Original file line number Diff line number Diff line change
@@ -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
};
}

Expand Down
Loading

0 comments on commit 94d4dde

Please sign in to comment.