rustc_builtin_macros/deriving/
mod.rs

1//! The compiler code necessary to implement the `#[derive]` extensions.
2
3use rustc_ast as ast;
4use rustc_ast::{GenericArg, MetaItem};
5use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier};
6use rustc_span::{Span, Symbol, sym};
7use thin_vec::{ThinVec, thin_vec};
8
9macro path_local($x:ident) {
10    generic::ty::Path::new_local(sym::$x)
11}
12
13macro pathvec_std($($rest:ident)::+) {{
14    vec![ $( sym::$rest ),+ ]
15}}
16
17macro path_std($($x:tt)*) {
18    generic::ty::Path::new( pathvec_std!( $($x)* ) )
19}
20
21pub(crate) mod bounds;
22pub(crate) mod clone;
23pub(crate) mod coerce_pointee;
24pub(crate) mod debug;
25pub(crate) mod default;
26pub(crate) mod hash;
27
28#[path = "cmp/eq.rs"]
29pub(crate) mod eq;
30#[path = "cmp/ord.rs"]
31pub(crate) mod ord;
32#[path = "cmp/partial_eq.rs"]
33pub(crate) mod partial_eq;
34#[path = "cmp/partial_ord.rs"]
35pub(crate) mod partial_ord;
36
37pub(crate) mod generic;
38
39pub(crate) type BuiltinDeriveFn =
40    fn(&ExtCtxt<'_>, Span, &MetaItem, &Annotatable, &mut dyn FnMut(Annotatable), bool);
41
42pub(crate) struct BuiltinDerive(pub(crate) BuiltinDeriveFn);
43
44impl MultiItemModifier for BuiltinDerive {
45    fn expand(
46        &self,
47        ecx: &mut ExtCtxt<'_>,
48        span: Span,
49        meta_item: &MetaItem,
50        item: Annotatable,
51        is_derive_const: bool,
52    ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
53        // FIXME: Built-in derives often forget to give spans contexts,
54        // so we are doing it here in a centralized way.
55        let span = ecx.with_def_site_ctxt(span);
56        let mut items = Vec::new();
57        match item {
58            Annotatable::Stmt(stmt) => {
59                if let ast::StmtKind::Item(item) = stmt.kind {
60                    (self.0)(
61                        ecx,
62                        span,
63                        meta_item,
64                        &Annotatable::Item(item),
65                        &mut |a| {
66                            // Cannot use 'ecx.stmt_item' here, because we need to pass 'ecx'
67                            // to the function
68                            items.push(Annotatable::Stmt(Box::new(ast::Stmt {
69                                id: ast::DUMMY_NODE_ID,
70                                kind: ast::StmtKind::Item(a.expect_item()),
71                                span,
72                            })));
73                        },
74                        is_derive_const,
75                    );
76                } else {
77                    unreachable!("should have already errored on non-item statement")
78                }
79            }
80            _ => {
81                (self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a), is_derive_const);
82            }
83        }
84        ExpandResult::Ready(items)
85    }
86}
87
88/// Constructs an expression that calls an intrinsic
89fn call_intrinsic(
90    cx: &ExtCtxt<'_>,
91    span: Span,
92    intrinsic: Symbol,
93    args: ThinVec<Box<ast::Expr>>,
94) -> Box<ast::Expr> {
95    let span = cx.with_def_site_ctxt(span);
96    let path = cx.std_path(&[sym::intrinsics, intrinsic]);
97    cx.expr_call_global(span, path, args)
98}
99
100/// Constructs an expression that calls the `unreachable` intrinsic.
101fn call_unreachable(cx: &ExtCtxt<'_>, span: Span) -> Box<ast::Expr> {
102    let span = cx.with_def_site_ctxt(span);
103    let path = cx.std_path(&[sym::intrinsics, sym::unreachable]);
104    let call = cx.expr_call_global(span, path, ThinVec::new());
105
106    cx.expr_block(Box::new(ast::Block {
107        stmts: thin_vec![cx.stmt_expr(call)],
108        id: ast::DUMMY_NODE_ID,
109        rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated),
110        span,
111        tokens: None,
112    }))
113}
114
115fn assert_ty_bounds(
116    cx: &ExtCtxt<'_>,
117    stmts: &mut ThinVec<ast::Stmt>,
118    ty: Box<ast::Ty>,
119    span: Span,
120    assert_path: &[Symbol],
121) {
122    // Generate statement `let _: assert_path<ty>;`.
123    let span = cx.with_def_site_ctxt(span);
124    let assert_path = cx.path_all(span, true, cx.std_path(assert_path), vec![GenericArg::Type(ty)]);
125    stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path)));
126}