diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index c952fc0a866f8..10797fd889cda 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -2,8 +2,9 @@ use crate::deriving::generic::ty::*; use crate::deriving::generic::*; use crate::deriving::path_std; +use rustc_ast as ast; use rustc_ast::ptr::P; -use rustc_ast::{self as ast, Expr, GenericArg, Generics, ItemKind, MetaItem, VariantData}; +use rustc_ast::{Expr, GenericArg, Generics, ItemKind, MetaItem, VariantData}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; @@ -30,7 +31,8 @@ pub fn expand_deriving_clone( // for deriving, Clone alone is not enough. // Wherever Clone is implemented for fields is irrelevant so we don't assert it. let bounds; - let substructure; + let substructure_clone; + let substructure_clone_from; let is_shallow; match *item { Annotatable::Item(ref annitem) => match annitem.kind { @@ -45,28 +47,45 @@ pub fn expand_deriving_clone( { bounds = vec![]; is_shallow = true; - substructure = combine_substructure(Box::new(|c, s, sub| { + substructure_clone = combine_substructure(Box::new(|c, s, sub| { cs_clone_shallow("Clone", c, s, sub, false) })); + // There is no point in implementing `Clone::clone_from` for `Copy` types + // because they don't own resources to preserve. + // Default implementation would suffice and this would save compilation time a little. + substructure_clone_from = None; } else { bounds = vec![]; is_shallow = false; - substructure = + substructure_clone = combine_substructure(Box::new(|c, s, sub| cs_clone("Clone", c, s, sub))); + if is_type_without_fields(item) { + // It clones field by field + // so there is no point to generate it if there aren't any. + substructure_clone_from = None; + } else { + substructure_clone_from = + Some(combine_substructure(Box::new(|c, s, sub| { + cs_clone_from("Clone", c, s, sub) + }))) + } } } ItemKind::Union(..) => { bounds = vec![Literal(path_std!(marker::Copy))]; is_shallow = true; - substructure = combine_substructure(Box::new(|c, s, sub| { + substructure_clone = combine_substructure(Box::new(|c, s, sub| { cs_clone_shallow("Clone", c, s, sub, true) })); + // Same reasoning as with `is_shallow`. + substructure_clone_from = None; } _ => { bounds = vec![]; is_shallow = false; - substructure = + substructure_clone = combine_substructure(Box::new(|c, s, sub| cs_clone("Clone", c, s, sub))); + substructure_clone_from = None; } }, @@ -74,7 +93,34 @@ pub fn expand_deriving_clone( } let inline = cx.meta_word(span, sym::inline); - let attrs = vec![cx.attribute(inline)]; + let attrs = [cx.attribute(inline)]; + + let mut methods = Vec::with_capacity(2); + methods.push(MethodDef { + name: sym::clone, + generics: Bounds::empty(), + explicit_self: borrowed_explicit_self(), + args: Vec::new(), + ret_ty: Self_, + attributes: attrs.to_vec(), + is_unsafe: false, + unify_fieldless_variants: false, + combine_substructure: substructure_clone, + }); + if let Some(substructure_clone_from) = substructure_clone_from { + methods.push(MethodDef { + name: sym::clone_from, + generics: Bounds::empty(), + explicit_self: mutable_explicit_self(), + args: vec![(borrowed_self(), sym::other)], + ret_ty: nil_ty(), + attributes: attrs.to_vec(), + is_unsafe: false, + unify_fieldless_variants: true, + combine_substructure: substructure_clone_from, + }) + } + let trait_def = TraitDef { span, attributes: Vec::new(), @@ -83,17 +129,7 @@ pub fn expand_deriving_clone( generics: Bounds::empty(), is_unsafe: false, supports_unions: true, - methods: vec![MethodDef { - name: sym::clone, - generics: Bounds::empty(), - explicit_self: borrowed_explicit_self(), - args: Vec::new(), - ret_ty: Self_, - attributes: attrs, - is_unsafe: false, - unify_fieldless_variants: false, - combine_substructure: substructure, - }], + methods, associated_types: Vec::new(), }; @@ -157,6 +193,10 @@ fn cs_clone_shallow( cx.expr_block(cx.block(trait_span, stmts)) } +fn clone_fn_full_path(cx: &ExtCtxt<'_>) -> Vec { + cx.std_path(&[sym::clone, sym::Clone, sym::clone]) +} + fn cs_clone( name: &str, cx: &mut ExtCtxt<'_>, @@ -165,7 +205,7 @@ fn cs_clone( ) -> P { let ctor_path; let all_fields; - let fn_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone]); + let fn_path = clone_fn_full_path(cx); let subcall = |cx: &mut ExtCtxt<'_>, field: &FieldInfo<'_>| { let args = vec![cx.expr_addr_of(field.span, field.self_.clone())]; cx.expr_call_global(field.span, fn_path.clone(), args) @@ -217,3 +257,56 @@ fn cs_clone( VariantData::Unit(..) => cx.expr_path(ctor_path), } } + +fn cs_clone_from( + name: &str, + cx: &mut ExtCtxt<'_>, + trait_span: Span, + substr: &Substructure<'_>, +) -> P { + let all_fields = match *substr.fields { + Struct(.., ref af) => af, + EnumMatching(.., ref af) => af, + EnumNonMatchingCollapsed(..) => { + // Cannot do something smart here. + // so emit `*self = other.clone();` + + let [self_, other] = &substr.self_args[..] else{ + cx.span_bug(trait_span, &format!("not exactly 2 arguments in `clone_from` in `derive({})`", name)) + }; + let self_ = self_.clone(); + let clone_call = cx.expr_call_global( + trait_span, + clone_fn_full_path(cx), + vec![cx.expr_addr_of(trait_span, other.clone())], + ); + return cx.expr(trait_span, ast::ExprKind::Assign(self_, clone_call, trait_span)); + } + StaticEnum(..) | StaticStruct(..) => { + cx.span_bug(trait_span, &format!("associated function in `derive({})`", name)) + } + }; + + // Here we know that we have same fields in `&mut self` and in `other` + // so we can call `clone_from` of each of them. + let clone_from_path = cx.std_path(&[sym::clone, sym::Clone, sym::clone_from]); + let fields_clones_from: Vec<_> = all_fields + .iter() + .map(|field| { + if field.other.len() != 1 { + cx.span_bug( + trait_span, + &format!("not exactly 2 arguments in `clone_from` in `derive({})`", name), + ); + } + let self_ = cx.expr_addr_of_mut(field.span, field.self_.clone()); + let other = cx.expr_addr_of(field.span, field.other[0].clone()); + cx.stmt_semi(cx.expr_call_global( + field.span, + clone_from_path.clone(), + vec![self_, other], + )) + }) + .collect(); + cx.expr_block(cx.block(trait_span, fields_clones_from)) +} diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 53369afae278c..02b8128c707a2 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -185,7 +185,6 @@ use rustc_ast::ptr::P; use rustc_ast::{self as ast, BinOpKind, EnumDef, Expr, Generics, PatKind}; use rustc_ast::{GenericArg, GenericParamKind, VariantData}; use rustc_attr as attr; -use rustc_data_structures::map_in_place::MapInPlace; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; @@ -1039,6 +1038,13 @@ impl<'a> MethodDef<'a> { let span = trait_.span; let mut patterns = Vec::new(); for i in 0..self_args.len() { + // Currently supports mutability only for `&mut self` + let mutbl = match (i, &self.explicit_self) { + (0, Some(Some(PtrTy::Borrowed(_, mutbl)))) => *mutbl, + (0, Some(Some(PtrTy::Raw(mutbl)))) => *mutbl, + _ => ast::Mutability::Not, + }; + // We could use `type_ident` instead of `Self`, but in the case of a type parameter // shadowing the struct name, that causes a second, unnecessary E0578 error. #97343 let struct_path = cx.path(span, vec![Ident::new(kw::SelfUpper, type_ident.span)]); @@ -1047,7 +1053,7 @@ impl<'a> MethodDef<'a> { struct_path, struct_def, &format!("__self_{}", i), - ast::Mutability::Not, + mutbl, use_temporaries, ); patterns.push(pat); @@ -1200,7 +1206,7 @@ impl<'a> MethodDef<'a> { trait_: &TraitDef<'b>, enum_def: &'b EnumDef, type_ident: Ident, - mut self_args: Vec>, + self_args: Vec>, nonself_args: &[P], ) -> P { let span = trait_.span; @@ -1240,6 +1246,13 @@ impl<'a> MethodDef<'a> { let first_fieldless = variants.iter().find(|v| v.data.fields().is_empty()); + // Support mutability only for `&mut self` for now. + let self_mutbl = match &self.explicit_self { + Some(Some(PtrTy::Borrowed(_, mutbl))) => *mutbl, + Some(Some(PtrTy::Raw(mutbl))) => *mutbl, + _ => ast::Mutability::Not, + }; + // These arms are of the form: // (Variant1, Variant1, ...) => Body1 // (Variant2, Variant2, ...) => Body2 @@ -1250,28 +1263,29 @@ impl<'a> MethodDef<'a> { .enumerate() .filter(|&(_, v)| !(self.unify_fieldless_variants && v.data.fields().is_empty())) .map(|(index, variant)| { - let mk_self_pat = |cx: &mut ExtCtxt<'_>, self_arg_name: &str| { - let (p, idents) = trait_.create_enum_variant_pattern( - cx, - type_ident, - variant, - self_arg_name, - ast::Mutability::Not, - ); - (cx.pat(span, PatKind::Ref(p, ast::Mutability::Not)), idents) - }; + let mk_self_pat = + |cx: &mut ExtCtxt<'_>, self_arg_name: &str, mutbl: ast::Mutability| { + let (p, idents) = trait_.create_enum_variant_pattern( + cx, + type_ident, + variant, + self_arg_name, + mutbl, + ); + (cx.pat(span, PatKind::Ref(p, mutbl)), idents) + }; // A single arm has form (&VariantK, &VariantK, ...) => BodyK // (see "Final wrinkle" note below for why.) let mut subpats = Vec::with_capacity(self_arg_names.len()); let mut self_pats_idents = Vec::with_capacity(self_arg_names.len() - 1); let first_self_pat_idents = { - let (p, idents) = mk_self_pat(cx, &self_arg_names[0]); + let (p, idents) = mk_self_pat(cx, &self_arg_names[0], self_mutbl); subpats.push(p); idents }; for self_arg_name in &self_arg_names[1..] { - let (p, idents) = mk_self_pat(cx, &self_arg_name); + let (p, idents) = mk_self_pat(cx, &self_arg_name, ast::Mutability::Not); subpats.push(p); self_pats_idents.push(idents); } @@ -1440,7 +1454,17 @@ impl<'a> MethodDef<'a> { // them when they are fed as r-values into a tuple // expression; here add a layer of borrowing, turning // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. - self_args.map_in_place(|self_arg| cx.expr_addr_of(span, self_arg)); + let self_args: Vec<_> = self_args + .into_iter() + .enumerate() + .map(|(i, self_arg)| { + if i == 0 && self_mutbl == ast::Mutability::Mut { + cx.expr_addr_of_mut(span, self_arg) + } else { + cx.expr_addr_of(span, self_arg) + } + }) + .collect(); let match_arg = cx.expr(span, ast::ExprKind::Tup(self_args)); // Lastly we create an expression which branches on all discriminants being equal @@ -1516,7 +1540,17 @@ impl<'a> MethodDef<'a> { // them when they are fed as r-values into a tuple // expression; here add a layer of borrowing, turning // `(*self, *__arg_0, ...)` into `(&*self, &*__arg_0, ...)`. - self_args.map_in_place(|self_arg| cx.expr_addr_of(span, self_arg)); + let self_args: Vec<_> = self_args + .into_iter() + .enumerate() + .map(|(i, self_arg)| { + if i == 0 && self_mutbl == ast::Mutability::Mut { + cx.expr_addr_of_mut(span, self_arg) + } else { + cx.expr_addr_of(span, self_arg) + } + }) + .collect(); let match_arg = cx.expr(span, ast::ExprKind::Tup(self_args)); cx.expr_match(span, match_arg, match_arms) } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs index 7a41800325084..aad9d21c32f11 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/ty.rs @@ -108,10 +108,16 @@ pub fn borrowed(ty: Box) -> Ty { Ptr(ty, borrowed_ptrty()) } +/// `&self` argument pub fn borrowed_explicit_self() -> Option> { Some(Some(borrowed_ptrty())) } +/// `&mut self` argument +pub fn mutable_explicit_self() -> Option> { + Some(Some(PtrTy::Borrowed(None, ast::Mutability::Mut))) +} + pub fn borrowed_self() -> Ty { borrowed(Box::new(Self_)) } diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index e73c31c98fe32..4a74eed5e6957 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -144,6 +144,10 @@ impl<'a> ExtCtxt<'a> { ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Expr(expr) } } + pub fn stmt_semi(&self, expr: P) -> ast::Stmt { + ast::Stmt { id: ast::DUMMY_NODE_ID, span: expr.span, kind: ast::StmtKind::Semi(expr) } + } + pub fn stmt_let(&self, sp: Span, mutbl: bool, ident: Ident, ex: P) -> ast::Stmt { let pat = if mutbl { let binding_mode = ast::BindingMode::ByValue(ast::Mutability::Mut); @@ -239,6 +243,10 @@ impl<'a> ExtCtxt<'a> { self.expr(sp, ast::ExprKind::AddrOf(ast::BorrowKind::Ref, ast::Mutability::Not, e)) } + pub fn expr_addr_of_mut(&self, sp: Span, e: P) -> P { + self.expr(sp, ast::ExprKind::AddrOf(ast::BorrowKind::Ref, ast::Mutability::Mut, e)) + } + pub fn expr_call( &self, span: Span,