diff options
Diffstat (limited to 'compiler/rustc_builtin_macros/src/deriving/hash.rs')
-rw-r--r-- | compiler/rustc_builtin_macros/src/deriving/hash.rs | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/compiler/rustc_builtin_macros/src/deriving/hash.rs b/compiler/rustc_builtin_macros/src/deriving/hash.rs new file mode 100644 index 000000000..32ae3d344 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/hash.rs @@ -0,0 +1,80 @@ +use crate::deriving::generic::ty::*; +use crate::deriving::generic::*; +use crate::deriving::{path_std, pathvec_std}; + +use rustc_ast::{MetaItem, Mutability}; +use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_span::symbol::sym; +use rustc_span::Span; + +pub fn expand_deriving_hash( + cx: &mut ExtCtxt<'_>, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut dyn FnMut(Annotatable), +) { + let path = Path::new_(pathvec_std!(hash::Hash), vec![], PathKind::Std); + + let typaram = sym::__H; + + let arg = Path::new_local(typaram); + let hash_trait_def = TraitDef { + span, + attributes: Vec::new(), + path, + additional_bounds: Vec::new(), + generics: Bounds::empty(), + supports_unions: false, + methods: vec![MethodDef { + name: sym::hash, + generics: Bounds { bounds: vec![(typaram, vec![path_std!(hash::Hasher)])] }, + explicit_self: true, + nonself_args: vec![(Ref(Box::new(Path(arg)), Mutability::Mut), sym::state)], + ret_ty: Unit, + attributes: vec![], + unify_fieldless_variants: true, + combine_substructure: combine_substructure(Box::new(|a, b, c| { + hash_substructure(a, b, c) + })), + }], + associated_types: Vec::new(), + }; + + hash_trait_def.expand(cx, mitem, item, push); +} + +fn hash_substructure( + cx: &mut ExtCtxt<'_>, + trait_span: Span, + substr: &Substructure<'_>, +) -> BlockOrExpr { + let [state_expr] = substr.nonselflike_args else { + cx.span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`"); + }; + let call_hash = |span, expr| { + let hash_path = { + let strs = cx.std_path(&[sym::hash, sym::Hash, sym::hash]); + + cx.expr_path(cx.path_global(span, strs)) + }; + let expr = cx.expr_call(span, hash_path, vec![expr, state_expr.clone()]); + cx.stmt_expr(expr) + }; + + let (stmts, match_expr) = match substr.fields { + Struct(_, fields) | EnumMatching(.., fields) => { + let stmts = + fields.iter().map(|field| call_hash(field.span, field.self_expr.clone())).collect(); + (stmts, None) + } + EnumTag(tag_field, match_expr) => { + assert!(tag_field.other_selflike_exprs.is_empty()); + let stmts = vec![call_hash(tag_field.span, tag_field.self_expr.clone())]; + (stmts, match_expr.clone()) + } + _ => cx.span_bug(trait_span, "impossible substructure in `derive(Hash)`"), + }; + + BlockOrExpr::new_mixed(stmts, match_expr) +} |