diff options
Diffstat (limited to 'compiler/rustc_builtin_macros/src/deriving/mod.rs')
-rw-r--r-- | compiler/rustc_builtin_macros/src/deriving/mod.rs | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/compiler/rustc_builtin_macros/src/deriving/mod.rs b/compiler/rustc_builtin_macros/src/deriving/mod.rs new file mode 100644 index 000000000..c1ca089da --- /dev/null +++ b/compiler/rustc_builtin_macros/src/deriving/mod.rs @@ -0,0 +1,208 @@ +//! The compiler code necessary to implement the `#[derive]` extensions. + +use rustc_ast as ast; +use rustc_ast::ptr::P; +use rustc_ast::{GenericArg, Impl, ItemKind, MetaItem}; +use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier}; +use rustc_span::symbol::{sym, Ident, Symbol}; +use rustc_span::Span; + +macro path_local($x:ident) { + generic::ty::Path::new_local(sym::$x) +} + +macro pathvec_std($($rest:ident)::+) {{ + vec![ $( sym::$rest ),+ ] +}} + +macro path_std($($x:tt)*) { + generic::ty::Path::new( pathvec_std!( $($x)* ) ) +} + +pub mod bounds; +pub mod clone; +pub mod debug; +pub mod decodable; +pub mod default; +pub mod encodable; +pub mod hash; + +#[path = "cmp/eq.rs"] +pub mod eq; +#[path = "cmp/ord.rs"] +pub mod ord; +#[path = "cmp/partial_eq.rs"] +pub mod partial_eq; +#[path = "cmp/partial_ord.rs"] +pub mod partial_ord; + +pub mod generic; + +pub(crate) struct BuiltinDerive( + pub(crate) fn(&mut ExtCtxt<'_>, Span, &MetaItem, &Annotatable, &mut dyn FnMut(Annotatable)), +); + +impl MultiItemModifier for BuiltinDerive { + fn expand( + &self, + ecx: &mut ExtCtxt<'_>, + span: Span, + meta_item: &MetaItem, + item: Annotatable, + ) -> ExpandResult<Vec<Annotatable>, Annotatable> { + // FIXME: Built-in derives often forget to give spans contexts, + // so we are doing it here in a centralized way. + let span = ecx.with_def_site_ctxt(span); + let mut items = Vec::new(); + match item { + Annotatable::Stmt(stmt) => { + if let ast::StmtKind::Item(item) = stmt.into_inner().kind { + (self.0)(ecx, span, meta_item, &Annotatable::Item(item), &mut |a| { + // Cannot use 'ecx.stmt_item' here, because we need to pass 'ecx' + // to the function + items.push(Annotatable::Stmt(P(ast::Stmt { + id: ast::DUMMY_NODE_ID, + kind: ast::StmtKind::Item(a.expect_item()), + span, + }))); + }); + } else { + unreachable!("should have already errored on non-item statement") + } + } + _ => { + (self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a)); + } + } + ExpandResult::Ready(items) + } +} + +/// Constructs an expression that calls an intrinsic +fn call_intrinsic( + cx: &ExtCtxt<'_>, + span: Span, + intrinsic: Symbol, + args: Vec<P<ast::Expr>>, +) -> P<ast::Expr> { + let span = cx.with_def_site_ctxt(span); + let path = cx.std_path(&[sym::intrinsics, intrinsic]); + cx.expr_call_global(span, path, args) +} + +/// Constructs an expression that calls the `unreachable` intrinsic. +fn call_unreachable(cx: &ExtCtxt<'_>, span: Span) -> P<ast::Expr> { + let span = cx.with_def_site_ctxt(span); + let path = cx.std_path(&[sym::intrinsics, sym::unreachable]); + let call = cx.expr_call_global(span, path, vec![]); + + cx.expr_block(P(ast::Block { + stmts: vec![cx.stmt_expr(call)], + id: ast::DUMMY_NODE_ID, + rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated), + span, + tokens: None, + could_be_bare_literal: false, + })) +} + +// Injects `impl<...> Structural for ItemType<...> { }`. In particular, +// does *not* add `where T: Structural` for parameters `T` in `...`. +// (That's the main reason we cannot use TraitDef here.) +fn inject_impl_of_structural_trait( + cx: &mut ExtCtxt<'_>, + span: Span, + item: &Annotatable, + structural_path: generic::ty::Path, + push: &mut dyn FnMut(Annotatable), +) { + let Annotatable::Item(ref item) = *item else { + unreachable!(); + }; + + let generics = match item.kind { + ItemKind::Struct(_, ref generics) | ItemKind::Enum(_, ref generics) => generics, + // Do not inject `impl Structural for Union`. (`PartialEq` does not + // support unions, so we will see error downstream.) + ItemKind::Union(..) => return, + _ => unreachable!(), + }; + + // Create generics param list for where clauses and impl headers + let mut generics = generics.clone(); + + // Create the type of `self`. + // + // in addition, remove defaults from generic params (impls cannot have them). + let self_params: Vec<_> = generics + .params + .iter_mut() + .map(|param| match &mut param.kind { + ast::GenericParamKind::Lifetime => { + ast::GenericArg::Lifetime(cx.lifetime(span, param.ident)) + } + ast::GenericParamKind::Type { default } => { + *default = None; + ast::GenericArg::Type(cx.ty_ident(span, param.ident)) + } + ast::GenericParamKind::Const { ty: _, kw_span: _, default } => { + *default = None; + ast::GenericArg::Const(cx.const_ident(span, param.ident)) + } + }) + .collect(); + + let type_ident = item.ident; + + let trait_ref = cx.trait_ref(structural_path.to_path(cx, span, type_ident, &generics)); + let self_type = cx.ty_path(cx.path_all(span, false, vec![type_ident], self_params)); + + // It would be nice to also encode constraint `where Self: Eq` (by adding it + // onto `generics` cloned above). Unfortunately, that strategy runs afoul of + // rust-lang/rust#48214. So we perform that additional check in the compiler + // itself, instead of encoding it here. + + // Keep the lint and stability attributes of the original item, to control + // how the generated implementation is linted. + let mut attrs = Vec::new(); + attrs.extend( + item.attrs + .iter() + .filter(|a| { + [sym::allow, sym::warn, sym::deny, sym::forbid, sym::stable, sym::unstable] + .contains(&a.name_or_empty()) + }) + .cloned(), + ); + + let newitem = cx.item( + span, + Ident::empty(), + attrs, + ItemKind::Impl(Box::new(Impl { + unsafety: ast::Unsafe::No, + polarity: ast::ImplPolarity::Positive, + defaultness: ast::Defaultness::Final, + constness: ast::Const::No, + generics, + of_trait: Some(trait_ref), + self_ty: self_type, + items: Vec::new(), + })), + ); + + push(Annotatable::Item(newitem)); +} + +fn assert_ty_bounds( + cx: &mut ExtCtxt<'_>, + stmts: &mut Vec<ast::Stmt>, + ty: P<ast::Ty>, + span: Span, + assert_path: &[Symbol], +) { + // Generate statement `let _: assert_path<ty>;`. + let span = cx.with_def_site_ctxt(span); + let assert_path = cx.path_all(span, true, cx.std_path(assert_path), vec![GenericArg::Type(ty)]); + stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path))); +} |