Skip to content

Commit 6f0c4a6

Browse files
committed
Auto merge of #101673 - crlf0710:generator_clone, r=oli-obk
Allow generators to impl Clone/Copy Revives #95137. It's a pity that the original pr didn't land because the implementation is almost complete! All credits goes to `@canndrew,` and i just resolved the merge conflicts and updated the feature gate version number. r? `@oli-obk`
2 parents 4c3f8eb + 4e9bcb5 commit 6f0c4a6

File tree

11 files changed

+642
-35
lines changed

11 files changed

+642
-35
lines changed

compiler/rustc_feature/src/active.rs

+2
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,8 @@ declare_features! (
394394
(active, ffi_returns_twice, "1.34.0", Some(58314), None),
395395
/// Allows using `#[repr(align(...))]` on function items
396396
(active, fn_align, "1.53.0", Some(82232), None),
397+
/// Allows generators to be cloned.
398+
(active, generator_clone, "CURRENT_RUSTC_VERSION", Some(95360), None),
397399
/// Allows defining generators.
398400
(active, generators, "1.21.0", Some(43122), None),
399401
/// Infer generic args for both consts and types.

compiler/rustc_mir_transform/src/shim.rs

+92-32
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc_hir::lang_items::LangItem;
44
use rustc_middle::mir::*;
55
use rustc_middle::ty::query::Providers;
66
use rustc_middle::ty::subst::{InternalSubsts, Subst};
7-
use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt};
7+
use rustc_middle::ty::{self, EarlyBinder, GeneratorSubsts, Ty, TyCtxt};
88
use rustc_target::abi::VariantIdx;
99

1010
use rustc_index::vec::{Idx, IndexVec};
@@ -323,6 +323,9 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -
323323
builder.tuple_like_shim(dest, src, substs.as_closure().upvar_tys())
324324
}
325325
ty::Tuple(..) => builder.tuple_like_shim(dest, src, self_ty.tuple_fields()),
326+
ty::Generator(gen_def_id, substs, hir::Movability::Movable) => {
327+
builder.generator_shim(dest, src, *gen_def_id, substs.as_generator())
328+
}
326329
_ => bug!("clone shim for `{:?}` which is not `Copy` and is not an aggregate", self_ty),
327330
};
328331

@@ -388,7 +391,7 @@ impl<'tcx> CloneShimBuilder<'tcx> {
388391
/// offset=0 will give you the index of the next BasicBlock,
389392
/// offset=1 will give the index of the next-to-next block,
390393
/// offset=-1 will give you the index of the last-created block
391-
fn block_index_offset(&mut self, offset: usize) -> BasicBlock {
394+
fn block_index_offset(&self, offset: usize) -> BasicBlock {
392395
BasicBlock::new(self.blocks.len() + offset)
393396
}
394397

@@ -461,49 +464,106 @@ impl<'tcx> CloneShimBuilder<'tcx> {
461464
);
462465
}
463466

464-
fn tuple_like_shim<I>(&mut self, dest: Place<'tcx>, src: Place<'tcx>, tys: I)
467+
fn clone_fields<I>(
468+
&mut self,
469+
dest: Place<'tcx>,
470+
src: Place<'tcx>,
471+
target: BasicBlock,
472+
mut unwind: BasicBlock,
473+
tys: I,
474+
) -> BasicBlock
465475
where
466476
I: IntoIterator<Item = Ty<'tcx>>,
467477
{
468-
let mut previous_field = None;
478+
// For an iterator of length n, create 2*n + 1 blocks.
469479
for (i, ity) in tys.into_iter().enumerate() {
480+
// Each iteration creates two blocks, referred to here as block 2*i and block 2*i + 1.
481+
//
482+
// Block 2*i attempts to clone the field. If successful it branches to 2*i + 2 (the
483+
// next clone block). If unsuccessful it branches to the previous unwind block, which
484+
// is initially the `unwind` argument passed to this function.
485+
//
486+
// Block 2*i + 1 is the unwind block for this iteration. It drops the cloned value
487+
// created by block 2*i. We store this block in `unwind` so that the next clone block
488+
// will unwind to it if cloning fails.
489+
470490
let field = Field::new(i);
471491
let src_field = self.tcx.mk_place_field(src, field, ity);
472492

473493
let dest_field = self.tcx.mk_place_field(dest, field, ity);
474494

475-
// #(2i + 1) is the cleanup block for the previous clone operation
476-
let cleanup_block = self.block_index_offset(1);
477-
// #(2i + 2) is the next cloning block
478-
// (or the Return terminator if this is the last block)
495+
let next_unwind = self.block_index_offset(1);
479496
let next_block = self.block_index_offset(2);
497+
self.make_clone_call(dest_field, src_field, ity, next_block, unwind);
498+
self.block(
499+
vec![],
500+
TerminatorKind::Drop { place: dest_field, target: unwind, unwind: None },
501+
true,
502+
);
503+
unwind = next_unwind;
504+
}
505+
// If all clones succeed then we end up here.
506+
self.block(vec![], TerminatorKind::Goto { target }, false);
507+
unwind
508+
}
480509

481-
// BB #(2i)
482-
// `dest.i = Clone::clone(&src.i);`
483-
// Goto #(2i + 2) if ok, #(2i + 1) if unwinding happens.
484-
self.make_clone_call(dest_field, src_field, ity, next_block, cleanup_block);
485-
486-
// BB #(2i + 1) (cleanup)
487-
if let Some((previous_field, previous_cleanup)) = previous_field.take() {
488-
// Drop previous field and goto previous cleanup block.
489-
self.block(
490-
vec![],
491-
TerminatorKind::Drop {
492-
place: previous_field,
493-
target: previous_cleanup,
494-
unwind: None,
495-
},
496-
true,
497-
);
498-
} else {
499-
// Nothing to drop, just resume.
500-
self.block(vec![], TerminatorKind::Resume, true);
501-
}
510+
fn tuple_like_shim<I>(&mut self, dest: Place<'tcx>, src: Place<'tcx>, tys: I)
511+
where
512+
I: IntoIterator<Item = Ty<'tcx>>,
513+
{
514+
self.block(vec![], TerminatorKind::Goto { target: self.block_index_offset(3) }, false);
515+
let unwind = self.block(vec![], TerminatorKind::Resume, true);
516+
let target = self.block(vec![], TerminatorKind::Return, false);
502517

503-
previous_field = Some((dest_field, cleanup_block));
504-
}
518+
let _final_cleanup_block = self.clone_fields(dest, src, target, unwind, tys);
519+
}
505520

506-
self.block(vec![], TerminatorKind::Return, false);
521+
fn generator_shim(
522+
&mut self,
523+
dest: Place<'tcx>,
524+
src: Place<'tcx>,
525+
gen_def_id: DefId,
526+
substs: GeneratorSubsts<'tcx>,
527+
) {
528+
self.block(vec![], TerminatorKind::Goto { target: self.block_index_offset(3) }, false);
529+
let unwind = self.block(vec![], TerminatorKind::Resume, true);
530+
// This will get overwritten with a switch once we know the target blocks
531+
let switch = self.block(vec![], TerminatorKind::Unreachable, false);
532+
let unwind = self.clone_fields(dest, src, switch, unwind, substs.upvar_tys());
533+
let target = self.block(vec![], TerminatorKind::Return, false);
534+
let unreachable = self.block(vec![], TerminatorKind::Unreachable, false);
535+
let mut cases = Vec::with_capacity(substs.state_tys(gen_def_id, self.tcx).count());
536+
for (index, state_tys) in substs.state_tys(gen_def_id, self.tcx).enumerate() {
537+
let variant_index = VariantIdx::new(index);
538+
let dest = self.tcx.mk_place_downcast_unnamed(dest, variant_index);
539+
let src = self.tcx.mk_place_downcast_unnamed(src, variant_index);
540+
let clone_block = self.block_index_offset(1);
541+
let start_block = self.block(
542+
vec![self.make_statement(StatementKind::SetDiscriminant {
543+
place: Box::new(Place::return_place()),
544+
variant_index,
545+
})],
546+
TerminatorKind::Goto { target: clone_block },
547+
false,
548+
);
549+
cases.push((index as u128, start_block));
550+
let _final_cleanup_block = self.clone_fields(dest, src, target, unwind, state_tys);
551+
}
552+
let discr_ty = substs.discr_ty(self.tcx);
553+
let temp = self.make_place(Mutability::Mut, discr_ty);
554+
let rvalue = Rvalue::Discriminant(src);
555+
let statement = self.make_statement(StatementKind::Assign(Box::new((temp, rvalue))));
556+
match &mut self.blocks[switch] {
557+
BasicBlockData { statements, terminator: Some(Terminator { kind, .. }), .. } => {
558+
statements.push(statement);
559+
*kind = TerminatorKind::SwitchInt {
560+
discr: Operand::Move(temp),
561+
switch_ty: discr_ty,
562+
targets: SwitchTargets::new(cases.into_iter(), unreachable),
563+
};
564+
}
565+
BasicBlockData { terminator: None, .. } => unreachable!(),
566+
}
507567
}
508568
}
509569

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,7 @@ symbols! {
763763
gen_future,
764764
gen_kill,
765765
generator,
766+
generator_clone,
766767
generator_state,
767768
generators,
768769
generic_arg_infer,

compiler/rustc_trait_selection/src/traits/select/mod.rs

+38-2
Original file line numberDiff line numberDiff line change
@@ -1928,8 +1928,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
19281928
ty::Dynamic(..)
19291929
| ty::Str
19301930
| ty::Slice(..)
1931-
| ty::Generator(..)
1932-
| ty::GeneratorWitness(..)
1931+
| ty::Generator(_, _, hir::Movability::Static)
19331932
| ty::Foreign(..)
19341933
| ty::Ref(_, _, hir::Mutability::Mut) => None,
19351934

@@ -1938,6 +1937,43 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
19381937
Where(obligation.predicate.rebind(tys.iter().collect()))
19391938
}
19401939

1940+
ty::Generator(_, substs, hir::Movability::Movable) => {
1941+
if self.tcx().features().generator_clone {
1942+
let resolved_upvars =
1943+
self.infcx.shallow_resolve(substs.as_generator().tupled_upvars_ty());
1944+
let resolved_witness =
1945+
self.infcx.shallow_resolve(substs.as_generator().witness());
1946+
if resolved_upvars.is_ty_var() || resolved_witness.is_ty_var() {
1947+
// Not yet resolved.
1948+
Ambiguous
1949+
} else {
1950+
let all = substs
1951+
.as_generator()
1952+
.upvar_tys()
1953+
.chain(iter::once(substs.as_generator().witness()))
1954+
.collect::<Vec<_>>();
1955+
Where(obligation.predicate.rebind(all))
1956+
}
1957+
} else {
1958+
None
1959+
}
1960+
}
1961+
1962+
ty::GeneratorWitness(binder) => {
1963+
let witness_tys = binder.skip_binder();
1964+
for witness_ty in witness_tys.iter() {
1965+
let resolved = self.infcx.shallow_resolve(witness_ty);
1966+
if resolved.is_ty_var() {
1967+
return Ambiguous;
1968+
}
1969+
}
1970+
// (*) binder moved here
1971+
let all_vars = self.tcx().mk_bound_variable_kinds(
1972+
obligation.predicate.bound_vars().iter().chain(binder.bound_vars().iter()),
1973+
);
1974+
Where(ty::Binder::bind_with_vars(witness_tys.to_vec(), all_vars))
1975+
}
1976+
19411977
ty::Closure(_, substs) => {
19421978
// (*) binder moved here
19431979
let ty = self.infcx.shallow_resolve(substs.as_closure().tupled_upvars_ty());

compiler/rustc_ty_utils/src/instance.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,10 @@ fn resolve_associated_item<'tcx>(
263263
let is_copy = self_ty.is_copy_modulo_regions(tcx.at(DUMMY_SP), param_env);
264264
match self_ty.kind() {
265265
_ if is_copy => (),
266-
ty::Closure(..) | ty::Tuple(..) => {}
266+
ty::Generator(..)
267+
| ty::GeneratorWitness(..)
268+
| ty::Closure(..)
269+
| ty::Tuple(..) => {}
267270
_ => return Ok(None),
268271
};
269272

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// edition:2021
2+
// gate-test-generator_clone
3+
// Verifies that feature(generator_clone) doesn't allow async blocks to be cloned/copied.
4+
5+
#![feature(generators, generator_clone)]
6+
7+
use std::future::ready;
8+
9+
struct NonClone;
10+
11+
fn main() {
12+
let inner_non_clone = async {
13+
let non_clone = NonClone;
14+
let () = ready(()).await;
15+
drop(non_clone);
16+
};
17+
check_copy(&inner_non_clone);
18+
//~^ ERROR the trait bound `impl Future<Output = ()>: Copy` is not satisfied
19+
check_clone(&inner_non_clone);
20+
//~^ ERROR the trait bound `impl Future<Output = ()>: Clone` is not satisfied
21+
22+
let non_clone = NonClone;
23+
let outer_non_clone = async move {
24+
drop(non_clone);
25+
};
26+
check_copy(&outer_non_clone);
27+
//~^ ERROR the trait bound `impl Future<Output = ()>: Copy` is not satisfied
28+
check_clone(&outer_non_clone);
29+
//~^ ERROR the trait bound `impl Future<Output = ()>: Clone` is not satisfied
30+
31+
let maybe_copy_clone = async move {};
32+
check_copy(&maybe_copy_clone);
33+
//~^ ERROR the trait bound `impl Future<Output = ()>: Copy` is not satisfied
34+
check_clone(&maybe_copy_clone);
35+
//~^ ERROR the trait bound `impl Future<Output = ()>: Clone` is not satisfied
36+
37+
let inner_non_clone_fn = the_inner_non_clone_fn();
38+
check_copy(&inner_non_clone_fn);
39+
//~^ ERROR the trait bound `impl Future<Output = ()>: Copy` is not satisfied
40+
check_clone(&inner_non_clone_fn);
41+
//~^ ERROR the trait bound `impl Future<Output = ()>: Clone` is not satisfied
42+
43+
let outer_non_clone_fn = the_outer_non_clone_fn(NonClone);
44+
check_copy(&outer_non_clone_fn);
45+
//~^ ERROR the trait bound `impl Future<Output = ()>: Copy` is not satisfied
46+
check_clone(&outer_non_clone_fn);
47+
//~^ ERROR the trait bound `impl Future<Output = ()>: Clone` is not satisfied
48+
49+
let maybe_copy_clone_fn = the_maybe_copy_clone_fn();
50+
check_copy(&maybe_copy_clone_fn);
51+
//~^ ERROR the trait bound `impl Future<Output = ()>: Copy` is not satisfied
52+
check_clone(&maybe_copy_clone_fn);
53+
//~^ ERROR the trait bound `impl Future<Output = ()>: Clone` is not satisfied
54+
}
55+
56+
async fn the_inner_non_clone_fn() {
57+
let non_clone = NonClone;
58+
let () = ready(()).await;
59+
drop(non_clone);
60+
}
61+
62+
async fn the_outer_non_clone_fn(non_clone: NonClone) {
63+
let () = ready(()).await;
64+
drop(non_clone);
65+
}
66+
67+
async fn the_maybe_copy_clone_fn() {
68+
}
69+
70+
fn check_copy<T: Copy>(_x: &T) {}
71+
fn check_clone<T: Clone>(_x: &T) {}

0 commit comments

Comments
 (0)