]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_builtin_macros/src/deriving/mod.rs
Merge commit 'fdb84cbfd25908df5683f8f62388f663d9260e39' into clippyup
[rust.git] / compiler / rustc_builtin_macros / src / deriving / mod.rs
1 //! The compiler code necessary to implement the `#[derive]` extensions.
2
3 use rustc_ast as ast;
4 use rustc_ast::ptr::P;
5 use rustc_ast::{GenericArg, Impl, ItemKind, MetaItem};
6 use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier};
7 use rustc_span::symbol::{sym, Ident, Symbol};
8 use rustc_span::Span;
9
10 macro path_local($x:ident) {
11     generic::ty::Path::new_local(sym::$x)
12 }
13
14 macro pathvec_std($($rest:ident)::+) {{
15     vec![ $( sym::$rest ),+ ]
16 }}
17
18 macro path_std($($x:tt)*) {
19     generic::ty::Path::new( pathvec_std!( $($x)* ) )
20 }
21
22 pub mod bounds;
23 pub mod clone;
24 pub mod debug;
25 pub mod decodable;
26 pub mod default;
27 pub mod encodable;
28 pub mod hash;
29
30 #[path = "cmp/eq.rs"]
31 pub mod eq;
32 #[path = "cmp/ord.rs"]
33 pub mod ord;
34 #[path = "cmp/partial_eq.rs"]
35 pub mod partial_eq;
36 #[path = "cmp/partial_ord.rs"]
37 pub mod partial_ord;
38
39 pub mod generic;
40
41 pub(crate) struct BuiltinDerive(
42     pub(crate) fn(&mut ExtCtxt<'_>, Span, &MetaItem, &Annotatable, &mut dyn FnMut(Annotatable)),
43 );
44
45 impl MultiItemModifier for BuiltinDerive {
46     fn expand(
47         &self,
48         ecx: &mut ExtCtxt<'_>,
49         span: Span,
50         meta_item: &MetaItem,
51         item: Annotatable,
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.into_inner().kind {
60                     (self.0)(ecx, span, meta_item, &Annotatable::Item(item), &mut |a| {
61                         // Cannot use 'ecx.stmt_item' here, because we need to pass 'ecx'
62                         // to the function
63                         items.push(Annotatable::Stmt(P(ast::Stmt {
64                             id: ast::DUMMY_NODE_ID,
65                             kind: ast::StmtKind::Item(a.expect_item()),
66                             span,
67                         })));
68                     });
69                 } else {
70                     unreachable!("should have already errored on non-item statement")
71                 }
72             }
73             _ => {
74                 (self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a));
75             }
76         }
77         ExpandResult::Ready(items)
78     }
79 }
80
81 /// Constructs an expression that calls an intrinsic
82 fn call_intrinsic(
83     cx: &ExtCtxt<'_>,
84     span: Span,
85     intrinsic: Symbol,
86     args: Vec<P<ast::Expr>>,
87 ) -> P<ast::Expr> {
88     let span = cx.with_def_site_ctxt(span);
89     let path = cx.std_path(&[sym::intrinsics, intrinsic]);
90     cx.expr_call_global(span, path, args)
91 }
92
93 /// Constructs an expression that calls the `unreachable` intrinsic.
94 fn call_unreachable(cx: &ExtCtxt<'_>, span: Span) -> P<ast::Expr> {
95     let span = cx.with_def_site_ctxt(span);
96     let path = cx.std_path(&[sym::intrinsics, sym::unreachable]);
97     let call = cx.expr_call_global(span, path, vec![]);
98
99     cx.expr_block(P(ast::Block {
100         stmts: vec![cx.stmt_expr(call)],
101         id: ast::DUMMY_NODE_ID,
102         rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated),
103         span,
104         tokens: None,
105         could_be_bare_literal: false,
106     }))
107 }
108
109 // Injects `impl<...> Structural for ItemType<...> { }`. In particular,
110 // does *not* add `where T: Structural` for parameters `T` in `...`.
111 // (That's the main reason we cannot use TraitDef here.)
112 fn inject_impl_of_structural_trait(
113     cx: &mut ExtCtxt<'_>,
114     span: Span,
115     item: &Annotatable,
116     structural_path: generic::ty::Path,
117     push: &mut dyn FnMut(Annotatable),
118 ) {
119     let Annotatable::Item(ref item) = *item else {
120         unreachable!();
121     };
122
123     let generics = match item.kind {
124         ItemKind::Struct(_, ref generics) | ItemKind::Enum(_, ref generics) => generics,
125         // Do not inject `impl Structural for Union`. (`PartialEq` does not
126         // support unions, so we will see error downstream.)
127         ItemKind::Union(..) => return,
128         _ => unreachable!(),
129     };
130
131     // Create generics param list for where clauses and impl headers
132     let mut generics = generics.clone();
133
134     // Create the type of `self`.
135     //
136     // in addition, remove defaults from generic params (impls cannot have them).
137     let self_params: Vec<_> = generics
138         .params
139         .iter_mut()
140         .map(|param| match &mut param.kind {
141             ast::GenericParamKind::Lifetime => {
142                 ast::GenericArg::Lifetime(cx.lifetime(span, param.ident))
143             }
144             ast::GenericParamKind::Type { default } => {
145                 *default = None;
146                 ast::GenericArg::Type(cx.ty_ident(span, param.ident))
147             }
148             ast::GenericParamKind::Const { ty: _, kw_span: _, default } => {
149                 *default = None;
150                 ast::GenericArg::Const(cx.const_ident(span, param.ident))
151             }
152         })
153         .collect();
154
155     let type_ident = item.ident;
156
157     let trait_ref = cx.trait_ref(structural_path.to_path(cx, span, type_ident, &generics));
158     let self_type = cx.ty_path(cx.path_all(span, false, vec![type_ident], self_params));
159
160     // It would be nice to also encode constraint `where Self: Eq` (by adding it
161     // onto `generics` cloned above). Unfortunately, that strategy runs afoul of
162     // rust-lang/rust#48214. So we perform that additional check in the compiler
163     // itself, instead of encoding it here.
164
165     // Keep the lint and stability attributes of the original item, to control
166     // how the generated implementation is linted.
167     let mut attrs = Vec::new();
168     attrs.extend(
169         item.attrs
170             .iter()
171             .filter(|a| {
172                 [sym::allow, sym::warn, sym::deny, sym::forbid, sym::stable, sym::unstable]
173                     .contains(&a.name_or_empty())
174             })
175             .cloned(),
176     );
177
178     let newitem = cx.item(
179         span,
180         Ident::empty(),
181         attrs,
182         ItemKind::Impl(Box::new(Impl {
183             unsafety: ast::Unsafe::No,
184             polarity: ast::ImplPolarity::Positive,
185             defaultness: ast::Defaultness::Final,
186             constness: ast::Const::No,
187             generics,
188             of_trait: Some(trait_ref),
189             self_ty: self_type,
190             items: Vec::new(),
191         })),
192     );
193
194     push(Annotatable::Item(newitem));
195 }
196
197 fn assert_ty_bounds(
198     cx: &mut ExtCtxt<'_>,
199     stmts: &mut Vec<ast::Stmt>,
200     ty: P<ast::Ty>,
201     span: Span,
202     assert_path: &[Symbol],
203 ) {
204     // Generate statement `let _: assert_path<ty>;`.
205     let span = cx.with_def_site_ctxt(span);
206     let assert_path = cx.path_all(span, true, cx.std_path(assert_path), vec![GenericArg::Type(ty)]);
207     stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path)));
208 }