Skip to content

Commit 346ede0

Browse files
committed
[experiment] Add MustClone and impl Copy+MustClone on Range{,From,Inclusive}.
1 parent 5bfeee5 commit 346ede0

File tree

21 files changed

+118
-54
lines changed

21 files changed

+118
-54
lines changed

compiler/rustc_hir/src/lang_items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ language_item_table! {
196196
StructuralTeq, sym::structural_teq, structural_teq_trait, Target::Trait;
197197
Copy, sym::copy, copy_trait, Target::Trait;
198198
Clone, sym::clone, clone_trait, Target::Trait;
199+
MustClone, sym::must_clone, must_clone_trait, Target::Trait;
199200
Sync, sym::sync, sync_trait, Target::Trait;
200201
DiscriminantKind, sym::discriminant_kind, discriminant_kind_trait, Target::Trait;
201202
// The associated item of `trait DiscriminantKind`.

compiler/rustc_middle/src/query/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -912,6 +912,10 @@ rustc_queries! {
912912
query is_copy_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
913913
desc { "computing whether `{}` is `Copy`", env.value }
914914
}
915+
/// Query backing `TyS::is_must_clone`.
916+
query is_must_clone_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
917+
desc { "computing whether `{}` is `MustClone`", env.value }
918+
}
915919
/// Query backing `TyS::is_sized`.
916920
query is_sized_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
917921
desc { "computing whether `{}` is `Sized`", env.value }

compiler/rustc_middle/src/ty/util.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,21 @@ impl<'tcx> ty::TyS<'tcx> {
688688
tcx_at.is_copy_raw(param_env.and(self))
689689
}
690690

691+
/// Checks whether values of type `T` are discouraged to be copied,
692+
/// despite being `Copy` (i.e. whether `T: MustClone`).
693+
/// This is only relevant when deciding whether to to forbid/lint
694+
/// direct copies of `T`, but *not* e.g. whether a `struct`/`enum`
695+
/// with fields of type `T` can implement `Copy`, or whether some
696+
/// `Copy`-specific optimization (such as assuming `!needs_drop`)
697+
/// can be applied.
698+
pub fn is_must_clone(
699+
&'tcx self,
700+
tcx_at: TyCtxtAt<'tcx>,
701+
param_env: ty::ParamEnv<'tcx>,
702+
) -> bool {
703+
tcx_at.is_must_clone_raw(param_env.and(self))
704+
}
705+
691706
/// Checks whether values of this type `T` have a size known at
692707
/// compile time (i.e., whether `T: Sized`). Lifetimes are ignored
693708
/// for the purposes of this check, so it can be an
@@ -705,7 +720,6 @@ impl<'tcx> ty::TyS<'tcx> {
705720
/// optimization as well as the rules around static values. Note
706721
/// that the `Freeze` trait is not exposed to end users and is
707722
/// effectively an implementation detail.
708-
// FIXME: use `TyCtxtAt` instead of separate `Span`.
709723
pub fn is_freeze(&'tcx self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
710724
self.is_trivially_freeze() || tcx_at.is_freeze_raw(param_env.and(self))
711725
}

compiler/rustc_mir/src/borrow_check/type_check/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2000,6 +2000,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
20002000
// a required check to make sure that repeated elements implement `Copy`.
20012001
let span = body.source_info(location).span;
20022002
let ty = operand.ty(body, tcx);
2003+
// FIXME(eddyb) add `|| type_is_must_clone(ty)` (or handle separately).
20032004
if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) {
20042005
let ccx = ConstCx::new_with_param_env(
20052006
tcx,

compiler/rustc_mir_build/src/build/misc.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
6666
crate fn consume_by_copy_or_move(&self, place: Place<'tcx>) -> Operand<'tcx> {
6767
let tcx = self.hir.tcx();
6868
let ty = place.ty(&self.local_decls, tcx).ty;
69-
if !self.hir.type_is_copy_modulo_regions(ty, DUMMY_SP) {
69+
if !self.hir.type_is_copy_modulo_regions(ty, DUMMY_SP)
70+
|| self.hir.type_is_must_clone(ty, DUMMY_SP)
71+
{
7072
Operand::Move(place)
7173
} else {
7274
Operand::Copy(place)

compiler/rustc_mir_build/src/thir/cx/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,10 @@ impl<'a, 'tcx> Cx<'a, 'tcx> {
201201
crate fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool {
202202
self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span)
203203
}
204+
205+
crate fn type_is_must_clone(&self, ty: Ty<'tcx>, span: Span) -> bool {
206+
self.infcx.type_is_must_clone(self.param_env, ty, span)
207+
}
204208
}
205209

206210
impl<'tcx> UserAnnotatedTyHelpers<'tcx> for Cx<'_, 'tcx> {

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

+1
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,7 @@ fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec<Span>
598598

599599
/// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
600600
fn is_binding_by_move(cx: &MatchVisitor<'_, '_>, hir_id: HirId, span: Span) -> bool {
601+
// FIXME(eddyb) add `|| type_is_must_clone(ty)` (or make sure it's unnecessary).
601602
!cx.typeck_results.node_type(hir_id).is_copy_modulo_regions(cx.tcx.at(span), cx.param_env)
602603
}
603604

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -688,6 +688,7 @@ symbols! {
688688
mul,
689689
mul_assign,
690690
mul_with_overflow,
691+
must_clone,
691692
must_use,
692693
mut_ptr,
693694
mut_slice_ptr,

compiler/rustc_trait_selection/src/infer.rs

+26-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ pub trait InferCtxtExt<'tcx> {
2323
span: Span,
2424
) -> bool;
2525

26+
fn type_is_must_clone(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, span: Span) -> bool;
27+
2628
fn partially_normalize_associated_types_in<T>(
2729
&self,
2830
span: Span,
@@ -49,13 +51,35 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> {
4951

5052
let copy_def_id = self.tcx.require_lang_item(LangItem::Copy, None);
5153

52-
// This can get called from typeck (by euv), and `moves_by_default`
54+
// This can get called from typeck (by euv), and `is_copy_modulo_regions`
5355
// rightly refuses to work with inference variables, but
54-
// moves_by_default has a cache, which we want to use in other
56+
// `is_copy_modulo_regions` has a cache, which we want to use in other
5557
// cases.
5658
traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id, span)
5759
}
5860

61+
fn type_is_must_clone(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, span: Span) -> bool {
62+
let ty = self.resolve_vars_if_possible(&ty);
63+
64+
if !(param_env, ty).needs_infer() {
65+
return ty.is_must_clone(self.tcx.at(span), param_env);
66+
}
67+
68+
let must_clone_def_id = self.tcx.require_lang_item(LangItem::MustClone, None);
69+
70+
// This can get called from typeck (by euv), and `is_must_clone`
71+
// rightly refuses to work with inference variables, but
72+
// `is_must_clone` has a cache, which we want to use in other
73+
// cases.
74+
traits::type_known_to_meet_bound_modulo_regions(
75+
self,
76+
param_env,
77+
ty,
78+
must_clone_def_id,
79+
span,
80+
)
81+
}
82+
5983
/// Normalizes associated types in `value`, potentially returning
6084
/// new obligations that must further be processed.
6185
fn partially_normalize_associated_types_in<T>(

compiler/rustc_ty/src/common_traits.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>)
1010
is_item_raw(tcx, query, LangItem::Copy)
1111
}
1212

13+
fn is_must_clone_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
14+
is_item_raw(tcx, query, LangItem::MustClone)
15+
}
16+
1317
fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
1418
is_item_raw(tcx, query, LangItem::Sized)
1519
}
@@ -37,5 +41,11 @@ fn is_item_raw<'tcx>(
3741
}
3842

3943
pub(crate) fn provide(providers: &mut ty::query::Providers) {
40-
*providers = ty::query::Providers { is_copy_raw, is_sized_raw, is_freeze_raw, ..*providers };
44+
*providers = ty::query::Providers {
45+
is_copy_raw,
46+
is_must_clone_raw,
47+
is_sized_raw,
48+
is_freeze_raw,
49+
..*providers
50+
};
4151
}

compiler/rustc_typeck/src/expr_use_visitor.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -588,10 +588,9 @@ fn copy_or_move<'a, 'tcx>(
588588
mc: &mc::MemCategorizationContext<'a, 'tcx>,
589589
place_with_id: &PlaceWithHirId<'tcx>,
590590
) -> ConsumeMode {
591-
if !mc.type_is_copy_modulo_regions(
592-
place_with_id.place.ty(),
593-
mc.tcx().hir().span(place_with_id.hir_id),
594-
) {
591+
let ty = place_with_id.place.ty();
592+
let span = mc.tcx().hir().span(place_with_id.hir_id);
593+
if !mc.type_is_copy_modulo_regions(ty, span) || mc.type_is_must_clone(ty, span) {
595594
Move
596595
} else {
597596
Copy

compiler/rustc_typeck/src/mem_categorization.rs

+4
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
124124
self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span)
125125
}
126126

127+
crate fn type_is_must_clone(&self, ty: Ty<'tcx>, span: Span) -> bool {
128+
self.infcx.type_is_must_clone(self.param_env, ty, span)
129+
}
130+
127131
fn resolve_vars_if_possible<T>(&self, value: &T) -> T
128132
where
129133
T: TypeFoldable<'tcx>,

library/core/src/marker.rs

+15
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,21 @@ pub macro Copy($item:item) {
391391
/* compiler built-in */
392392
}
393393

394+
/// Types that are `Copy` but which require `.clone()` for copying,
395+
/// in order to indicate that some state is being duplicated, such
396+
/// as iterators (where implicit copies may appear misleading).
397+
///
398+
/// This doesn't propagate to aggregate types containing `MustClone`
399+
/// fields, unless they themselves also implement `MustClone`.
400+
/// Generics are also not affected, i.e. a `MustClone` type can still
401+
/// be used with a generic expecting a type that implements `Copy`.
402+
#[unstable(feature = "must_clone", issue = "none")]
403+
#[lang = "must_clone"]
404+
#[cfg(not(bootstrap))]
405+
pub trait MustClone: Copy {
406+
// Empty.
407+
}
408+
394409
/// Types for which it is safe to share references between threads.
395410
///
396411
/// This trait is automatically implemented when the compiler determines

library/core/src/ops/range.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ impl fmt::Debug for RangeFull {
7171
/// ```
7272
#[lang = "Range"]
7373
#[doc(alias = "..")]
74-
#[derive(Clone, Default, PartialEq, Eq, Hash)] // not Copy -- see #27186
74+
#[derive(Clone, Default, PartialEq, Eq, Hash)]
75+
#[cfg_attr(not(bootstrap), derive(Copy))]
7576
#[stable(feature = "rust1", since = "1.0.0")]
7677
pub struct Range<Idx> {
7778
/// The lower bound of the range (inclusive).
@@ -82,6 +83,10 @@ pub struct Range<Idx> {
8283
pub end: Idx,
8384
}
8485

86+
#[unstable(feature = "must_clone", issue = "none")]
87+
#[cfg(not(bootstrap))]
88+
impl<Idx: Copy> crate::marker::MustClone for Range<Idx> {}
89+
8590
#[stable(feature = "rust1", since = "1.0.0")]
8691
impl<Idx: fmt::Debug> fmt::Debug for Range<Idx> {
8792
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -174,14 +179,19 @@ impl<Idx: PartialOrd<Idx>> Range<Idx> {
174179
/// ```
175180
#[lang = "RangeFrom"]
176181
#[doc(alias = "..")]
177-
#[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186
182+
#[derive(Clone, PartialEq, Eq, Hash)]
183+
#[cfg_attr(not(bootstrap), derive(Copy))]
178184
#[stable(feature = "rust1", since = "1.0.0")]
179185
pub struct RangeFrom<Idx> {
180186
/// The lower bound of the range (inclusive).
181187
#[stable(feature = "rust1", since = "1.0.0")]
182188
pub start: Idx,
183189
}
184190

191+
#[unstable(feature = "must_clone", issue = "none")]
192+
#[cfg(not(bootstrap))]
193+
impl<Idx: Copy> crate::marker::MustClone for RangeFrom<Idx> {}
194+
185195
#[stable(feature = "rust1", since = "1.0.0")]
186196
impl<Idx: fmt::Debug> fmt::Debug for RangeFrom<Idx> {
187197
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -324,7 +334,8 @@ impl<Idx: PartialOrd<Idx>> RangeTo<Idx> {
324334
/// ```
325335
#[lang = "RangeInclusive"]
326336
#[doc(alias = "..=")]
327-
#[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186
337+
#[derive(Clone, PartialEq, Eq, Hash)]
338+
#[cfg_attr(not(bootstrap), derive(Copy))]
328339
#[stable(feature = "inclusive_range", since = "1.26.0")]
329340
pub struct RangeInclusive<Idx> {
330341
// Note that the fields here are not public to allow changing the
@@ -344,6 +355,10 @@ pub struct RangeInclusive<Idx> {
344355
pub(crate) exhausted: bool,
345356
}
346357

358+
#[unstable(feature = "must_clone", issue = "none")]
359+
#[cfg(not(bootstrap))]
360+
impl<Idx: Copy> crate::marker::MustClone for RangeInclusive<Idx> {}
361+
347362
impl<Idx> RangeInclusive<Idx> {
348363
/// Creates a new inclusive range. Equivalent to writing `start..=end`.
349364
///

src/test/codegen/avr/avr-func-addrspace.rs

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
pub trait Sized { }
1818
#[lang = "copy"]
1919
pub trait Copy { }
20+
#[lang = "must_clone"]
21+
pub trait MustClone { }
2022
#[lang = "receiver"]
2123
pub trait Receiver { }
2224

src/test/ui/range/range_traits-2.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
// run-pass
2+
13
use std::ops::*;
24

3-
#[derive(Copy, Clone)] //~ ERROR Copy
5+
#[derive(Copy, Clone)]
46
struct R(Range<usize>);
57

68
fn main() {}

src/test/ui/range/range_traits-2.stderr

-13
This file was deleted.

src/test/ui/range/range_traits-3.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
// run-pass
2+
13
use std::ops::*;
24

3-
#[derive(Copy, Clone)] //~ ERROR Copy
5+
#[derive(Copy, Clone)]
46
struct R(RangeFrom<usize>);
57

68
fn main() {}

src/test/ui/range/range_traits-3.stderr

-13
This file was deleted.

src/test/ui/range/range_traits-6.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
// run-pass
2+
13
use std::ops::*;
24

3-
#[derive(Copy, Clone)] //~ ERROR Copy
5+
#[derive(Copy, Clone)]
46
struct R(RangeInclusive<usize>);
57

68
fn main() {}

src/test/ui/range/range_traits-6.stderr

-13
This file was deleted.

0 commit comments

Comments
 (0)