Skip to content

Commit

Permalink
[experiment] Add MustClone and impl Copy+MustClone on Range{,From,Inc…
Browse files Browse the repository at this point in the history
…lusive}.
  • Loading branch information
eddyb committed Sep 25, 2020
1 parent 5bfeee5 commit 346ede0
Show file tree
Hide file tree
Showing 21 changed files with 118 additions and 54 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 @@ -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.

0 comments on commit 346ede0

Please sign in to comment.