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

[Experiment] [WIP] Add clone_from to clone derive #98445

Closed
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
131 changes: 112 additions & 19 deletions compiler/rustc_builtin_macros/src/deriving/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand All @@ -45,36 +47,80 @@ 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;
}
},

_ => cx.span_bug(span, "`#[derive(Clone)]` on trait item or impl item"),
}

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(),
Expand All @@ -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(),
};

Expand Down Expand Up @@ -157,6 +193,10 @@ fn cs_clone_shallow(
cx.expr_block(cx.block(trait_span, stmts))
}

fn clone_fn_full_path(cx: &ExtCtxt<'_>) -> Vec<Ident> {
cx.std_path(&[sym::clone, sym::Clone, sym::clone])
}

fn cs_clone(
name: &str,
cx: &mut ExtCtxt<'_>,
Expand All @@ -165,7 +205,7 @@ fn cs_clone(
) -> P<Expr> {
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)
Expand Down Expand Up @@ -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<Expr> {
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))
}
68 changes: 51 additions & 17 deletions compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)]);
Expand All @@ -1047,7 +1053,7 @@ impl<'a> MethodDef<'a> {
struct_path,
struct_def,
&format!("__self_{}", i),
ast::Mutability::Not,
mutbl,
use_temporaries,
);
patterns.push(pat);
Expand Down Expand Up @@ -1200,7 +1206,7 @@ impl<'a> MethodDef<'a> {
trait_: &TraitDef<'b>,
enum_def: &'b EnumDef,
type_ident: Ident,
mut self_args: Vec<P<Expr>>,
self_args: Vec<P<Expr>>,
nonself_args: &[P<Expr>],
) -> P<Expr> {
let span = trait_.span;
Expand Down Expand Up @@ -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
Expand All @@ -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);
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_builtin_macros/src/deriving/generic/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,16 @@ pub fn borrowed(ty: Box<Ty>) -> Ty {
Ptr(ty, borrowed_ptrty())
}

/// `&self` argument
pub fn borrowed_explicit_self() -> Option<Option<PtrTy>> {
Some(Some(borrowed_ptrty()))
}

/// `&mut self` argument
pub fn mutable_explicit_self() -> Option<Option<PtrTy>> {
Some(Some(PtrTy::Borrowed(None, ast::Mutability::Mut)))
}

pub fn borrowed_self() -> Ty {
borrowed(Box::new(Self_))
}
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_expand/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::Expr>) -> 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::Expr>) -> ast::Stmt {
let pat = if mutbl {
let binding_mode = ast::BindingMode::ByValue(ast::Mutability::Mut);
Expand Down Expand Up @@ -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<ast::Expr>) -> P<ast::Expr> {
self.expr(sp, ast::ExprKind::AddrOf(ast::BorrowKind::Ref, ast::Mutability::Mut, e))
}

pub fn expr_call(
&self,
span: Span,
Expand Down