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

Replace Copy/Clone compiler magic on arrays with library impls #86041

Merged
merged 3 commits into from
Nov 9, 2021
Merged
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
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);
}
}
bstrie marked this conversation as resolved.
Show resolved Hide resolved

// 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`.