Skip to content

Commit e2504cf

Browse files
committed
Auto merge of #44551 - scalexm:copy-clone-closures, r=arielb1
Implement `Copy`/`Clone` for closures Implement RFC [#2132](rust-lang/rfcs#2132) (tracking issue: #44490). NB: I'm not totally sure about the whole feature gates thing, that's my first PR of this kind...
2 parents 870483a + 3fa3fe0 commit e2504cf

17 files changed

+264
-18
lines changed

src/librustc/dep_graph/dep_node.rs

+2
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,8 @@ define_dep_nodes!( <'tcx>
570570
[] MissingExternCrateItem(CrateNum),
571571
[] UsedCrateSource(CrateNum),
572572
[] PostorderCnums,
573+
[] HasCloneClosures(CrateNum),
574+
[] HasCopyClosures(CrateNum),
573575

574576
[] Freevars(DefId),
575577
[] MaybeUnusedTraitImport(DefId),

src/librustc/traits/select.rs

+22-7
Original file line numberDiff line numberDiff line change
@@ -1340,7 +1340,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
13401340
self.assemble_candidates_from_impls(obligation, &mut candidates)?;
13411341

13421342
// For other types, we'll use the builtin rules.
1343-
let copy_conditions = self.copy_conditions(obligation);
1343+
let copy_conditions = self.copy_clone_conditions(obligation);
13441344
self.assemble_builtin_bound_candidates(copy_conditions, &mut candidates)?;
13451345
} else if lang_items.sized_trait() == Some(def_id) {
13461346
// Sized is never implementable by end-users, it is
@@ -1355,7 +1355,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
13551355
// Same builtin conditions as `Copy`, i.e. every type which has builtin support
13561356
// for `Copy` also has builtin support for `Clone`, + tuples and arrays of `Clone`
13571357
// types have builtin support for `Clone`.
1358-
let clone_conditions = self.copy_conditions(obligation);
1358+
let clone_conditions = self.copy_clone_conditions(obligation);
13591359
self.assemble_builtin_bound_candidates(clone_conditions, &mut candidates)?;
13601360
}
13611361

@@ -2050,7 +2050,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
20502050
}
20512051
}
20522052

2053-
fn copy_conditions(&mut self, obligation: &TraitObligation<'tcx>)
2053+
fn copy_clone_conditions(&mut self, obligation: &TraitObligation<'tcx>)
20542054
-> BuiltinImplConditions<'tcx>
20552055
{
20562056
// NOTE: binder moved to (*)
@@ -2068,8 +2068,7 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
20682068
Where(ty::Binder(Vec::new()))
20692069
}
20702070

2071-
ty::TyDynamic(..) | ty::TyStr | ty::TySlice(..) |
2072-
ty::TyClosure(..) | ty::TyGenerator(..) |
2071+
ty::TyDynamic(..) | ty::TyStr | ty::TySlice(..) | ty::TyGenerator(..) |
20732072
ty::TyRef(_, ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => {
20742073
Never
20752074
}
@@ -2084,6 +2083,22 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
20842083
Where(ty::Binder(tys.to_vec()))
20852084
}
20862085

2086+
ty::TyClosure(def_id, substs) => {
2087+
let trait_id = obligation.predicate.def_id();
2088+
let copy_closures =
2089+
Some(trait_id) == self.tcx().lang_items().copy_trait() &&
2090+
self.tcx().has_copy_closures(def_id.krate);
2091+
let clone_closures =
2092+
Some(trait_id) == self.tcx().lang_items().clone_trait() &&
2093+
self.tcx().has_clone_closures(def_id.krate);
2094+
2095+
if copy_closures || clone_closures {
2096+
Where(ty::Binder(substs.upvar_tys(def_id, self.tcx()).collect()))
2097+
} else {
2098+
Never
2099+
}
2100+
}
2101+
20872102
ty::TyAdt(..) | ty::TyProjection(..) | ty::TyParam(..) | ty::TyAnon(..) => {
20882103
// Fallback to whatever user-defined impls exist in this case.
20892104
None
@@ -2370,10 +2385,10 @@ impl<'cx, 'gcx, 'tcx> SelectionContext<'cx, 'gcx, 'tcx> {
23702385
self.sized_conditions(obligation)
23712386
}
23722387
_ if Some(trait_def) == lang_items.copy_trait() => {
2373-
self.copy_conditions(obligation)
2388+
self.copy_clone_conditions(obligation)
23742389
}
23752390
_ if Some(trait_def) == lang_items.clone_trait() => {
2376-
self.copy_conditions(obligation)
2391+
self.copy_clone_conditions(obligation)
23772392
}
23782393
_ => bug!("unexpected builtin trait {:?}", trait_def)
23792394
};

src/librustc/ty/context.rs

+8
Original file line numberDiff line numberDiff line change
@@ -2247,4 +2247,12 @@ pub fn provide(providers: &mut ty::maps::Providers) {
22472247
assert_eq!(cnum, LOCAL_CRATE);
22482248
tcx.output_filenames.clone()
22492249
};
2250+
providers.has_copy_closures = |tcx, cnum| {
2251+
assert_eq!(cnum, LOCAL_CRATE);
2252+
tcx.sess.features.borrow().copy_closures
2253+
};
2254+
providers.has_clone_closures = |tcx, cnum| {
2255+
assert_eq!(cnum, LOCAL_CRATE);
2256+
tcx.sess.features.borrow().clone_closures
2257+
};
22502258
}

src/librustc/ty/instance.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub enum InstanceDef<'tcx> {
3838
/// drop_in_place::<T>; None for empty drop glue.
3939
DropGlue(DefId, Option<Ty<'tcx>>),
4040

41-
/// Builtin method implementation, e.g. `Clone::clone`.
41+
///`<T as Clone>::clone` shim.
4242
CloneShim(DefId, Ty<'tcx>),
4343
}
4444

src/librustc/ty/maps/config.rs

+12
Original file line numberDiff line numberDiff line change
@@ -490,3 +490,15 @@ impl<'tcx> QueryDescription for queries::output_filenames<'tcx> {
490490
format!("output_filenames")
491491
}
492492
}
493+
494+
impl<'tcx> QueryDescription for queries::has_clone_closures<'tcx> {
495+
fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
496+
format!("seeing if the crate has enabled `Clone` closures")
497+
}
498+
}
499+
500+
impl<'tcx> QueryDescription for queries::has_copy_closures<'tcx> {
501+
fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
502+
format!("seeing if the crate has enabled `Copy` closures")
503+
}
504+
}

src/librustc/ty/maps/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,9 @@ define_maps! { <'tcx>
326326
[] fn compile_codegen_unit: CompileCodegenUnit(InternedString) -> Stats,
327327
[] fn output_filenames: output_filenames_node(CrateNum)
328328
-> Arc<OutputFilenames>,
329+
330+
[] fn has_copy_closures: HasCopyClosures(CrateNum) -> bool,
331+
[] fn has_clone_closures: HasCloneClosures(CrateNum) -> bool,
329332
}
330333

331334
//////////////////////////////////////////////////////////////////////

src/librustc_metadata/cstore.rs

+10
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,16 @@ impl CrateMetadata {
218218
attr::contains_name(&attrs, "no_builtins")
219219
}
220220

221+
pub fn has_copy_closures(&self) -> bool {
222+
let attrs = self.get_item_attrs(CRATE_DEF_INDEX);
223+
attr::contains_feature_attr(&attrs, "copy_closures")
224+
}
225+
226+
pub fn has_clone_closures(&self) -> bool {
227+
let attrs = self.get_item_attrs(CRATE_DEF_INDEX);
228+
attr::contains_feature_attr(&attrs, "clone_closures")
229+
}
230+
221231
pub fn panic_strategy(&self) -> PanicStrategy {
222232
self.root.panic_strategy.clone()
223233
}

src/librustc_metadata/cstore_impl.rs

+3
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,9 @@ provide! { <'tcx> tcx, def_id, other, cdata,
231231
}
232232

233233
used_crate_source => { Rc::new(cdata.source.clone()) }
234+
235+
has_copy_closures => { cdata.has_copy_closures() }
236+
has_clone_closures => { cdata.has_clone_closures() }
234237
}
235238

236239
pub fn provide_local<'tcx>(providers: &mut Providers<'tcx>) {

src/librustc_mir/shim.rs

+17-6
Original file line numberDiff line numberDiff line change
@@ -296,9 +296,15 @@ fn build_clone_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
296296
let len = len.val.to_const_int().unwrap().to_u64().unwrap();
297297
builder.array_shim(ty, len)
298298
}
299-
ty::TyTuple(tys, _) => builder.tuple_shim(tys),
299+
ty::TyClosure(def_id, substs) => {
300+
builder.tuple_like_shim(
301+
&substs.upvar_tys(def_id, tcx).collect::<Vec<_>>(),
302+
AggregateKind::Closure(def_id, substs)
303+
)
304+
}
305+
ty::TyTuple(tys, _) => builder.tuple_like_shim(&**tys, AggregateKind::Tuple),
300306
_ => {
301-
bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty);
307+
bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty)
302308
}
303309
};
304310

@@ -613,7 +619,12 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
613619
self.block(vec![], TerminatorKind::Resume, true);
614620
}
615621

616-
fn tuple_shim(&mut self, tys: &ty::Slice<Ty<'tcx>>) {
622+
fn tuple_like_shim(&mut self, tys: &[ty::Ty<'tcx>], kind: AggregateKind<'tcx>) {
623+
match kind {
624+
AggregateKind::Tuple | AggregateKind::Closure(..) => (),
625+
_ => bug!("only tuples and closures are accepted"),
626+
};
627+
617628
let rcvr = Lvalue::Local(Local::new(1+0)).deref();
618629

619630
let mut returns = Vec::new();
@@ -646,17 +657,17 @@ impl<'a, 'tcx> CloneShimBuilder<'a, 'tcx> {
646657
}
647658
}
648659

649-
// `return (returns[0], returns[1], ..., returns[tys.len() - 1]);`
660+
// `return kind(returns[0], returns[1], ..., returns[tys.len() - 1]);`
650661
let ret_statement = self.make_statement(
651662
StatementKind::Assign(
652663
Lvalue::Local(RETURN_POINTER),
653664
Rvalue::Aggregate(
654-
box AggregateKind::Tuple,
665+
box kind,
655666
returns.into_iter().map(Operand::Consume).collect()
656667
)
657668
)
658669
);
659-
self.block(vec![ret_statement], TerminatorKind::Return, false);
670+
self.block(vec![ret_statement], TerminatorKind::Return, false);
660671
}
661672
}
662673

src/libsyntax/attr.rs

+14
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,20 @@ pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: &str) -> Option<S
500500
.and_then(|at| at.value_str())
501501
}
502502

503+
/// Check if `attrs` contains an attribute like `#![feature(feature_name)]`.
504+
/// This will not perform any "sanity checks" on the form of the attributes.
505+
pub fn contains_feature_attr(attrs: &[Attribute], feature_name: &str) -> bool {
506+
attrs.iter().any(|item| {
507+
item.check_name("feature") &&
508+
item.meta_item_list().map(|list| {
509+
list.iter().any(|mi| {
510+
mi.word().map(|w| w.name() == feature_name)
511+
.unwrap_or(false)
512+
})
513+
}).unwrap_or(false)
514+
})
515+
}
516+
503517
/* Higher-level applications */
504518

505519
pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> {

src/libsyntax/feature_gate.rs

+27-4
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,10 @@ declare_features! (
385385

386386
// allow '|' at beginning of match arms (RFC 1925)
387387
(active, match_beginning_vert, "1.21.0", Some(44101)),
388+
389+
// Copy/Clone closures (RFC 2132)
390+
(active, clone_closures, "1.22.0", Some(44490)),
391+
(active, copy_closures, "1.22.0", Some(44490)),
388392
);
389393

390394
declare_features! (
@@ -1573,7 +1577,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
15731577
pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute]) -> Features {
15741578
let mut features = Features::new();
15751579

1576-
let mut feature_checker = MutexFeatureChecker::default();
1580+
let mut feature_checker = FeatureChecker::default();
15771581

15781582
for attr in krate_attrs {
15791583
if !attr.check_name("feature") {
@@ -1622,14 +1626,16 @@ pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute]) -> F
16221626
features
16231627
}
16241628

1625-
// A collector for mutually-exclusive features and their flag spans
1629+
/// A collector for mutually exclusive and interdependent features and their flag spans.
16261630
#[derive(Default)]
1627-
struct MutexFeatureChecker {
1631+
struct FeatureChecker {
16281632
proc_macro: Option<Span>,
16291633
custom_attribute: Option<Span>,
1634+
copy_closures: Option<Span>,
1635+
clone_closures: Option<Span>,
16301636
}
16311637

1632-
impl MutexFeatureChecker {
1638+
impl FeatureChecker {
16331639
// If this method turns out to be a hotspot due to branching,
16341640
// the branching can be eliminated by modifying `set!()` to set these spans
16351641
// only for the features that need to be checked for mutual exclusion.
@@ -1642,6 +1648,14 @@ impl MutexFeatureChecker {
16421648
if features.custom_attribute {
16431649
self.custom_attribute = self.custom_attribute.or(Some(span));
16441650
}
1651+
1652+
if features.copy_closures {
1653+
self.copy_closures = self.copy_closures.or(Some(span));
1654+
}
1655+
1656+
if features.clone_closures {
1657+
self.clone_closures = self.clone_closures.or(Some(span));
1658+
}
16451659
}
16461660

16471661
fn check(self, handler: &Handler) {
@@ -1653,6 +1667,15 @@ impl MutexFeatureChecker {
16531667

16541668
panic!(FatalError);
16551669
}
1670+
1671+
if let (Some(span), None) = (self.copy_closures, self.clone_closures) {
1672+
handler.struct_span_err(span, "`#![feature(copy_closures)]` can only be used with \
1673+
`#![feature(clone_closures)]`")
1674+
.span_note(span, "`#![feature(copy_closures)]` declared here")
1675+
.emit();
1676+
1677+
panic!(FatalError);
1678+
}
16561679
}
16571680
}
16581681

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#[derive(Clone)]
12+
struct S(i32);
13+
14+
fn main() {
15+
let a = S(5);
16+
let hello = move || {
17+
println!("Hello {}", a.0);
18+
};
19+
20+
let hello = hello.clone(); //~ ERROR no method named `clone` found for type
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
let a = 5;
13+
let hello = || {
14+
println!("Hello {}", a);
15+
};
16+
17+
let b = hello;
18+
let c = hello; //~ ERROR use of moved value: `hello` [E0382]
19+
}
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Check that closures do not implement `Clone` if their environment is not `Clone`.
12+
13+
#![feature(clone_closures)]
14+
15+
struct S(i32);
16+
17+
fn main() {
18+
let a = S(5);
19+
let hello = move || {
20+
println!("Hello {}", a.0);
21+
};
22+
23+
let hello = hello.clone(); //~ ERROR the trait bound `S: std::clone::Clone` is not satisfied
24+
}
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Check that closures do not implement `Copy` if their environment is not `Copy`.
12+
13+
#![feature(copy_closures)]
14+
#![feature(clone_closures)]
15+
16+
fn main() {
17+
let mut a = 5;
18+
let hello = || {
19+
a += 1;
20+
};
21+
22+
let b = hello;
23+
let c = hello; //~ ERROR use of moved value: `hello` [E0382]
24+
}

0 commit comments

Comments
 (0)