summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_builtin_macros/src/deriving/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_builtin_macros/src/deriving/mod.rs')
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/mod.rs208
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)));
+}