about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-10-22 15:49:00 +0000
committerbors <bors@rust-lang.org>2022-10-22 15:49:00 +0000
commit19efa0b1103287ad521c0036c065ea2ebbe5939d (patch)
tree0b1b89352d03cd3b7715d871f2dff0d3fc6fd990
parentd3b7e94d0a443a4df13eb6aecc724d36ae80a29d (diff)
parent6459d7f817ddb91fa2bdc4de8ff61e7905177686 (diff)
downloadrust-19efa0b1103287ad521c0036c065ea2ebbe5939d.tar.gz
rust-19efa0b1103287ad521c0036c065ea2ebbe5939d.zip
Auto merge of #13463 - lowr:fix/builtin-derive-with-const-generics, r=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.
-rw-r--r--crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs36
-rw-r--r--crates/hir-expand/src/builtin_derive_macro.rs86
2 files changed, 62 insertions, 60 deletions
diff --git a/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
index 6819e9114a0..fafcde25ae7 100644
--- a/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
+++ b/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
@@ -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< > {}"#]],
     );
 }
 
@@ -33,7 +33,7 @@ macro Copy {}
 #[derive(Copy)]
 struct Foo;
 "#,
-        expect![[r##"
+        expect![[r#"
 #[rustc_builtin_macro]
 macro derive {}
 #[rustc_builtin_macro]
@@ -41,7 +41,7 @@ macro Copy {}
 #[derive(Copy)]
 struct Foo;
 
-impl < > crate ::marker::Copy for Foo< > {}"##]],
+impl < > crate ::marker::Copy for Foo< > {}"#]],
     );
 }
 
@@ -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, > {}"#]],
     );
 }
 
@@ -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, > {}"#]],
     );
 }
 
@@ -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, > {}"#]],
     );
 }
diff --git a/crates/hir-expand/src/builtin_derive_macro.rs b/crates/hir-expand/src/builtin_derive_macro.rs
index 79989bc2e38..8966047c9b2 100644
--- a/crates/hir-expand/src/builtin_derive_macro.rs
+++ b/crates/hir-expand/src/builtin_derive_macro.rs
@@ -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> {
@@ -92,50 +93,22 @@ 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> {
@@ -143,14 +116,27 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu
         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)
 }