Skip to content

Commit

Permalink
Auto merge of #13463 - lowr:fix/builtin-derive-with-const-generics, r…
Browse files Browse the repository at this point in the history
…=Veykril

Support const generics for builtin derive macro

Fixes #13121

We have been treating every generic parameter as type parameter during builtin derive macro expansion. This patch adds support for const generics in such expansions.
  • Loading branch information
bors committed Oct 22, 2022
2 parents d3b7e94 + 6459d7f commit 19efa0b
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 60 deletions.
36 changes: 26 additions & 10 deletions crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ fn test_copy_expand_simple() {
#[derive(Copy)]
struct Foo;
"#,
expect![[r##"
expect![[r#"
#[derive(Copy)]
struct Foo;
impl < > core::marker::Copy for Foo< > {}"##]],
impl < > core::marker::Copy for Foo< > {}"#]],
);
}

Expand All @@ -33,15 +33,15 @@ macro Copy {}
#[derive(Copy)]
struct Foo;
"#,
expect![[r##"
expect![[r#"
#[rustc_builtin_macro]
macro derive {}
#[rustc_builtin_macro]
macro Copy {}
#[derive(Copy)]
struct Foo;
impl < > crate ::marker::Copy for Foo< > {}"##]],
impl < > crate ::marker::Copy for Foo< > {}"#]],
);
}

Expand All @@ -53,11 +53,11 @@ fn test_copy_expand_with_type_params() {
#[derive(Copy)]
struct Foo<A, B>;
"#,
expect![[r##"
expect![[r#"
#[derive(Copy)]
struct Foo<A, B>;
impl <T0: core::marker::Copy, T1: core::marker::Copy> core::marker::Copy for Foo<T0, T1> {}"##]],
impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
);
}

Expand All @@ -70,11 +70,11 @@ fn test_copy_expand_with_lifetimes() {
#[derive(Copy)]
struct Foo<A, B, 'a, 'b>;
"#,
expect![[r##"
expect![[r#"
#[derive(Copy)]
struct Foo<A, B, 'a, 'b>;
impl <T0: core::marker::Copy, T1: core::marker::Copy> core::marker::Copy for Foo<T0, T1> {}"##]],
impl <T0: core::marker::Copy, T1: core::marker::Copy, > core::marker::Copy for Foo<T0, T1, > {}"#]],
);
}

Expand All @@ -86,10 +86,26 @@ fn test_clone_expand() {
#[derive(Clone)]
struct Foo<A, B>;
"#,
expect![[r##"
expect![[r#"
#[derive(Clone)]
struct Foo<A, B>;
impl <T0: core::clone::Clone, T1: core::clone::Clone> core::clone::Clone for Foo<T0, T1> {}"##]],
impl <T0: core::clone::Clone, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
);
}

#[test]
fn test_clone_expand_with_const_generics() {
check(
r#"
//- minicore: derive, clone
#[derive(Clone)]
struct Foo<const X: usize, T>(u32);
"#,
expect![[r#"
#[derive(Clone)]
struct Foo<const X: usize, T>(u32);
impl <const T0: usize, T1: core::clone::Clone, > core::clone::Clone for Foo<T0, T1, > {}"#]],
);
}
86 changes: 36 additions & 50 deletions crates/hir-expand/src/builtin_derive_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option<BuiltinDeriveExpander>

struct BasicAdtInfo {
name: tt::Ident,
type_or_const_params: usize,
/// `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param.
param_types: Vec<Option<tt::Subtree>>,
}

fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
Expand Down Expand Up @@ -92,65 +93,50 @@ fn parse_adt(tt: &tt::Subtree) -> Result<BasicAdtInfo, ExpandError> {
let name_token_id =
token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified);
let name_token = tt::Ident { id: name_token_id, text: name.text().into() };
let type_or_const_params =
params.map_or(0, |type_param_list| type_param_list.type_or_const_params().count());
Ok(BasicAdtInfo { name: name_token, type_or_const_params })
}

fn make_type_args(n: usize, bound: Vec<tt::TokenTree>) -> Vec<tt::TokenTree> {
let mut result = Vec::<tt::TokenTree>::with_capacity(n * 2);
result.push(
tt::Leaf::Punct(tt::Punct {
char: '<',
spacing: tt::Spacing::Alone,
id: tt::TokenId::unspecified(),
})
.into(),
);
for i in 0..n {
if i > 0 {
result.push(
tt::Leaf::Punct(tt::Punct {
char: ',',
spacing: tt::Spacing::Alone,
id: tt::TokenId::unspecified(),
})
.into(),
);
}
result.push(
tt::Leaf::Ident(tt::Ident {
id: tt::TokenId::unspecified(),
text: format!("T{}", i).into(),
})
.into(),
);
result.extend(bound.iter().cloned());
}
result.push(
tt::Leaf::Punct(tt::Punct {
char: '>',
spacing: tt::Spacing::Alone,
id: tt::TokenId::unspecified(),
let param_types = params
.into_iter()
.flat_map(|param_list| param_list.type_or_const_params())
.map(|param| {
if let ast::TypeOrConstParam::Const(param) = param {
let ty = param
.ty()
.map(|ty| mbe::syntax_node_to_token_tree(ty.syntax()).0)
.unwrap_or_default();
Some(ty)
} else {
None
}
})
.into(),
);
result
.collect();
Ok(BasicAdtInfo { name: name_token, param_types })
}

fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult<tt::Subtree> {
let info = match parse_adt(tt) {
Ok(info) => info,
Err(e) => return ExpandResult::only_err(e),
};
let (params, args): (Vec<_>, Vec<_>) = info
.param_types
.into_iter()
.enumerate()
.map(|(idx, param_ty)| {
let ident = tt::Leaf::Ident(tt::Ident {
id: tt::TokenId::unspecified(),
text: format!("T{idx}").into(),
});
let ident_ = ident.clone();
if let Some(ty) = param_ty {
(quote! { const #ident : #ty , }, quote! { #ident_ , })
} else {
let bound = trait_path.clone();
(quote! { #ident : #bound , }, quote! { #ident_ , })
}
})
.unzip();
let name = info.name;
let trait_path_clone = trait_path.token_trees.clone();
let bound = (quote! { : ##trait_path_clone }).token_trees;
let type_params = make_type_args(info.type_or_const_params, bound);
let type_args = make_type_args(info.type_or_const_params, Vec::new());
let trait_path = trait_path.token_trees;
let expanded = quote! {
impl ##type_params ##trait_path for #name ##type_args {}
impl < ##params > #trait_path for #name < ##args > {}
};
ExpandResult::ok(expanded)
}
Expand Down

0 comments on commit 19efa0b

Please sign in to comment.