Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[experiment] Add MustClone and impl Copy+MustClone on Range{,From,Inclusive}. #77180

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -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`.
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
16 changes: 15 additions & 1 deletion compiler/rustc_middle/src/ty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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))
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir/src/borrow_check/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_mir_build/src/build/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_mir_build/src/thir/cx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,7 @@ fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec<Span>

/// 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)
}

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 @@ -688,6 +688,7 @@ symbols! {
mul,
mul_assign,
mul_with_overflow,
must_clone,
must_use,
mut_ptr,
mut_slice_ptr,
Expand Down
28 changes: 26 additions & 2 deletions compiler/rustc_trait_selection/src/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(
&self,
span: Span,
Expand All @@ -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<T>(
Expand Down
12 changes: 11 additions & 1 deletion compiler/rustc_ty/src/common_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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
};
}
7 changes: 3 additions & 4 deletions compiler/rustc_typeck/src/expr_use_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_typeck/src/mem_categorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(&self, value: &T) -> T
where
T: TypeFoldable<'tcx>,
Expand Down
15 changes: 15 additions & 0 deletions library/core/src/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 18 additions & 3 deletions library/core/src/ops/range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Idx> {
/// The lower bound of the range (inclusive).
Expand All @@ -82,6 +83,10 @@ pub struct Range<Idx> {
pub end: Idx,
}

#[unstable(feature = "must_clone", issue = "none")]
#[cfg(not(bootstrap))]
impl<Idx: Copy> crate::marker::MustClone for Range<Idx> {}

#[stable(feature = "rust1", since = "1.0.0")]
impl<Idx: fmt::Debug> fmt::Debug for Range<Idx> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand Down Expand Up @@ -174,14 +179,19 @@ impl<Idx: PartialOrd<Idx>> Range<Idx> {
/// ```
#[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<Idx> {
/// The lower bound of the range (inclusive).
#[stable(feature = "rust1", since = "1.0.0")]
pub start: Idx,
}

#[unstable(feature = "must_clone", issue = "none")]
#[cfg(not(bootstrap))]
impl<Idx: Copy> crate::marker::MustClone for RangeFrom<Idx> {}

#[stable(feature = "rust1", since = "1.0.0")]
impl<Idx: fmt::Debug> fmt::Debug for RangeFrom<Idx> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand Down Expand Up @@ -324,7 +334,8 @@ impl<Idx: PartialOrd<Idx>> RangeTo<Idx> {
/// ```
#[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<Idx> {
// Note that the fields here are not public to allow changing the
Expand All @@ -344,6 +355,10 @@ pub struct RangeInclusive<Idx> {
pub(crate) exhausted: bool,
}

#[unstable(feature = "must_clone", issue = "none")]
#[cfg(not(bootstrap))]
impl<Idx: Copy> crate::marker::MustClone for RangeInclusive<Idx> {}

impl<Idx> RangeInclusive<Idx> {
/// Creates a new inclusive range. Equivalent to writing `start..=end`.
///
Expand Down
2 changes: 2 additions & 0 deletions src/test/codegen/avr/avr-func-addrspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
pub trait Sized { }
#[lang = "copy"]
pub trait Copy { }
#[lang = "must_clone"]
pub trait MustClone { }
#[lang = "receiver"]
pub trait Receiver { }

Expand Down
4 changes: 3 additions & 1 deletion src/test/ui/range/range_traits-2.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// run-pass

use std::ops::*;

#[derive(Copy, Clone)] //~ ERROR Copy
#[derive(Copy, Clone)]
struct R(Range<usize>);

fn main() {}
13 changes: 0 additions & 13 deletions src/test/ui/range/range_traits-2.stderr

This file was deleted.

4 changes: 3 additions & 1 deletion src/test/ui/range/range_traits-3.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// run-pass

use std::ops::*;

#[derive(Copy, Clone)] //~ ERROR Copy
#[derive(Copy, Clone)]
struct R(RangeFrom<usize>);

fn main() {}
13 changes: 0 additions & 13 deletions src/test/ui/range/range_traits-3.stderr

This file was deleted.

4 changes: 3 additions & 1 deletion src/test/ui/range/range_traits-6.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// run-pass

use std::ops::*;

#[derive(Copy, Clone)] //~ ERROR Copy
#[derive(Copy, Clone)]
struct R(RangeInclusive<usize>);

fn main() {}
13 changes: 0 additions & 13 deletions src/test/ui/range/range_traits-6.stderr

This file was deleted.