diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs index 12ef166b8b051..077f934ed97c3 100644 --- a/compiler/rustc_builtin_macros/src/deriving/bounds.rs +++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs @@ -20,6 +20,7 @@ pub fn expand_deriving_copy( additional_bounds: Vec::new(), generics: Bounds::empty(), is_unsafe: false, + is_const: false, supports_unions: true, methods: Vec::new(), associated_types: Vec::new(), diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index 2e5ad66c60bb8..744fc187907f3 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -15,7 +15,7 @@ pub fn expand_deriving_clone( item: &Annotatable, push: &mut dyn FnMut(Annotatable), ) { - // check if we can use a short form + // check if we can use a short form, and if the impl can be const // // the short form is `fn clone(&self) -> Self { *self }` // @@ -29,6 +29,9 @@ pub fn expand_deriving_clone( // Unions with generic parameters still can derive Clone because they require Copy // for deriving, Clone alone is not enough. // Whever Clone is implemented for fields is irrelevant so we don't assert it. + // + // for now, the impl is only able to be marked as const if we can use the short form; + // in the future, this may be expanded let bounds; let substructure; let is_shallow; @@ -73,15 +76,29 @@ pub fn expand_deriving_clone( _ => cx.span_bug(span, "`#[derive(Clone)]` on trait item or impl item"), } + let features = cx.sess.features_untracked(); let inline = cx.meta_word(span, sym::inline); let attrs = vec![cx.attribute(inline)]; + let is_const = is_shallow && features.const_trait_impl; let trait_def = TraitDef { span, - attributes: Vec::new(), + attributes: if is_const && features.staged_api { + vec![cx.attribute(cx.meta_list( + span, + sym::rustc_const_unstable, + vec![ + cx.meta_name_value(span, sym::feature, Symbol::intern("const_clone")), + cx.meta_name_value(span, sym::issue, Symbol::intern("none")), + ], + ))] + } else { + Vec::new() + }, path: path_std!(clone::Clone), additional_bounds: bounds, generics: Bounds::empty(), is_unsafe: false, + is_const, supports_unions: true, methods: vec![MethodDef { name: sym::clone, diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index 54ab88dc3ffc9..1bdff4b9960e3 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -28,6 +28,7 @@ pub fn expand_deriving_eq( additional_bounds: Vec::new(), generics: Bounds::empty(), is_unsafe: false, + is_const: false, supports_unions: true, methods: vec![MethodDef { name: sym::assert_receiver_is_total_eq, diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs index f84e6e0762012..52ec0ad313dbb 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/ord.rs @@ -24,6 +24,7 @@ pub fn expand_deriving_ord( additional_bounds: Vec::new(), generics: Bounds::empty(), is_unsafe: false, + is_const: false, supports_unions: false, methods: vec![MethodDef { name: sym::cmp, diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs index 8e9f15743cc34..8f8e15af1770a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_eq.rs @@ -104,6 +104,7 @@ pub fn expand_deriving_partial_eq( additional_bounds: Vec::new(), generics: Bounds::empty(), is_unsafe: false, + is_const: false, supports_unions: false, methods, associated_types: Vec::new(), diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index 151a919e0293b..bddc46f1b7a4c 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -47,6 +47,7 @@ pub fn expand_deriving_partial_ord( additional_bounds: vec![], generics: Bounds::empty(), is_unsafe: false, + is_const: false, supports_unions: false, methods: vec![partial_cmp_def], associated_types: Vec::new(), diff --git a/compiler/rustc_builtin_macros/src/deriving/debug.rs b/compiler/rustc_builtin_macros/src/deriving/debug.rs index ecf70da6d96c5..a4d722413a813 100644 --- a/compiler/rustc_builtin_macros/src/deriving/debug.rs +++ b/compiler/rustc_builtin_macros/src/deriving/debug.rs @@ -30,6 +30,7 @@ pub fn expand_deriving_debug( additional_bounds: Vec::new(), generics: Bounds::empty(), is_unsafe: false, + is_const: false, supports_unions: false, methods: vec![MethodDef { name: sym::fmt, diff --git a/compiler/rustc_builtin_macros/src/deriving/decodable.rs b/compiler/rustc_builtin_macros/src/deriving/decodable.rs index 1d892b20729d5..fb76bbb856de4 100644 --- a/compiler/rustc_builtin_macros/src/deriving/decodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/decodable.rs @@ -27,6 +27,7 @@ pub fn expand_deriving_rustc_decodable( additional_bounds: Vec::new(), generics: Bounds::empty(), is_unsafe: false, + is_const: false, supports_unions: false, methods: vec![MethodDef { name: sym::decode, diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index 8c53094b62496..978a49b344f76 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -31,6 +31,7 @@ pub fn expand_deriving_default( additional_bounds: Vec::new(), generics: Bounds::empty(), is_unsafe: false, + is_const: false, supports_unions: false, methods: vec![MethodDef { name: kw::Default, diff --git a/compiler/rustc_builtin_macros/src/deriving/encodable.rs b/compiler/rustc_builtin_macros/src/deriving/encodable.rs index c5f3a9d3379a7..14ffdaf1f2541 100644 --- a/compiler/rustc_builtin_macros/src/deriving/encodable.rs +++ b/compiler/rustc_builtin_macros/src/deriving/encodable.rs @@ -112,6 +112,7 @@ pub fn expand_deriving_rustc_encodable( additional_bounds: Vec::new(), generics: Bounds::empty(), is_unsafe: false, + is_const: false, supports_unions: false, methods: vec![MethodDef { name: sym::encode, diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 985c45e225388..e40cfdb1cc13d 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -215,6 +215,9 @@ pub struct TraitDef<'a> { /// Is it an `unsafe` trait? pub is_unsafe: bool, + /// Is it a `const` impl? + pub is_const: bool, + /// Can this trait be derived for unions? pub supports_unions: bool, @@ -568,7 +571,7 @@ impl<'a> TraitDef<'a> { }); let Generics { mut params, mut where_clause, .. } = - self.generics.to_generics(cx, self.span, type_ident, generics); + self.generics.to_generics(cx, self.span, type_ident, generics, self.is_const); where_clause.span = generics.where_clause.span; let ctxt = self.span.ctxt(); let span = generics.span.with_ctxt(ctxt); @@ -583,10 +586,24 @@ impl<'a> TraitDef<'a> { // extra restrictions on the generics parameters to the // type being derived upon self.additional_bounds.iter().map(|p| { - cx.trait_bound(p.to_path(cx, self.span, type_ident, generics)) + cx.trait_bound( + p.to_path(cx, self.span, type_ident, generics), + if self.is_const { + ast::TraitBoundModifier::MaybeConst + } else { + ast::TraitBoundModifier::None + }, + ) }).chain( // require the current trait - iter::once(cx.trait_bound(trait_path.clone())) + iter::once(cx.trait_bound( + trait_path.clone(), + if self.is_const { + ast::TraitBoundModifier::MaybeConst + } else { + ast::TraitBoundModifier::None + } + )) ).chain( // also add in any bounds from the declaration param.bounds.iter().cloned() @@ -663,11 +680,27 @@ impl<'a> TraitDef<'a> { let mut bounds: Vec<_> = self .additional_bounds .iter() - .map(|p| cx.trait_bound(p.to_path(cx, self.span, type_ident, generics))) + .map(|p| { + cx.trait_bound( + p.to_path(cx, self.span, type_ident, generics), + if self.is_const { + ast::TraitBoundModifier::MaybeConst + } else { + ast::TraitBoundModifier::None + }, + ) + }) .collect(); // require the current trait - bounds.push(cx.trait_bound(trait_path.clone())); + bounds.push(cx.trait_bound( + trait_path.clone(), + if self.is_const { + ast::TraitBoundModifier::MaybeConst + } else { + ast::TraitBoundModifier::None + }, + )); let predicate = ast::WhereBoundPredicate { span: self.span, @@ -723,6 +756,7 @@ impl<'a> TraitDef<'a> { a.extend(self.attributes.iter().cloned()); let unsafety = if self.is_unsafe { ast::Unsafe::Yes(self.span) } else { ast::Unsafe::No }; + let constness = if self.is_const { ast::Const::Yes(self.span) } else { ast::Const::No }; cx.item( self.span, @@ -732,7 +766,7 @@ impl<'a> TraitDef<'a> { unsafety, polarity: ast::ImplPolarity::Positive, defaultness: ast::Defaultness::Final, - constness: ast::Const::No, + constness, generics: trait_generics, of_trait: opt_trait_ref, self_ty: self_type, @@ -933,7 +967,8 @@ impl<'a> MethodDef<'a> { ) -> P { let span = trait_.span; // Create the generics that aren't for `Self`. - let fn_generics = self.generics.to_generics(cx, span, type_ident, generics); + let fn_generics = + self.generics.to_generics(cx, span, type_ident, generics, trait_.is_const); let args = { let self_args = explicit_self.map(|explicit_self| { diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs index 7a41800325084..cf5511f5668ec 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs @@ -200,12 +200,20 @@ fn mk_ty_param( bounds: &[Path], self_ident: Ident, self_generics: &Generics, + is_const: bool, ) -> ast::GenericParam { let bounds = bounds .iter() - .map(|b| { - let path = b.to_path(cx, span, self_ident, self_generics); - cx.trait_bound(path) + .map(|path| { + let path = path.to_path(cx, span, self_ident, self_generics); + cx.trait_bound( + path, + if is_const { + ast::TraitBoundModifier::MaybeConst + } else { + ast::TraitBoundModifier::None + }, + ) }) .collect(); cx.typaram(span, Ident::new(name, span), attrs.to_owned(), bounds, None) @@ -227,13 +235,14 @@ impl Bounds { span: Span, self_ty: Ident, self_generics: &Generics, + is_const: bool, ) -> Generics { let params = self .bounds .iter() .map(|t| { let (name, ref bounds) = *t; - mk_ty_param(cx, span, name, &[], &bounds, self_ty, self_generics) + mk_ty_param(cx, span, name, &[], &bounds, self_ty, self_generics, is_const) }) .collect(); diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs index 7114b98768091..fe912ae6e7c3a 100644 --- a/compiler/rustc_builtin_macros/src/deriving/hash.rs +++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs @@ -27,6 +27,7 @@ pub fn expand_deriving_hash( additional_bounds: Vec::new(), generics: Bounds::empty(), is_unsafe: false, + is_const: false, supports_unions: false, methods: vec![MethodDef { name: sym::hash, diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 2cc15b3e53f46..f8760066edb53 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -128,11 +128,12 @@ impl<'a> ExtCtxt<'a> { } } - pub fn trait_bound(&self, path: ast::Path) -> ast::GenericBound { - ast::GenericBound::Trait( - self.poly_trait_ref(path.span, path), - ast::TraitBoundModifier::None, - ) + pub fn trait_bound( + &self, + path: ast::Path, + modif: ast::TraitBoundModifier, + ) -> ast::GenericBound { + ast::GenericBound::Trait(self.poly_trait_ref(path.span, path), modif) } pub fn lifetime(&self, span: Span, ident: Ident) -> ast::Lifetime { @@ -567,4 +568,12 @@ impl<'a> ExtCtxt<'a> { pub fn meta_word(&self, sp: Span, w: Symbol) -> ast::MetaItem { attr::mk_word_item(Ident::new(w, sp)) } + + pub fn meta_name_value(&self, sp: Span, n: Symbol, v: Symbol) -> ast::NestedMetaItem { + ast::NestedMetaItem::MetaItem(attr::mk_name_value_item_str(Ident::new(n, sp), v, sp)) + } + + pub fn meta_list(&self, sp: Span, w: Symbol, items: Vec) -> ast::MetaItem { + attr::mk_list_item(Ident::new(w, sp), items) + } } diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 6f9579043c37d..96b9b7ffce26f 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -127,7 +127,11 @@ pub trait Clone: Sized { /// allocations. #[inline] #[stable(feature = "rust1", since = "1.0.0")] - fn clone_from(&mut self, source: &Self) { + #[default_method_body_is_const] + fn clone_from(&mut self, source: &Self) + where + Self: ~const Clone + ~const Drop, + { *self = source.clone() } } @@ -178,7 +182,8 @@ mod impls { ($($t:ty)*) => { $( #[stable(feature = "rust1", since = "1.0.0")] - impl Clone for $t { + #[rustc_const_unstable(feature = "const_clone", issue = "none")] + impl const Clone for $t { #[inline] fn clone(&self) -> Self { *self @@ -196,7 +201,8 @@ mod impls { } #[unstable(feature = "never_type", issue = "35121")] - impl Clone for ! { + #[rustc_const_unstable(feature = "const_clone", issue = "none")] + impl const Clone for ! { #[inline] fn clone(&self) -> Self { *self @@ -204,7 +210,8 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl Clone for *const T { + #[rustc_const_unstable(feature = "const_clone", issue = "none")] + impl const Clone for *const T { #[inline] fn clone(&self) -> Self { *self @@ -212,7 +219,8 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl Clone for *mut T { + #[rustc_const_unstable(feature = "const_clone", issue = "none")] + impl const Clone for *mut T { #[inline] fn clone(&self) -> Self { *self @@ -221,7 +229,8 @@ mod impls { /// Shared references can be cloned, but mutable references *cannot*! #[stable(feature = "rust1", since = "1.0.0")] - impl Clone for &T { + #[rustc_const_unstable(feature = "const_clone", issue = "none")] + impl const Clone for &T { #[inline] #[rustc_diagnostic_item = "noop_method_clone"] fn clone(&self) -> Self { diff --git a/src/test/ui/derives/deriving-copyclone.rs b/src/test/ui/derives/deriving-copyclone.rs index 06b3157a77a84..688e95a374d21 100644 --- a/src/test/ui/derives/deriving-copyclone.rs +++ b/src/test/ui/derives/deriving-copyclone.rs @@ -1,22 +1,34 @@ +#![feature(const_trait_impl)] + // this will get a no-op Clone impl #[derive(Copy, Clone)] struct A { a: i32, - b: i64 + b: i64, } // this will get a deep Clone impl #[derive(Copy, Clone)] struct B { a: i32, - b: T + b: T, } struct C; // not Copy or Clone -#[derive(Clone)] struct D; // Clone but not Copy +#[derive(Clone)] +struct D; // Clone but not Copy + +// not const Clone or Copy +struct E(A); +impl Clone for E { + fn clone(&self) -> E { + *self + } +} +impl Copy for E {} fn is_copy(_: T) {} -fn is_clone(_: T) {} +const fn is_clone(_: T) {} fn main() { // A can be copied and cloned @@ -35,3 +47,9 @@ fn main() { is_copy(B { a: 1, b: D }); //~ ERROR Copy is_clone(B { a: 1, b: D }); } + +// A can be cloned in a const context +const _: () = is_clone(A { a: 1, b: 2 }); + +// E can't be cloned in a const context +const _: () = is_clone(E(A { a: 1, b: 2 })); //~ ERROR Clone diff --git a/src/test/ui/derives/deriving-copyclone.stderr b/src/test/ui/derives/deriving-copyclone.stderr index 13097edf0adf9..2a7c3aaf57895 100644 --- a/src/test/ui/derives/deriving-copyclone.stderr +++ b/src/test/ui/derives/deriving-copyclone.stderr @@ -1,5 +1,19 @@ +error[E0277]: the trait bound `E: ~const Clone` is not satisfied + --> $DIR/deriving-copyclone.rs:55:24 + | +LL | const _: () = is_clone(E(A { a: 1, b: 2 })); + | -------- ^^^^^^^^^^^^^^^^^^^ the trait `~const Clone` is not implemented for `E` + | | + | required by a bound introduced by this call + | +note: required by a bound in `is_clone` + --> $DIR/deriving-copyclone.rs:31:22 + | +LL | const fn is_clone(_: T) {} + | ^^^^^^^^^^^^ required by this bound in `is_clone` + error[E0277]: the trait bound `B: Copy` is not satisfied - --> $DIR/deriving-copyclone.rs:31:13 + --> $DIR/deriving-copyclone.rs:43:13 | LL | is_copy(B { a: 1, b: C }); | ------- ^^^^^^^^^^^^^^^^ expected an implementor of trait `Copy` @@ -7,12 +21,12 @@ LL | is_copy(B { a: 1, b: C }); | required by a bound introduced by this call | note: required because of the requirements on the impl of `Copy` for `B` - --> $DIR/deriving-copyclone.rs:9:10 + --> $DIR/deriving-copyclone.rs:11:10 | LL | #[derive(Copy, Clone)] | ^^^^ note: required by a bound in `is_copy` - --> $DIR/deriving-copyclone.rs:18:15 + --> $DIR/deriving-copyclone.rs:30:15 | LL | fn is_copy(_: T) {} | ^^^^ required by this bound in `is_copy` @@ -22,24 +36,24 @@ help: consider borrowing here LL | is_copy(&B { a: 1, b: C }); | + -error[E0277]: the trait bound `B: Clone` is not satisfied - --> $DIR/deriving-copyclone.rs:32:14 +error[E0277]: the trait bound `B: ~const Clone` is not satisfied + --> $DIR/deriving-copyclone.rs:44:14 | LL | is_clone(B { a: 1, b: C }); - | -------- ^^^^^^^^^^^^^^^^ expected an implementor of trait `Clone` + | -------- ^^^^^^^^^^^^^^^^ expected an implementor of trait `~const Clone` | | | required by a bound introduced by this call | note: required because of the requirements on the impl of `Clone` for `B` - --> $DIR/deriving-copyclone.rs:9:16 + --> $DIR/deriving-copyclone.rs:11:16 | LL | #[derive(Copy, Clone)] | ^^^^^ note: required by a bound in `is_clone` - --> $DIR/deriving-copyclone.rs:19:16 + --> $DIR/deriving-copyclone.rs:31:22 | -LL | fn is_clone(_: T) {} - | ^^^^^ required by this bound in `is_clone` +LL | const fn is_clone(_: T) {} + | ^^^^^^^^^^^^ required by this bound in `is_clone` = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider borrowing here | @@ -47,7 +61,7 @@ LL | is_clone(&B { a: 1, b: C }); | + error[E0277]: the trait bound `B: Copy` is not satisfied - --> $DIR/deriving-copyclone.rs:35:13 + --> $DIR/deriving-copyclone.rs:47:13 | LL | is_copy(B { a: 1, b: D }); | ------- ^^^^^^^^^^^^^^^^ expected an implementor of trait `Copy` @@ -55,12 +69,12 @@ LL | is_copy(B { a: 1, b: D }); | required by a bound introduced by this call | note: required because of the requirements on the impl of `Copy` for `B` - --> $DIR/deriving-copyclone.rs:9:10 + --> $DIR/deriving-copyclone.rs:11:10 | LL | #[derive(Copy, Clone)] | ^^^^ note: required by a bound in `is_copy` - --> $DIR/deriving-copyclone.rs:18:15 + --> $DIR/deriving-copyclone.rs:30:15 | LL | fn is_copy(_: T) {} | ^^^^ required by this bound in `is_copy` @@ -70,6 +84,6 @@ help: consider borrowing here LL | is_copy(&B { a: 1, b: D }); | + -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0277`.