From d9f4cbbe8f261278cb16ad462b56d5eafbd22bed Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Wed, 3 May 2023 14:14:47 +0330 Subject: [PATCH] Emit function bodies in expanding builtin derives --- .../builtin_derive_macro.rs | 292 ++++++++- crates/hir-expand/src/builtin_derive_macro.rs | 554 +++++++++++++++++- crates/hir-expand/src/quote.rs | 6 + crates/hir-ty/src/consteval/tests.rs | 47 ++ crates/ide/src/expand_macro.rs | 41 +- crates/test-utils/src/minicore.rs | 54 ++ crates/tt/src/lib.rs | 6 + 7 files changed, 962 insertions(+), 38 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 37cf348c92d..9ea688a8c1a 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 @@ -84,13 +84,33 @@ fn test_clone_expand() { r#" //- minicore: derive, clone #[derive(Clone)] -struct Foo; +enum Command { + Move { x: A, y: B }, + Do(&'static str), + Jump, +} "#, expect![[r#" #[derive(Clone)] -struct Foo; +enum Command { + Move { x: A, y: B }, + Do(&'static str), + Jump, +} -impl core::clone::Clone for Foo where {}"#]], +impl core::clone::Clone for Command where { + fn clone(&self ) -> Self { + match self { + Command::Move { + x: x, y: y, + } + =>Command::Move { + x: x.clone(), y: y.clone(), + } + , Command::Do(f0, )=>Command::Do(f0.clone(), ), Command::Jump=>Command::Jump, + } + } +}"#]], ); } @@ -106,6 +126,270 @@ fn test_clone_expand_with_const_generics() { #[derive(Clone)] struct Foo(u32); -impl core::clone::Clone for Foo where {}"#]], +impl core::clone::Clone for Foo where { + fn clone(&self ) -> Self { + match self { + Foo(f0, )=>Foo(f0.clone(), ), + } + } +}"#]], + ); +} + +#[test] +fn test_default_expand() { + check( + r#" +//- minicore: derive, default +#[derive(Default)] +struct Foo { + field1: i32, + field2: (), +} +#[derive(Default)] +enum Bar { + Foo(u8), + #[default] + Bar, +} +"#, + expect![[r#" +#[derive(Default)] +struct Foo { + field1: i32, + field2: (), +} +#[derive(Default)] +enum Bar { + Foo(u8), + #[default] + Bar, +} + +impl < > core::default::Default for Foo< > where { + fn default() -> Self { + Foo { + field1: core::default::Default::default(), field2: core::default::Default::default(), + } + } +} +impl < > core::default::Default for Bar< > where { + fn default() -> Self { + Bar::Bar + } +}"#]], + ); +} + +#[test] +fn test_partial_eq_expand() { + check( + r#" +//- minicore: derive, eq +#[derive(PartialEq, Eq)] +enum Command { + Move { x: i32, y: i32 }, + Do(&'static str), + Jump, +} +"#, + expect![[r#" +#[derive(PartialEq, Eq)] +enum Command { + Move { x: i32, y: i32 }, + Do(&'static str), + Jump, +} + +impl < > core::cmp::PartialEq for Command< > where { + fn eq(&self , other: &Self ) -> bool { + match (self , other) { + (Command::Move { + x: x_self, y: y_self, + } + , Command::Move { + x: x_other, y: y_other, + } + )=>x_self.eq(x_other) && y_self.eq(y_other), (Command::Do(f0_self, ), Command::Do(f0_other, ))=>f0_self.eq(f0_other), (Command::Jump, Command::Jump)=>true , _unused=>false + } + } +} +impl < > core::cmp::Eq for Command< > where {}"#]], + ); +} + +#[test] +fn test_partial_ord_expand() { + check( + r#" +//- minicore: derive, ord +#[derive(PartialOrd, Ord)] +enum Command { + Move { x: i32, y: i32 }, + Do(&'static str), + Jump, +} +"#, + expect![[r#" +#[derive(PartialOrd, Ord)] +enum Command { + Move { x: i32, y: i32 }, + Do(&'static str), + Jump, +} + +impl < > core::cmp::PartialOrd for Command< > where { + fn partial_cmp(&self , other: &Self ) -> core::option::Option::Option { + match core::intrinsics::discriminant_value(self ).partial_cmp(&core::intrinsics::discriminant_value(other)) { + core::option::Option::Some(core::cmp::Ordering::Equal)=> { + match (self , other) { + (Command::Move { + x: x_self, y: y_self, + } + , Command::Move { + x: x_other, y: y_other, + } + )=>match x_self.partial_cmp(&x_other) { + core::option::Option::Some(core::cmp::Ordering::Equal)=> { + match y_self.partial_cmp(&y_other) { + core::option::Option::Some(core::cmp::Ordering::Equal)=> { + core::option::Option::Some(core::cmp::Ordering::Equal) + } + c=>return c, + } + } + c=>return c, + } + , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.partial_cmp(&f0_other) { + core::option::Option::Some(core::cmp::Ordering::Equal)=> { + core::option::Option::Some(core::cmp::Ordering::Equal) + } + c=>return c, + } + , (Command::Jump, Command::Jump)=>core::option::Option::Some(core::cmp::Ordering::Equal), _unused=>core::option::Option::Some(core::cmp::Ordering::Equal) + } + } + c=>return c, + } + } +} +impl < > core::cmp::Ord for Command< > where { + fn cmp(&self , other: &Self ) -> core::cmp::Ordering { + match core::intrinsics::discriminant_value(self ).cmp(&core::intrinsics::discriminant_value(other)) { + core::cmp::Ordering::Equal=> { + match (self , other) { + (Command::Move { + x: x_self, y: y_self, + } + , Command::Move { + x: x_other, y: y_other, + } + )=>match x_self.cmp(&x_other) { + core::cmp::Ordering::Equal=> { + match y_self.cmp(&y_other) { + core::cmp::Ordering::Equal=> { + core::cmp::Ordering::Equal + } + c=>return c, + } + } + c=>return c, + } + , (Command::Do(f0_self, ), Command::Do(f0_other, ))=>match f0_self.cmp(&f0_other) { + core::cmp::Ordering::Equal=> { + core::cmp::Ordering::Equal + } + c=>return c, + } + , (Command::Jump, Command::Jump)=>core::cmp::Ordering::Equal, _unused=>core::cmp::Ordering::Equal + } + } + c=>return c, + } + } +}"#]], + ); +} + +#[test] +fn test_hash_expand() { + check( + r#" +//- minicore: derive, hash +use core::hash::Hash; + +#[derive(Hash)] +enum Command { + Move { x: i32, y: i32 }, + Do(&'static str), + Jump, +} +"#, + expect![[r#" +use core::hash::Hash; + +#[derive(Hash)] +enum Command { + Move { x: i32, y: i32 }, + Do(&'static str), + Jump, +} + +impl < > core::hash::Hash for Command< > where { + fn hash(&self , state: &mut H) { + core::mem::discriminant(self ).hash(state); + match self { + Command::Move { + x: x, y: y, + } + => { + x.hash(state); + y.hash(state); + } + , Command::Do(f0, )=> { + f0.hash(state); + } + , Command::Jump=> {} + , + } + } +}"#]], + ); +} + +#[test] +fn test_debug_expand() { + check( + r#" +//- minicore: derive, fmt +use core::fmt::Debug; + +#[derive(Debug)] +enum Command { + Move { x: i32, y: i32 }, + Do(&'static str), + Jump, +} +"#, + expect![[r#" +use core::fmt::Debug; + +#[derive(Debug)] +enum Command { + Move { x: i32, y: i32 }, + Do(&'static str), + Jump, +} + +impl < > core::fmt::Debug for Command< > where { + fn fmt(&self , f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + Command::Move { + x: x, y: y, + } + =>f.debug_struct("Move").field("x", x).field("y", y).finish(), Command::Do(f0, )=>f.debug_tuple("Do").field(f0).finish(), Command::Jump=>f.write_str("Jump"), + } + } +}"#]], ); } diff --git a/crates/hir-expand/src/builtin_derive_macro.rs b/crates/hir-expand/src/builtin_derive_macro.rs index 4b9f9704c25..4ce71e9774b 100644 --- a/crates/hir-expand/src/builtin_derive_macro.rs +++ b/crates/hir-expand/src/builtin_derive_macro.rs @@ -1,12 +1,19 @@ //! Builtin derives. +use ::tt::Ident; use base_db::{CrateOrigin, LangCrateOrigin}; +use itertools::izip; +use mbe::TokenMap; use std::collections::HashSet; +use stdx::never; use tracing::debug; use crate::tt::{self, TokenId}; use syntax::{ - ast::{self, AstNode, HasGenericParams, HasModuleItem, HasName, HasTypeBounds, PathType}, + ast::{ + self, AstNode, FieldList, HasAttrs, HasGenericParams, HasModuleItem, HasName, + HasTypeBounds, PathType, + }, match_ast, }; @@ -59,8 +66,124 @@ pub fn find_builtin_derive(ident: &name::Name) -> Option BuiltinDeriveExpander::find_by_name(ident) } +enum VariantShape { + Struct(Vec), + Tuple(usize), + Unit, +} + +fn tuple_field_iterator(n: usize) -> impl Iterator { + (0..n).map(|x| Ident::new(format!("f{x}"), tt::TokenId::unspecified())) +} + +impl VariantShape { + fn as_pattern(&self, path: tt::Subtree) -> tt::Subtree { + self.as_pattern_map(path, |x| quote!(#x)) + } + + fn field_names(&self) -> Vec { + match self { + VariantShape::Struct(s) => s.clone(), + VariantShape::Tuple(n) => tuple_field_iterator(*n).collect(), + VariantShape::Unit => vec![], + } + } + + fn as_pattern_map( + &self, + path: tt::Subtree, + field_map: impl Fn(&tt::Ident) -> tt::Subtree, + ) -> tt::Subtree { + match self { + VariantShape::Struct(fields) => { + let fields = fields.iter().map(|x| { + let mapped = field_map(x); + quote! { #x : #mapped , } + }); + quote! { + #path { ##fields } + } + } + &VariantShape::Tuple(n) => { + let fields = tuple_field_iterator(n).map(|x| { + let mapped = field_map(&x); + quote! { + #mapped , + } + }); + quote! { + #path ( ##fields ) + } + } + VariantShape::Unit => path, + } + } + + fn from(value: Option, token_map: &TokenMap) -> Result { + let r = match value { + None => VariantShape::Unit, + Some(FieldList::RecordFieldList(x)) => VariantShape::Struct( + x.fields() + .map(|x| x.name()) + .map(|x| name_to_token(token_map, x)) + .collect::>()?, + ), + Some(FieldList::TupleFieldList(x)) => VariantShape::Tuple(x.fields().count()), + }; + Ok(r) + } +} + +enum AdtShape { + Struct(VariantShape), + Enum { variants: Vec<(tt::Ident, VariantShape)>, default_variant: Option }, + Union, +} + +impl AdtShape { + fn as_pattern(&self, name: &tt::Ident) -> Vec { + self.as_pattern_map(name, |x| quote!(#x)) + } + + fn field_names(&self) -> Vec> { + match self { + AdtShape::Struct(s) => { + vec![s.field_names()] + } + AdtShape::Enum { variants, .. } => { + variants.iter().map(|(_, fields)| fields.field_names()).collect() + } + AdtShape::Union => { + never!("using fields of union in derive is always wrong"); + vec![] + } + } + } + + fn as_pattern_map( + &self, + name: &tt::Ident, + field_map: impl Fn(&tt::Ident) -> tt::Subtree, + ) -> Vec { + match self { + AdtShape::Struct(s) => { + vec![s.as_pattern_map(quote! { #name }, field_map)] + } + AdtShape::Enum { variants, .. } => variants + .iter() + .map(|(v, fields)| fields.as_pattern_map(quote! { #name :: #v }, &field_map)) + .collect(), + AdtShape::Union => { + never!("pattern matching on union is always wrong"); + vec![quote! { un }] + } + } + } +} + struct BasicAdtInfo { name: tt::Ident, + shape: AdtShape, /// first field is the name, and /// second field is `Some(ty)` if it's a const param of type `ty`, `None` if it's a type param. /// third fields is where bounds, if any @@ -79,11 +202,24 @@ fn parse_adt(tt: &tt::Subtree) -> Result { ExpandError::Other("no item found".into()) })?; let node = item.syntax(); - let (name, params) = match_ast! { + let (name, params, shape) = match_ast! { match node { - ast::Struct(it) => (it.name(), it.generic_param_list()), - ast::Enum(it) => (it.name(), it.generic_param_list()), - ast::Union(it) => (it.name(), it.generic_param_list()), + ast::Struct(it) => (it.name(), it.generic_param_list(), AdtShape::Struct(VariantShape::from(it.field_list(), &token_map)?)), + ast::Enum(it) => { + let default_variant = it.variant_list().into_iter().flat_map(|x| x.variants()).position(|x| x.attrs().any(|x| x.simple_name() == Some("default".into()))); + ( + it.name(), + it.generic_param_list(), + AdtShape::Enum { + default_variant, + variants: it.variant_list() + .into_iter() + .flat_map(|x| x.variants()) + .map(|x| Ok((name_to_token(&token_map,x.name())?, VariantShape::from(x.field_list(), &token_map)?))).collect::>()? + } + ) + }, + ast::Union(it) => (it.name(), it.generic_param_list(), AdtShape::Union), _ => { debug!("unexpected node is {:?}", node); return Err(ExpandError::Other("expected struct, enum or union".into())) @@ -154,6 +290,11 @@ fn parse_adt(tt: &tt::Subtree) -> Result { .filter(is_associated_type) .map(|x| mbe::syntax_node_to_token_tree(x.syntax()).0) .collect::>(); + let name_token = name_to_token(&token_map, name)?; + Ok(BasicAdtInfo { name: name_token, shape, param_types, associated_types }) +} + +fn name_to_token(token_map: &TokenMap, name: Option) -> Result { let name = name.ok_or_else(|| { debug!("parsed item has no name"); ExpandError::Other("missing name".into()) @@ -161,7 +302,7 @@ fn parse_adt(tt: &tt::Subtree) -> Result { let name_token_id = token_map.token_by_range(name.syntax().text_range()).unwrap_or_else(TokenId::unspecified); let name_token = tt::Ident { span: name_token_id, text: name.text().into() }; - Ok(BasicAdtInfo { name: name_token, param_types, associated_types }) + Ok(name_token) } /// Given that we are deriving a trait `DerivedTrait` for a type like: @@ -195,11 +336,16 @@ fn parse_adt(tt: &tt::Subtree) -> Result { /// /// where B1, ..., BN are the bounds given by `bounds_paths`.'. Z is a phantom type, and /// therefore does not get bound by the derived trait. -fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResult { +fn expand_simple_derive( + tt: &tt::Subtree, + trait_path: tt::Subtree, + trait_body: impl FnOnce(&BasicAdtInfo) -> tt::Subtree, +) -> ExpandResult { let info = match parse_adt(tt) { Ok(info) => info, Err(e) => return ExpandResult::new(tt::Subtree::empty(), e), }; + let trait_body = trait_body(&info); let mut where_block = vec![]; let (params, args): (Vec<_>, Vec<_>) = info .param_types @@ -227,7 +373,7 @@ fn expand_simple_derive(tt: &tt::Subtree, trait_path: tt::Subtree) -> ExpandResu let name = info.name; let expanded = quote! { - impl < ##params > #trait_path for #name < ##args > where ##where_block {} + impl < ##params > #trait_path for #name < ##args > where ##where_block { #trait_body } }; ExpandResult::ok(expanded) } @@ -254,7 +400,7 @@ fn copy_expand( tt: &tt::Subtree, ) -> ExpandResult { let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::marker::Copy }) + expand_simple_derive(tt, quote! { #krate::marker::Copy }, |_| quote! {}) } fn clone_expand( @@ -263,7 +409,63 @@ fn clone_expand( tt: &tt::Subtree, ) -> ExpandResult { let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::clone::Clone }) + expand_simple_derive(tt, quote! { #krate::clone::Clone }, |adt| { + if matches!(adt.shape, AdtShape::Union) { + let star = tt::Punct { + char: '*', + spacing: ::tt::Spacing::Alone, + span: tt::TokenId::unspecified(), + }; + return quote! { + fn clone(&self) -> Self { + #star self + } + }; + } + if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) { + let star = tt::Punct { + char: '*', + spacing: ::tt::Spacing::Alone, + span: tt::TokenId::unspecified(), + }; + return quote! { + fn clone(&self) -> Self { + match #star self {} + } + }; + } + let name = &adt.name; + let patterns = adt.shape.as_pattern(name); + let exprs = adt.shape.as_pattern_map(name, |x| quote! { #x .clone() }); + let arms = patterns.into_iter().zip(exprs.into_iter()).map(|(pat, expr)| { + let fat_arrow = fat_arrow(); + quote! { + #pat #fat_arrow #expr, + } + }); + + quote! { + fn clone(&self) -> Self { + match self { + ##arms + } + } + } + }) +} + +/// This function exists since `quote! { => }` doesn't work. +fn fat_arrow() -> ::tt::Subtree { + let eq = + tt::Punct { char: '=', spacing: ::tt::Spacing::Joint, span: tt::TokenId::unspecified() }; + quote! { #eq> } +} + +/// This function exists since `quote! { && }` doesn't work. +fn and_and() -> ::tt::Subtree { + let and = + tt::Punct { char: '&', spacing: ::tt::Spacing::Joint, span: tt::TokenId::unspecified() }; + quote! { #and& } } fn default_expand( @@ -271,8 +473,38 @@ fn default_expand( id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { - let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::default::Default }) + let krate = &find_builtin_crate(db, id); + expand_simple_derive(tt, quote! { #krate::default::Default }, |adt| { + let body = match &adt.shape { + AdtShape::Struct(fields) => { + let name = &adt.name; + fields + .as_pattern_map(quote!(#name), |_| quote!(#krate::default::Default::default())) + } + AdtShape::Enum { default_variant, variants } => { + if let Some(d) = default_variant { + let (name, fields) = &variants[*d]; + let adt_name = &adt.name; + fields.as_pattern_map( + quote!(#adt_name :: #name), + |_| quote!(#krate::default::Default::default()), + ) + } else { + // FIXME: Return expand error here + quote!() + } + } + AdtShape::Union => { + // FIXME: Return expand error here + quote!() + } + }; + quote! { + fn default() -> Self { + #body + } + } + }) } fn debug_expand( @@ -280,8 +512,79 @@ fn debug_expand( id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { - let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::fmt::Debug }) + let krate = &find_builtin_crate(db, id); + expand_simple_derive(tt, quote! { #krate::fmt::Debug }, |adt| { + let for_variant = |name: String, v: &VariantShape| match v { + VariantShape::Struct(fields) => { + let for_fields = fields.iter().map(|x| { + let x_string = x.to_string(); + quote! { + .field(#x_string, #x) + } + }); + quote! { + f.debug_struct(#name) ##for_fields .finish() + } + } + VariantShape::Tuple(n) => { + let for_fields = tuple_field_iterator(*n).map(|x| { + quote! { + .field(#x) + } + }); + quote! { + f.debug_tuple(#name) ##for_fields .finish() + } + } + VariantShape::Unit => quote! { + f.write_str(#name) + }, + }; + if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) { + let star = tt::Punct { + char: '*', + spacing: ::tt::Spacing::Alone, + span: tt::TokenId::unspecified(), + }; + return quote! { + fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result { + match #star self {} + } + }; + } + let arms = match &adt.shape { + AdtShape::Struct(fields) => { + let fat_arrow = fat_arrow(); + let name = &adt.name; + let pat = fields.as_pattern(quote!(#name)); + let expr = for_variant(name.to_string(), fields); + vec![quote! { #pat #fat_arrow #expr }] + } + AdtShape::Enum { variants, .. } => variants + .iter() + .map(|(name, v)| { + let fat_arrow = fat_arrow(); + let adt_name = &adt.name; + let pat = v.as_pattern(quote!(#adt_name :: #name)); + let expr = for_variant(name.to_string(), v); + quote! { + #pat #fat_arrow #expr , + } + }) + .collect(), + AdtShape::Union => { + // FIXME: Return expand error here + vec![] + } + }; + quote! { + fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result { + match self { + ##arms + } + } + } + }) } fn hash_expand( @@ -289,8 +592,47 @@ fn hash_expand( id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { - let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::hash::Hash }) + let krate = &find_builtin_crate(db, id); + expand_simple_derive(tt, quote! { #krate::hash::Hash }, |adt| { + if matches!(adt.shape, AdtShape::Union) { + // FIXME: Return expand error here + return quote! {}; + } + if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) { + let star = tt::Punct { + char: '*', + spacing: ::tt::Spacing::Alone, + span: tt::TokenId::unspecified(), + }; + return quote! { + fn hash(&self, state: &mut H) { + match #star self {} + } + }; + } + let arms = adt.shape.as_pattern(&adt.name).into_iter().zip(adt.shape.field_names()).map( + |(pat, names)| { + let expr = { + let it = names.iter().map(|x| quote! { #x . hash(state); }); + quote! { { + ##it + } } + }; + let fat_arrow = fat_arrow(); + quote! { + #pat #fat_arrow #expr , + } + }, + ); + quote! { + fn hash(&self, state: &mut H) { + #krate::mem::discriminant(self).hash(state); + match self { + ##arms + } + } + } + }) } fn eq_expand( @@ -299,7 +641,7 @@ fn eq_expand( tt: &tt::Subtree, ) -> ExpandResult { let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::cmp::Eq }) + expand_simple_derive(tt, quote! { #krate::cmp::Eq }, |_| quote! {}) } fn partial_eq_expand( @@ -308,7 +650,65 @@ fn partial_eq_expand( tt: &tt::Subtree, ) -> ExpandResult { let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::cmp::PartialEq }) + expand_simple_derive(tt, quote! { #krate::cmp::PartialEq }, |adt| { + if matches!(adt.shape, AdtShape::Union) { + // FIXME: Return expand error here + return quote! {}; + } + let name = &adt.name; + + let (self_patterns, other_patterns) = self_and_other_patterns(adt, name); + let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map( + |(pat1, pat2, names)| { + let fat_arrow = fat_arrow(); + let body = match &*names { + [] => { + quote!(true) + } + [first, rest @ ..] => { + let rest = rest.iter().map(|x| { + let t1 = Ident::new(format!("{}_self", x.text), x.span); + let t2 = Ident::new(format!("{}_other", x.text), x.span); + let and_and = and_and(); + quote!(#and_and #t1 .eq( #t2 )) + }); + let first = { + let t1 = Ident::new(format!("{}_self", first.text), first.span); + let t2 = Ident::new(format!("{}_other", first.text), first.span); + quote!(#t1 .eq( #t2 )) + }; + quote!(#first ##rest) + } + }; + quote! { ( #pat1 , #pat2 ) #fat_arrow #body , } + }, + ); + + let fat_arrow = fat_arrow(); + quote! { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + ##arms + _unused #fat_arrow false + } + } + } + }) +} + +fn self_and_other_patterns( + adt: &BasicAdtInfo, + name: &tt::Ident, +) -> (Vec, Vec) { + let self_patterns = adt.shape.as_pattern_map(name, |x| { + let t = Ident::new(format!("{}_self", x.text), x.span); + quote!(#t) + }); + let other_patterns = adt.shape.as_pattern_map(name, |x| { + let t = Ident::new(format!("{}_other", x.text), x.span); + quote!(#t) + }); + (self_patterns, other_patterns) } fn ord_expand( @@ -316,8 +716,63 @@ fn ord_expand( id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { - let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::cmp::Ord }) + let krate = &find_builtin_crate(db, id); + expand_simple_derive(tt, quote! { #krate::cmp::Ord }, |adt| { + fn compare( + krate: &tt::TokenTree, + left: tt::Subtree, + right: tt::Subtree, + rest: tt::Subtree, + ) -> tt::Subtree { + let fat_arrow1 = fat_arrow(); + let fat_arrow2 = fat_arrow(); + quote! { + match #left.cmp(&#right) { + #krate::cmp::Ordering::Equal #fat_arrow1 { + #rest + } + c #fat_arrow2 return c, + } + } + } + if matches!(adt.shape, AdtShape::Union) { + // FIXME: Return expand error here + return quote!(); + } + let left = quote!(#krate::intrinsics::discriminant_value(self)); + let right = quote!(#krate::intrinsics::discriminant_value(other)); + + let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name); + let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map( + |(pat1, pat2, fields)| { + let mut body = quote!(#krate::cmp::Ordering::Equal); + for f in fields.into_iter().rev() { + let t1 = Ident::new(format!("{}_self", f.text), f.span); + let t2 = Ident::new(format!("{}_other", f.text), f.span); + body = compare(krate, quote!(#t1), quote!(#t2), body); + } + let fat_arrow = fat_arrow(); + quote! { ( #pat1 , #pat2 ) #fat_arrow #body , } + }, + ); + let fat_arrow = fat_arrow(); + let body = compare( + krate, + left, + right, + quote! { + match (self, other) { + ##arms + _unused #fat_arrow #krate::cmp::Ordering::Equal + } + }, + ); + quote! { + fn cmp(&self, other: &Self) -> #krate::cmp::Ordering { + #body + } + } + }) } fn partial_ord_expand( @@ -325,6 +780,61 @@ fn partial_ord_expand( id: MacroCallId, tt: &tt::Subtree, ) -> ExpandResult { - let krate = find_builtin_crate(db, id); - expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd }) + let krate = &find_builtin_crate(db, id); + expand_simple_derive(tt, quote! { #krate::cmp::PartialOrd }, |adt| { + fn compare( + krate: &tt::TokenTree, + left: tt::Subtree, + right: tt::Subtree, + rest: tt::Subtree, + ) -> tt::Subtree { + let fat_arrow1 = fat_arrow(); + let fat_arrow2 = fat_arrow(); + quote! { + match #left.partial_cmp(&#right) { + #krate::option::Option::Some(#krate::cmp::Ordering::Equal) #fat_arrow1 { + #rest + } + c #fat_arrow2 return c, + } + } + } + if matches!(adt.shape, AdtShape::Union) { + // FIXME: Return expand error here + return quote!(); + } + let left = quote!(#krate::intrinsics::discriminant_value(self)); + let right = quote!(#krate::intrinsics::discriminant_value(other)); + + let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name); + let arms = izip!(self_patterns, other_patterns, adt.shape.field_names()).map( + |(pat1, pat2, fields)| { + let mut body = quote!(#krate::option::Option::Some(#krate::cmp::Ordering::Equal)); + for f in fields.into_iter().rev() { + let t1 = Ident::new(format!("{}_self", f.text), f.span); + let t2 = Ident::new(format!("{}_other", f.text), f.span); + body = compare(krate, quote!(#t1), quote!(#t2), body); + } + let fat_arrow = fat_arrow(); + quote! { ( #pat1 , #pat2 ) #fat_arrow #body , } + }, + ); + let fat_arrow = fat_arrow(); + let body = compare( + krate, + left, + right, + quote! { + match (self, other) { + ##arms + _unused #fat_arrow #krate::option::Option::Some(#krate::cmp::Ordering::Equal) + } + }, + ); + quote! { + fn partial_cmp(&self, other: &Self) -> #krate::option::Option::Option<#krate::cmp::Ordering> { + #body + } + } + }) } diff --git a/crates/hir-expand/src/quote.rs b/crates/hir-expand/src/quote.rs index 63586f9daf0..ab3809abc7a 100644 --- a/crates/hir-expand/src/quote.rs +++ b/crates/hir-expand/src/quote.rs @@ -162,6 +162,12 @@ fn to_token(self) -> crate::tt::TokenTree { } } +impl ToTokenTree for &crate::tt::TokenTree { + fn to_token(self) -> crate::tt::TokenTree { + self.clone() + } +} + impl ToTokenTree for crate::tt::Subtree { fn to_token(self) -> crate::tt::TokenTree { self.into() diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 5a850f6d570..12b15065ddf 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/crates/hir-ty/src/consteval/tests.rs @@ -1227,6 +1227,53 @@ fn from(E1(x): E1) -> Self { ); } +#[test] +fn builtin_derive_macro() { + check_number( + r#" + //- minicore: clone, derive, builtin_impls + #[derive(Clone)] + enum Z { + Foo(Y), + Bar, + } + #[derive(Clone)] + struct X(i32, Z, i64) + #[derive(Clone)] + struct Y { + field1: i32, + field2: u8, + } + + const GOAL: u8 = { + let x = X(2, Z::Foo(Y { field1: 4, field2: 5 }), 8); + let x = x.clone(); + let Z::Foo(t) = x.1; + t.field2 + }; + "#, + 5, + ); + check_number( + r#" + //- minicore: default, derive, builtin_impls + #[derive(Default)] + struct X(i32, Y, i64) + #[derive(Default)] + struct Y { + field1: i32, + field2: u8, + } + + const GOAL: u8 = { + let x = X::default(); + x.1.field2 + }; + "#, + 0, + ); +} + #[test] fn try_operator() { check_number( diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index 91af5716ca5..8c03d197e1c 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -151,9 +151,11 @@ fn _format( _db: &RootDatabase, _kind: SyntaxKind, _file_id: FileId, - _expansion: &str, + expansion: &str, ) -> Option { - None + // remove trailing spaces for test + use itertools::Itertools; + Some(expansion.lines().map(|x| x.trim_end()).join("\n")) } #[cfg(not(any(test, target_arch = "wasm32", target_os = "emscripten")))] @@ -276,8 +278,7 @@ macro_rules! baz { "#, expect![[r#" foo! - fn b(){} - "#]], + fn b(){}"#]], ); } @@ -471,8 +472,17 @@ struct Foo {} "#, expect![[r#" Clone - impl < >core::clone::Clone for Foo< >where{} - "#]], + impl < >core::clone::Clone for Foo< >where { + fn clone(&self) -> Self { + match self { + Foo{} + => Foo{} + , + + } + } + + }"#]], ); } @@ -488,8 +498,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >core::marker::Copy for Foo< >where{} - "#]], + impl < >core::marker::Copy for Foo< >where{}"#]], ); } @@ -504,8 +513,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >core::marker::Copy for Foo< >where{} - "#]], + impl < >core::marker::Copy for Foo< >where{}"#]], ); check( r#" @@ -516,8 +524,17 @@ struct Foo {} "#, expect![[r#" Clone - impl < >core::clone::Clone for Foo< >where{} - "#]], + impl < >core::clone::Clone for Foo< >where { + fn clone(&self) -> Self { + match self { + Foo{} + => Foo{} + , + + } + } + + }"#]], ); } } diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index f403ef8ee03..c693235f344 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -11,6 +11,7 @@ //! add: //! as_ref: sized //! bool_impl: option, fn +//! builtin_impls: //! cell: copy, drop //! clone: sized //! coerce_unsized: unsize @@ -127,6 +128,27 @@ pub trait Default: Sized { #[rustc_builtin_macro(Default, attributes(default))] pub macro Default($item:item) {} // endregion:derive + + // region:builtin_impls + macro_rules! impl_default { + ($v:literal; $($t:ty)*) => { + $( + impl const Default for $t { + fn default() -> Self { + $v + } + } + )* + } + } + + impl_default! { + 0; usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 + } + impl_default! { + 0.0; f32 f64 + } + // endregion:builtin_impls } // endregion:default @@ -137,6 +159,11 @@ pub trait Hasher {} pub trait Hash { fn hash(&self, state: &mut H); } + + // region:derive + #[rustc_builtin_macro] + pub macro Hash($item:item) {} + // endregion:derive } // endregion:hash @@ -198,6 +225,28 @@ fn clone(&self) -> Self { *self } } + + // region:builtin_impls + macro_rules! impl_clone { + ($($t:ty)*) => { + $( + impl const Clone for $t { + fn clone(&self) -> Self { + *self + } + } + )* + } + } + + impl_clone! { + usize u8 u16 u32 u64 u128 + isize i8 i16 i32 i64 i128 + f32 f64 + bool char + } + // endregion:builtin_impls + // region:derive #[rustc_builtin_macro] pub macro Clone($item:item) {} @@ -723,6 +772,11 @@ pub trait Debug { pub trait Display { fn fmt(&self, f: &mut Formatter<'_>) -> Result; } + + // region:derive + #[rustc_builtin_macro] + pub macro Debug($item:item) {} + // endregion:derive } // endregion:fmt diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs index b7dbc82e1d6..c2ebf03746a 100644 --- a/crates/tt/src/lib.rs +++ b/crates/tt/src/lib.rs @@ -153,6 +153,12 @@ pub struct Ident { pub span: Span, } +impl Ident { + pub fn new(text: impl Into, span: S) -> Self { + Ident { text: text.into(), span } + } +} + fn print_debug_subtree( f: &mut fmt::Formatter<'_>, subtree: &Subtree,