Skip to content

Commit

Permalink
Auto merge of #86041 - bstrie:unmagic-array-copy, r=jackh726
Browse files Browse the repository at this point in the history
Replace Copy/Clone compiler magic on arrays with library impls

With const generics the compiler no longer needs to fake these impls.
  • Loading branch information
bors committed Nov 9, 2021
2 parents 07acdb4 + 61b1394 commit d608229
Show file tree
Hide file tree
Showing 12 changed files with 35 additions and 195 deletions.
9 changes: 3 additions & 6 deletions compiler/rustc_error_codes/src/error_codes/E0206.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,12 @@ enum.
Erroneous code example:

```compile_fail,E0206
type Foo = [u8; 256];
impl Copy for Foo { } // error!
#[derive(Copy, Clone)]
struct Bar;
impl Copy for &'static mut Bar { } // error!
```

You can only implement `Copy` for a struct or an enum. Both of the previous
examples will fail, because neither `[u8; 256]` nor `&'static mut Bar`
(mutable reference to `Bar`) is a struct or enum.
You can only implement `Copy` for a struct or an enum.
The previous example will fail because `&'static mut Bar`
is not a struct or enum.
149 changes: 0 additions & 149 deletions compiler/rustc_mir_transform/src/shim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,6 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -

match self_ty.kind() {
_ if is_copy => builder.copy_shim(),
ty::Array(ty, len) => builder.array_shim(dest, src, ty, len),
ty::Closure(_, substs) => {
builder.tuple_like_shim(dest, src, substs.as_closure().upvar_tys())
}
Expand Down Expand Up @@ -459,154 +458,6 @@ impl CloneShimBuilder<'tcx> {
);
}

fn loop_header(
&mut self,
beg: Place<'tcx>,
end: Place<'tcx>,
loop_body: BasicBlock,
loop_end: BasicBlock,
is_cleanup: bool,
) {
let tcx = self.tcx;

let cond = self.make_place(Mutability::Mut, tcx.types.bool);
let compute_cond = self.make_statement(StatementKind::Assign(Box::new((
cond,
Rvalue::BinaryOp(BinOp::Ne, Box::new((Operand::Copy(end), Operand::Copy(beg)))),
))));

// `if end != beg { goto loop_body; } else { goto loop_end; }`
self.block(
vec![compute_cond],
TerminatorKind::if_(tcx, Operand::Move(cond), loop_body, loop_end),
is_cleanup,
);
}

fn make_usize(&self, value: u64) -> Box<Constant<'tcx>> {
Box::new(Constant {
span: self.span,
user_ty: None,
literal: ty::Const::from_usize(self.tcx, value).into(),
})
}

fn array_shim(
&mut self,
dest: Place<'tcx>,
src: Place<'tcx>,
ty: Ty<'tcx>,
len: &'tcx ty::Const<'tcx>,
) {
let tcx = self.tcx;
let span = self.span;

let beg = self.local_decls.push(LocalDecl::new(tcx.types.usize, span));
let end = self.make_place(Mutability::Not, tcx.types.usize);

// BB #0
// `let mut beg = 0;`
// `let end = len;`
// `goto #1;`
let inits = vec![
self.make_statement(StatementKind::Assign(Box::new((
Place::from(beg),
Rvalue::Use(Operand::Constant(self.make_usize(0))),
)))),
self.make_statement(StatementKind::Assign(Box::new((
end,
Rvalue::Use(Operand::Constant(Box::new(Constant {
span: self.span,
user_ty: None,
literal: len.into(),
}))),
)))),
];
self.block(inits, TerminatorKind::Goto { target: BasicBlock::new(1) }, false);

// BB #1: loop {
// BB #2;
// BB #3;
// }
// BB #4;
self.loop_header(Place::from(beg), end, BasicBlock::new(2), BasicBlock::new(4), false);

// BB #2
// `dest[i] = Clone::clone(src[beg])`;
// Goto #3 if ok, #5 if unwinding happens.
let dest_field = self.tcx.mk_place_index(dest, beg);
let src_field = self.tcx.mk_place_index(src, beg);
self.make_clone_call(dest_field, src_field, ty, BasicBlock::new(3), BasicBlock::new(5));

// BB #3
// `beg = beg + 1;`
// `goto #1`;
let statements = vec![self.make_statement(StatementKind::Assign(Box::new((
Place::from(beg),
Rvalue::BinaryOp(
BinOp::Add,
Box::new((Operand::Copy(Place::from(beg)), Operand::Constant(self.make_usize(1)))),
),
))))];
self.block(statements, TerminatorKind::Goto { target: BasicBlock::new(1) }, false);

// BB #4
// `return dest;`
self.block(vec![], TerminatorKind::Return, false);

// BB #5 (cleanup)
// `let end = beg;`
// `let mut beg = 0;`
// goto #6;
let end = beg;
let beg = self.local_decls.push(LocalDecl::new(tcx.types.usize, span));
let init = self.make_statement(StatementKind::Assign(Box::new((
Place::from(beg),
Rvalue::Use(Operand::Constant(self.make_usize(0))),
))));
self.block(vec![init], TerminatorKind::Goto { target: BasicBlock::new(6) }, true);

// BB #6 (cleanup): loop {
// BB #7;
// BB #8;
// }
// BB #9;
self.loop_header(
Place::from(beg),
Place::from(end),
BasicBlock::new(7),
BasicBlock::new(9),
true,
);

// BB #7 (cleanup)
// `drop(dest[beg])`;
self.block(
vec![],
TerminatorKind::Drop {
place: self.tcx.mk_place_index(dest, beg),
target: BasicBlock::new(8),
unwind: None,
},
true,
);

// BB #8 (cleanup)
// `beg = beg + 1;`
// `goto #6;`
let statement = self.make_statement(StatementKind::Assign(Box::new((
Place::from(beg),
Rvalue::BinaryOp(
BinOp::Add,
Box::new((Operand::Copy(Place::from(beg)), Operand::Constant(self.make_usize(1)))),
),
))));
self.block(vec![statement], TerminatorKind::Goto { target: BasicBlock::new(6) }, true);

// BB #9 (resume)
self.block(vec![], TerminatorKind::Resume, true);
}

fn tuple_like_shim<I>(&mut self, dest: Place<'tcx>, src: Place<'tcx>, tys: I)
where
I: Iterator<Item = Ty<'tcx>>,
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_trait_selection/src/traits/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ pub fn can_type_implement_copy(
| ty::Char
| ty::RawPtr(..)
| ty::Never
| ty::Ref(_, _, hir::Mutability::Not) => return Ok(()),
| ty::Ref(_, _, hir::Mutability::Not)
| ty::Array(..) => return Ok(()),

ty::Adt(adt, substs) => (adt, substs),

Expand Down
8 changes: 2 additions & 6 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1859,7 +1859,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
| ty::Char
| ty::RawPtr(..)
| ty::Never
| ty::Ref(_, _, hir::Mutability::Not) => {
| ty::Ref(_, _, hir::Mutability::Not)
| ty::Array(..) => {
// Implementations provided in libcore
None
}
Expand All @@ -1872,11 +1873,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
| ty::Foreign(..)
| ty::Ref(_, _, hir::Mutability::Mut) => None,

ty::Array(element_ty, _) => {
// (*) binder moved here
Where(obligation.predicate.rebind(vec![element_ty]))
}

ty::Tuple(tys) => {
// (*) binder moved here
Where(obligation.predicate.rebind(tys.iter().map(|k| k.expect_ty()).collect()))
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_ty_utils/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ fn resolve_associated_item<'tcx>(
let is_copy = self_ty.is_copy_modulo_regions(tcx.at(DUMMY_SP), param_env);
match self_ty.kind() {
_ if is_copy => (),
ty::Array(..) | ty::Closure(..) | ty::Tuple(..) => {}
ty::Closure(..) | ty::Tuple(..) => {}
_ => return Ok(None),
};

Expand Down
20 changes: 20 additions & 0 deletions library/core/src/array/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,26 @@ impl<T: Ord, const N: usize> Ord for [T; N] {
}
}

#[cfg(not(bootstrap))]
#[stable(feature = "copy_clone_array_lib", since = "1.58.0")]
impl<T: Copy, const N: usize> Copy for [T; N] {}

#[cfg(not(bootstrap))]
#[stable(feature = "copy_clone_array_lib", since = "1.58.0")]
impl<T: Clone, const N: usize> Clone for [T; N] {
#[inline]
fn clone(&self) -> Self {
// SAFETY: we know for certain that this iterator will yield exactly `N`
// items.
unsafe { collect_into_array_unchecked(&mut self.iter().cloned()) }
}

#[inline]
fn clone_from(&mut self, other: &Self) {
self.clone_from_slice(other);
}
}

// The Default impls cannot be done with const generics because `[T; 0]` doesn't
// require Default to be implemented, and having different impl blocks for
// different numbers isn't supported yet.
Expand Down
1 change: 0 additions & 1 deletion library/core/src/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@
///
/// * Function item types (i.e., the distinct types defined for each function)
/// * Function pointer types (e.g., `fn() -> i32`)
/// * Array types, for all sizes, if the item type also implements `Clone` (e.g., `[i32; 123456]`)
/// * Tuple types, if each component also implements `Clone` (e.g., `()`, `(i32, bool)`)
/// * Closure types, if they capture no value from the environment
/// or if all such captured values implement `Clone` themselves.
Expand Down
1 change: 0 additions & 1 deletion library/core/src/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,6 @@ pub trait StructuralEq {
///
/// * Function item types (i.e., the distinct types defined for each function)
/// * Function pointer types (e.g., `fn() -> i32`)
/// * Array types, for all sizes, if the item type also implements `Copy` (e.g., `[i32; 123456]`)
/// * Tuple types, if each component also implements `Copy` (e.g., `()`, `(i32, bool)`)
/// * Closure types, if they capture no value from the environment
/// or if all such captured values implement `Copy` themselves.
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/builtin-clone-unwind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ fn main() {
].clone();
});

assert!(result.is_err());
assert!(child.is_err());
assert_eq!(
1,
Rc::strong_count(&counter)
Expand Down
5 changes: 3 additions & 2 deletions src/test/ui/chalkify/builtin-copy-clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ fn test_copy_clone<T: Copy + Clone>(arg: T) {
fn foo() { }

fn main() {
// FIXME: add closures when they're considered WF
test_copy_clone(foo);
let f: fn() = foo;
test_copy_clone(f);
// FIXME: add closures when they're considered WF
test_copy_clone([1; 56]);
// FIXME(#86252): reinstate array test after chalk upgrade
//test_copy_clone([1; 56]);
test_copy_clone((1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1));
test_copy_clone((1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, true, 'a', 1.1));
test_copy_clone(());
Expand Down
6 changes: 0 additions & 6 deletions src/test/ui/error-codes/E0206.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
type Foo = [u8; 256];

impl Copy for Foo { }
//~^ ERROR the trait `Copy` may not be implemented for this type
//~| ERROR only traits defined in the current crate can be implemented for arbitrary types

#[derive(Copy, Clone)]
struct Bar;

Expand Down
24 changes: 3 additions & 21 deletions src/test/ui/error-codes/E0206.stderr
Original file line number Diff line number Diff line change
@@ -1,27 +1,9 @@
error[E0206]: the trait `Copy` may not be implemented for this type
--> $DIR/E0206.rs:3:15
|
LL | impl Copy for Foo { }
| ^^^ type is not a structure or enumeration

error[E0206]: the trait `Copy` may not be implemented for this type
--> $DIR/E0206.rs:10:15
--> $DIR/E0206.rs:4:15
|
LL | impl Copy for &'static mut Bar { }
| ^^^^^^^^^^^^^^^^ type is not a structure or enumeration

error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> $DIR/E0206.rs:3:1
|
LL | impl Copy for Foo { }
| ^^^^^^^^^^^^^^---
| | |
| | this is not defined in the current crate because arrays are always foreign
| impl doesn't use only types from inside the current crate
|
= note: define and implement a trait or new type instead

error: aborting due to 3 previous errors
error: aborting due to previous error

Some errors have detailed explanations: E0117, E0206.
For more information about an error, try `rustc --explain E0117`.
For more information about this error, try `rustc --explain E0206`.

0 comments on commit d608229

Please sign in to comment.