diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 5e4c03bec83dc..dbbc865bcc1b1 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -196,6 +196,7 @@ language_item_table! { StructuralTeq, sym::structural_teq, structural_teq_trait, Target::Trait; Copy, sym::copy, copy_trait, Target::Trait; Clone, sym::clone, clone_trait, Target::Trait; + MustClone, sym::must_clone, must_clone_trait, Target::Trait; Sync, sym::sync, sync_trait, Target::Trait; DiscriminantKind, sym::discriminant_kind, discriminant_kind_trait, Target::Trait; // The associated item of `trait DiscriminantKind`. diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 9c047cbfaefdd..dd47d74b7b720 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -912,6 +912,10 @@ rustc_queries! { query is_copy_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` is `Copy`", env.value } } + /// Query backing `TyS::is_must_clone`. + query is_must_clone_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is `MustClone`", env.value } + } /// Query backing `TyS::is_sized`. query is_sized_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` is `Sized`", env.value } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 4127b6535bca6..5a008f836dc24 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -688,6 +688,21 @@ impl<'tcx> ty::TyS<'tcx> { tcx_at.is_copy_raw(param_env.and(self)) } + /// Checks whether values of type `T` are discouraged to be copied, + /// despite being `Copy` (i.e. whether `T: MustClone`). + /// This is only relevant when deciding whether to to forbid/lint + /// direct copies of `T`, but *not* e.g. whether a `struct`/`enum` + /// with fields of type `T` can implement `Copy`, or whether some + /// `Copy`-specific optimization (such as assuming `!needs_drop`) + /// can be applied. + pub fn is_must_clone( + &'tcx self, + tcx_at: TyCtxtAt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> bool { + tcx_at.is_must_clone_raw(param_env.and(self)) + } + /// Checks whether values of this type `T` have a size known at /// compile time (i.e., whether `T: Sized`). Lifetimes are ignored /// for the purposes of this check, so it can be an @@ -705,7 +720,6 @@ impl<'tcx> ty::TyS<'tcx> { /// optimization as well as the rules around static values. Note /// that the `Freeze` trait is not exposed to end users and is /// effectively an implementation detail. - // FIXME: use `TyCtxtAt` instead of separate `Span`. pub fn is_freeze(&'tcx self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool { self.is_trivially_freeze() || tcx_at.is_freeze_raw(param_env.and(self)) } diff --git a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs index 3ace14610e2a7..4dfbc2dc9564d 100644 --- a/compiler/rustc_mir/src/borrow_check/type_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/type_check/mod.rs @@ -2000,6 +2000,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { // a required check to make sure that repeated elements implement `Copy`. let span = body.source_info(location).span; let ty = operand.ty(body, tcx); + // FIXME(eddyb) add `|| type_is_must_clone(ty)` (or handle separately). if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) { let ccx = ConstCx::new_with_param_env( tcx, diff --git a/compiler/rustc_mir_build/src/build/misc.rs b/compiler/rustc_mir_build/src/build/misc.rs index 29651d9bc663a..7062fc05730a3 100644 --- a/compiler/rustc_mir_build/src/build/misc.rs +++ b/compiler/rustc_mir_build/src/build/misc.rs @@ -66,7 +66,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { crate fn consume_by_copy_or_move(&self, place: Place<'tcx>) -> Operand<'tcx> { let tcx = self.hir.tcx(); let ty = place.ty(&self.local_decls, tcx).ty; - if !self.hir.type_is_copy_modulo_regions(ty, DUMMY_SP) { + if !self.hir.type_is_copy_modulo_regions(ty, DUMMY_SP) + || self.hir.type_is_must_clone(ty, DUMMY_SP) + { Operand::Move(place) } else { Operand::Copy(place) diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index cf42fee873e15..bcabf08fc5825 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -201,6 +201,10 @@ impl<'a, 'tcx> Cx<'a, 'tcx> { crate fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool { self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) } + + crate fn type_is_must_clone(&self, ty: Ty<'tcx>, span: Span) -> bool { + self.infcx.type_is_must_clone(self.param_env, ty, span) + } } impl<'tcx> UserAnnotatedTyHelpers<'tcx> for Cx<'_, 'tcx> { diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 047bf7db4c867..cb48c23a33663 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -598,6 +598,7 @@ fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec /// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`. fn is_binding_by_move(cx: &MatchVisitor<'_, '_>, hir_id: HirId, span: Span) -> bool { + // FIXME(eddyb) add `|| type_is_must_clone(ty)` (or make sure it's unnecessary). !cx.typeck_results.node_type(hir_id).is_copy_modulo_regions(cx.tcx.at(span), cx.param_env) } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 2d5c6451d1a52..41f16f413914e 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -688,6 +688,7 @@ symbols! { mul, mul_assign, mul_with_overflow, + must_clone, must_use, mut_ptr, mut_slice_ptr, diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index 4ec1b29bca4f1..1f41411c7589a 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -23,6 +23,8 @@ pub trait InferCtxtExt<'tcx> { span: Span, ) -> bool; + fn type_is_must_clone(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, span: Span) -> bool; + fn partially_normalize_associated_types_in( &self, span: Span, @@ -49,13 +51,35 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { let copy_def_id = self.tcx.require_lang_item(LangItem::Copy, None); - // This can get called from typeck (by euv), and `moves_by_default` + // This can get called from typeck (by euv), and `is_copy_modulo_regions` // rightly refuses to work with inference variables, but - // moves_by_default has a cache, which we want to use in other + // `is_copy_modulo_regions` has a cache, which we want to use in other // cases. traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id, span) } + fn type_is_must_clone(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, span: Span) -> bool { + let ty = self.resolve_vars_if_possible(&ty); + + if !(param_env, ty).needs_infer() { + return ty.is_must_clone(self.tcx.at(span), param_env); + } + + let must_clone_def_id = self.tcx.require_lang_item(LangItem::MustClone, None); + + // This can get called from typeck (by euv), and `is_must_clone` + // rightly refuses to work with inference variables, but + // `is_must_clone` has a cache, which we want to use in other + // cases. + traits::type_known_to_meet_bound_modulo_regions( + self, + param_env, + ty, + must_clone_def_id, + span, + ) + } + /// Normalizes associated types in `value`, potentially returning /// new obligations that must further be processed. fn partially_normalize_associated_types_in( diff --git a/compiler/rustc_ty/src/common_traits.rs b/compiler/rustc_ty/src/common_traits.rs index 24ba071786607..8dca28e59b5a5 100644 --- a/compiler/rustc_ty/src/common_traits.rs +++ b/compiler/rustc_ty/src/common_traits.rs @@ -10,6 +10,10 @@ fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) is_item_raw(tcx, query, LangItem::Copy) } +fn is_must_clone_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { + is_item_raw(tcx, query, LangItem::MustClone) +} + fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool { is_item_raw(tcx, query, LangItem::Sized) } @@ -37,5 +41,11 @@ fn is_item_raw<'tcx>( } pub(crate) fn provide(providers: &mut ty::query::Providers) { - *providers = ty::query::Providers { is_copy_raw, is_sized_raw, is_freeze_raw, ..*providers }; + *providers = ty::query::Providers { + is_copy_raw, + is_must_clone_raw, + is_sized_raw, + is_freeze_raw, + ..*providers + }; } diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs index e16f26c330401..613466de8db91 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_typeck/src/expr_use_visitor.rs @@ -588,10 +588,9 @@ fn copy_or_move<'a, 'tcx>( mc: &mc::MemCategorizationContext<'a, 'tcx>, place_with_id: &PlaceWithHirId<'tcx>, ) -> ConsumeMode { - if !mc.type_is_copy_modulo_regions( - place_with_id.place.ty(), - mc.tcx().hir().span(place_with_id.hir_id), - ) { + let ty = place_with_id.place.ty(); + let span = mc.tcx().hir().span(place_with_id.hir_id); + if !mc.type_is_copy_modulo_regions(ty, span) || mc.type_is_must_clone(ty, span) { Move } else { Copy diff --git a/compiler/rustc_typeck/src/mem_categorization.rs b/compiler/rustc_typeck/src/mem_categorization.rs index 04ead74936f88..3a95e63ef0840 100644 --- a/compiler/rustc_typeck/src/mem_categorization.rs +++ b/compiler/rustc_typeck/src/mem_categorization.rs @@ -124,6 +124,10 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) } + crate fn type_is_must_clone(&self, ty: Ty<'tcx>, span: Span) -> bool { + self.infcx.type_is_must_clone(self.param_env, ty, span) + } + fn resolve_vars_if_possible(&self, value: &T) -> T where T: TypeFoldable<'tcx>, diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index cdf742057b7b6..8435c8ca49e19 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -391,6 +391,21 @@ pub macro Copy($item:item) { /* compiler built-in */ } +/// Types that are `Copy` but which require `.clone()` for copying, +/// in order to indicate that some state is being duplicated, such +/// as iterators (where implicit copies may appear misleading). +/// +/// This doesn't propagate to aggregate types containing `MustClone` +/// fields, unless they themselves also implement `MustClone`. +/// Generics are also not affected, i.e. a `MustClone` type can still +/// be used with a generic expecting a type that implements `Copy`. +#[unstable(feature = "must_clone", issue = "none")] +#[lang = "must_clone"] +#[cfg(not(bootstrap))] +pub trait MustClone: Copy { + // Empty. +} + /// Types for which it is safe to share references between threads. /// /// This trait is automatically implemented when the compiler determines diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index 2eaf7601e54de..e60916ab09597 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -71,7 +71,8 @@ impl fmt::Debug for RangeFull { /// ``` #[lang = "Range"] #[doc(alias = "..")] -#[derive(Clone, Default, PartialEq, Eq, Hash)] // not Copy -- see #27186 +#[derive(Clone, Default, PartialEq, Eq, Hash)] +#[cfg_attr(not(bootstrap), derive(Copy))] #[stable(feature = "rust1", since = "1.0.0")] pub struct Range { /// The lower bound of the range (inclusive). @@ -82,6 +83,10 @@ pub struct Range { pub end: Idx, } +#[unstable(feature = "must_clone", issue = "none")] +#[cfg(not(bootstrap))] +impl crate::marker::MustClone for Range {} + #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for Range { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -174,7 +179,8 @@ impl> Range { /// ``` #[lang = "RangeFrom"] #[doc(alias = "..")] -#[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186 +#[derive(Clone, PartialEq, Eq, Hash)] +#[cfg_attr(not(bootstrap), derive(Copy))] #[stable(feature = "rust1", since = "1.0.0")] pub struct RangeFrom { /// The lower bound of the range (inclusive). @@ -182,6 +188,10 @@ pub struct RangeFrom { pub start: Idx, } +#[unstable(feature = "must_clone", issue = "none")] +#[cfg(not(bootstrap))] +impl crate::marker::MustClone for RangeFrom {} + #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for RangeFrom { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -324,7 +334,8 @@ impl> RangeTo { /// ``` #[lang = "RangeInclusive"] #[doc(alias = "..=")] -#[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186 +#[derive(Clone, PartialEq, Eq, Hash)] +#[cfg_attr(not(bootstrap), derive(Copy))] #[stable(feature = "inclusive_range", since = "1.26.0")] pub struct RangeInclusive { // Note that the fields here are not public to allow changing the @@ -344,6 +355,10 @@ pub struct RangeInclusive { pub(crate) exhausted: bool, } +#[unstable(feature = "must_clone", issue = "none")] +#[cfg(not(bootstrap))] +impl crate::marker::MustClone for RangeInclusive {} + impl RangeInclusive { /// Creates a new inclusive range. Equivalent to writing `start..=end`. /// diff --git a/src/test/codegen/avr/avr-func-addrspace.rs b/src/test/codegen/avr/avr-func-addrspace.rs index 0f15729158df1..41ec1ef1a8908 100644 --- a/src/test/codegen/avr/avr-func-addrspace.rs +++ b/src/test/codegen/avr/avr-func-addrspace.rs @@ -17,6 +17,8 @@ pub trait Sized { } #[lang = "copy"] pub trait Copy { } +#[lang = "must_clone"] +pub trait MustClone { } #[lang = "receiver"] pub trait Receiver { } diff --git a/src/test/ui/range/range_traits-2.rs b/src/test/ui/range/range_traits-2.rs index 234d7a64dc8b0..1bcb7bc89aaf8 100644 --- a/src/test/ui/range/range_traits-2.rs +++ b/src/test/ui/range/range_traits-2.rs @@ -1,6 +1,8 @@ +// run-pass + use std::ops::*; -#[derive(Copy, Clone)] //~ ERROR Copy +#[derive(Copy, Clone)] struct R(Range); fn main() {} diff --git a/src/test/ui/range/range_traits-2.stderr b/src/test/ui/range/range_traits-2.stderr deleted file mode 100644 index 8a9d15f09996b..0000000000000 --- a/src/test/ui/range/range_traits-2.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0204]: the trait `Copy` may not be implemented for this type - --> $DIR/range_traits-2.rs:3:10 - | -LL | #[derive(Copy, Clone)] - | ^^^^ -LL | struct R(Range); - | ------------ this field does not implement `Copy` - | - = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0204`. diff --git a/src/test/ui/range/range_traits-3.rs b/src/test/ui/range/range_traits-3.rs index 2d597cce5ad56..f492eb512630f 100644 --- a/src/test/ui/range/range_traits-3.rs +++ b/src/test/ui/range/range_traits-3.rs @@ -1,6 +1,8 @@ +// run-pass + use std::ops::*; -#[derive(Copy, Clone)] //~ ERROR Copy +#[derive(Copy, Clone)] struct R(RangeFrom); fn main() {} diff --git a/src/test/ui/range/range_traits-3.stderr b/src/test/ui/range/range_traits-3.stderr deleted file mode 100644 index 14fda58e1f813..0000000000000 --- a/src/test/ui/range/range_traits-3.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0204]: the trait `Copy` may not be implemented for this type - --> $DIR/range_traits-3.rs:3:10 - | -LL | #[derive(Copy, Clone)] - | ^^^^ -LL | struct R(RangeFrom); - | ---------------- this field does not implement `Copy` - | - = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0204`. diff --git a/src/test/ui/range/range_traits-6.rs b/src/test/ui/range/range_traits-6.rs index bce106bbfe79b..a042fb5989a45 100644 --- a/src/test/ui/range/range_traits-6.rs +++ b/src/test/ui/range/range_traits-6.rs @@ -1,6 +1,8 @@ +// run-pass + use std::ops::*; -#[derive(Copy, Clone)] //~ ERROR Copy +#[derive(Copy, Clone)] struct R(RangeInclusive); fn main() {} diff --git a/src/test/ui/range/range_traits-6.stderr b/src/test/ui/range/range_traits-6.stderr deleted file mode 100644 index 693600cdce4d1..0000000000000 --- a/src/test/ui/range/range_traits-6.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0204]: the trait `Copy` may not be implemented for this type - --> $DIR/range_traits-6.rs:3:10 - | -LL | #[derive(Copy, Clone)] - | ^^^^ -LL | struct R(RangeInclusive); - | --------------------- this field does not implement `Copy` - | - = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0204`.