]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_builtin_macros/src/deriving/mod.rs
Rollup merge of #104193 - TaKO8Ki:fix-104142, r=cjgillot
[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) type BuiltinDeriveFn =
42     fn(&mut ExtCtxt<'_>, Span, &MetaItem, &Annotatable, &mut dyn FnMut(Annotatable), bool);
43
44 pub(crate) struct BuiltinDerive(pub(crate) BuiltinDeriveFn);
45
46 impl MultiItemModifier for BuiltinDerive {
47     fn expand(
48         &self,
49         ecx: &mut ExtCtxt<'_>,
50         span: Span,
51         meta_item: &MetaItem,
52         item: Annotatable,
53         is_derive_const: bool,
54     ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
55         // FIXME: Built-in derives often forget to give spans contexts,
56         // so we are doing it here in a centralized way.
57         let span = ecx.with_def_site_ctxt(span);
58         let mut items = Vec::new();
59         match item {
60             Annotatable::Stmt(stmt) => {
61                 if let ast::StmtKind::Item(item) = stmt.into_inner().kind {
62                     (self.0)(
63                         ecx,
64                         span,
65                         meta_item,
66                         &Annotatable::Item(item),
67                         &mut |a| {
68                             // Cannot use 'ecx.stmt_item' here, because we need to pass 'ecx'
69                             // to the function
70                             items.push(Annotatable::Stmt(P(ast::Stmt {
71                                 id: ast::DUMMY_NODE_ID,
72                                 kind: ast::StmtKind::Item(a.expect_item()),
73                                 span,
74                             })));
75                         },
76                         is_derive_const,
77                     );
78                 } else {
79                     unreachable!("should have already errored on non-item statement")
80                 }
81             }
82             _ => {
83                 (self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a), is_derive_const);
84             }
85         }
86         ExpandResult::Ready(items)
87     }
88 }
89
90 /// Constructs an expression that calls an intrinsic
91 fn call_intrinsic(
92     cx: &ExtCtxt<'_>,
93     span: Span,
94     intrinsic: Symbol,
95     args: Vec<P<ast::Expr>>,
96 ) -> P<ast::Expr> {
97     let span = cx.with_def_site_ctxt(span);
98     let path = cx.std_path(&[sym::intrinsics, intrinsic]);
99     cx.expr_call_global(span, path, args)
100 }
101
102 /// Constructs an expression that calls the `unreachable` intrinsic.
103 fn call_unreachable(cx: &ExtCtxt<'_>, span: Span) -> P<ast::Expr> {
104     let span = cx.with_def_site_ctxt(span);
105     let path = cx.std_path(&[sym::intrinsics, sym::unreachable]);
106     let call = cx.expr_call_global(span, path, vec![]);
107
108     cx.expr_block(P(ast::Block {
109         stmts: vec![cx.stmt_expr(call)],
110         id: ast::DUMMY_NODE_ID,
111         rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated),
112         span,
113         tokens: None,
114         could_be_bare_literal: false,
115     }))
116 }
117
118 // Injects `impl<...> Structural for ItemType<...> { }`. In particular,
119 // does *not* add `where T: Structural` for parameters `T` in `...`.
120 // (That's the main reason we cannot use TraitDef here.)
121 fn inject_impl_of_structural_trait(
122     cx: &mut ExtCtxt<'_>,
123     span: Span,
124     item: &Annotatable,
125     structural_path: generic::ty::Path,
126     push: &mut dyn FnMut(Annotatable),
127 ) {
128     let Annotatable::Item(ref item) = *item else {
129         unreachable!();
130     };
131
132     let generics = match item.kind {
133         ItemKind::Struct(_, ref generics) | ItemKind::Enum(_, ref generics) => generics,
134         // Do not inject `impl Structural for Union`. (`PartialEq` does not
135         // support unions, so we will see error downstream.)
136         ItemKind::Union(..) => return,
137         _ => unreachable!(),
138     };
139
140     // Create generics param list for where clauses and impl headers
141     let mut generics = generics.clone();
142
143     let ctxt = span.ctxt();
144
145     // Create the type of `self`.
146     //
147     // in addition, remove defaults from generic params (impls cannot have them).
148     let self_params: Vec<_> = generics
149         .params
150         .iter_mut()
151         .map(|param| match &mut param.kind {
152             ast::GenericParamKind::Lifetime => ast::GenericArg::Lifetime(
153                 cx.lifetime(param.ident.span.with_ctxt(ctxt), param.ident),
154             ),
155             ast::GenericParamKind::Type { default } => {
156                 *default = None;
157                 ast::GenericArg::Type(cx.ty_ident(param.ident.span.with_ctxt(ctxt), param.ident))
158             }
159             ast::GenericParamKind::Const { ty: _, kw_span: _, default } => {
160                 *default = None;
161                 ast::GenericArg::Const(
162                     cx.const_ident(param.ident.span.with_ctxt(ctxt), param.ident),
163                 )
164             }
165         })
166         .collect();
167
168     let type_ident = item.ident;
169
170     let trait_ref = cx.trait_ref(structural_path.to_path(cx, span, type_ident, &generics));
171     let self_type = cx.ty_path(cx.path_all(span, false, vec![type_ident], self_params));
172
173     // It would be nice to also encode constraint `where Self: Eq` (by adding it
174     // onto `generics` cloned above). Unfortunately, that strategy runs afoul of
175     // rust-lang/rust#48214. So we perform that additional check in the compiler
176     // itself, instead of encoding it here.
177
178     // Keep the lint and stability attributes of the original item, to control
179     // how the generated implementation is linted.
180     let mut attrs = ast::AttrVec::new();
181     attrs.extend(
182         item.attrs
183             .iter()
184             .filter(|a| {
185                 [sym::allow, sym::warn, sym::deny, sym::forbid, sym::stable, sym::unstable]
186                     .contains(&a.name_or_empty())
187             })
188             .cloned(),
189     );
190     // Mark as `automatically_derived` to avoid some silly lints.
191     attrs.push(cx.attribute(cx.meta_word(span, sym::automatically_derived)));
192
193     let newitem = cx.item(
194         span,
195         Ident::empty(),
196         attrs,
197         ItemKind::Impl(Box::new(Impl {
198             unsafety: ast::Unsafe::No,
199             polarity: ast::ImplPolarity::Positive,
200             defaultness: ast::Defaultness::Final,
201             constness: ast::Const::No,
202             generics,
203             of_trait: Some(trait_ref),
204             self_ty: self_type,
205             items: Vec::new(),
206         })),
207     );
208
209     push(Annotatable::Item(newitem));
210 }
211
212 fn assert_ty_bounds(
213     cx: &mut ExtCtxt<'_>,
214     stmts: &mut Vec<ast::Stmt>,
215     ty: P<ast::Ty>,
216     span: Span,
217     assert_path: &[Symbol],
218 ) {
219     // Generate statement `let _: assert_path<ty>;`.
220     let span = cx.with_def_site_ctxt(span);
221     let assert_path = cx.path_all(span, true, cx.std_path(assert_path), vec![GenericArg::Type(ty)]);
222     stmts.push(cx.stmt_let_type_only(span, cx.ty_path(assert_path)));
223 }