]>
Commit | Line | Data |
---|---|---|
1a4d82fc | 1 | //! The compiler code necessary to implement the `#[derive]` extensions. |
223e47cc | 2 | |
3dfed10e | 3 | use rustc_ast as ast; |
74b04a01 | 4 | use rustc_ast::ptr::P; |
064997fb | 5 | use rustc_ast::{GenericArg, Impl, ItemKind, MetaItem}; |
ba9703b0 | 6 | use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, MultiItemModifier}; |
f9f354fc | 7 | use rustc_span::symbol::{sym, Ident, Symbol}; |
dfeec247 | 8 | use rustc_span::Span; |
223e47cc | 9 | |
ff7c6d11 | 10 | macro path_local($x:ident) { |
3dfed10e | 11 | generic::ty::Path::new_local(sym::$x) |
85aaf69f SL |
12 | } |
13 | ||
3dfed10e XL |
14 | macro pathvec_std($($rest:ident)::+) {{ |
15 | vec![ $( sym::$rest ),+ ] | |
ff7c6d11 | 16 | }} |
85aaf69f | 17 | |
ff7c6d11 XL |
18 | macro path_std($($x:tt)*) { |
19 | generic::ty::Path::new( pathvec_std!( $($x)* ) ) | |
85aaf69f SL |
20 | } |
21 | ||
1a4d82fc | 22 | pub mod bounds; |
223e47cc | 23 | pub mod clone; |
b039eaaf | 24 | pub mod debug; |
dfeec247 | 25 | pub mod decodable; |
1a4d82fc | 26 | pub mod default; |
dfeec247 XL |
27 | pub mod encodable; |
28 | pub mod hash; | |
970d7e83 | 29 | |
dfeec247 | 30 | #[path = "cmp/eq.rs"] |
970d7e83 | 31 | pub mod eq; |
dfeec247 | 32 | #[path = "cmp/ord.rs"] |
970d7e83 | 33 | pub mod ord; |
dfeec247 XL |
34 | #[path = "cmp/partial_eq.rs"] |
35 | pub mod partial_eq; | |
36 | #[path = "cmp/partial_ord.rs"] | |
37 | pub mod partial_ord; | |
223e47cc | 38 | |
970d7e83 LB |
39 | pub mod generic; |
40 | ||
487cf647 FG |
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); | |
dc9dc135 XL |
45 | |
46 | impl MultiItemModifier for BuiltinDerive { | |
dfeec247 XL |
47 | fn expand( |
48 | &self, | |
49 | ecx: &mut ExtCtxt<'_>, | |
50 | span: Span, | |
51 | meta_item: &MetaItem, | |
52 | item: Annotatable, | |
487cf647 | 53 | is_derive_const: bool, |
ba9703b0 | 54 | ) -> ExpandResult<Vec<Annotatable>, Annotatable> { |
e1599b0c XL |
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); | |
dc9dc135 | 58 | let mut items = Vec::new(); |
fc512014 XL |
59 | match item { |
60 | Annotatable::Stmt(stmt) => { | |
61 | if let ast::StmtKind::Item(item) = stmt.into_inner().kind { | |
487cf647 FG |
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 | ); | |
fc512014 XL |
78 | } else { |
79 | unreachable!("should have already errored on non-item statement") | |
80 | } | |
81 | } | |
82 | _ => { | |
487cf647 | 83 | (self.0)(ecx, span, meta_item, &item, &mut |a| items.push(a), is_derive_const); |
fc512014 XL |
84 | } |
85 | } | |
ba9703b0 | 86 | ExpandResult::Ready(items) |
dc9dc135 XL |
87 | } |
88 | } | |
89 | ||
54a0048b | 90 | /// Constructs an expression that calls an intrinsic |
dfeec247 XL |
91 | fn call_intrinsic( |
92 | cx: &ExtCtxt<'_>, | |
93 | span: Span, | |
3dfed10e | 94 | intrinsic: Symbol, |
dfeec247 XL |
95 | args: Vec<P<ast::Expr>>, |
96 | ) -> P<ast::Expr> { | |
e1599b0c | 97 | let span = cx.with_def_site_ctxt(span); |
3dfed10e | 98 | let path = cx.std_path(&[sym::intrinsics, intrinsic]); |
29967ef6 XL |
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![]); | |
54a0048b SL |
107 | |
108 | cx.expr_block(P(ast::Block { | |
3157f602 | 109 | stmts: vec![cx.stmt_expr(call)], |
54a0048b SL |
110 | id: ast::DUMMY_NODE_ID, |
111 | rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated), | |
3b2f2976 | 112 | span, |
1b1a35ee | 113 | tokens: None, |
94222f64 | 114 | could_be_bare_literal: false, |
5bcae85e | 115 | })) |
54a0048b | 116 | } |
e74abb32 | 117 | |
e74abb32 XL |
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.) | |
dfeec247 XL |
121 | fn inject_impl_of_structural_trait( |
122 | cx: &mut ExtCtxt<'_>, | |
123 | span: Span, | |
124 | item: &Annotatable, | |
3dfed10e | 125 | structural_path: generic::ty::Path, |
dfeec247 XL |
126 | push: &mut dyn FnMut(Annotatable), |
127 | ) { | |
487cf647 | 128 | let Annotatable::Item(item) = item else { |
5e7ed085 | 129 | unreachable!(); |
e74abb32 XL |
130 | }; |
131 | ||
487cf647 FG |
132 | let generics = match &item.kind { |
133 | ItemKind::Struct(_, generics) | ItemKind::Enum(_, generics) => generics, | |
e74abb32 XL |
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 | ||
2b03887a FG |
143 | let ctxt = span.ctxt(); |
144 | ||
e74abb32 XL |
145 | // Create the type of `self`. |
146 | // | |
5099ac24 | 147 | // in addition, remove defaults from generic params (impls cannot have them). |
dfeec247 XL |
148 | let self_params: Vec<_> = generics |
149 | .params | |
150 | .iter_mut() | |
151 | .map(|param| match &mut param.kind { | |
2b03887a FG |
152 | ast::GenericParamKind::Lifetime => ast::GenericArg::Lifetime( |
153 | cx.lifetime(param.ident.span.with_ctxt(ctxt), param.ident), | |
154 | ), | |
dfeec247 XL |
155 | ast::GenericParamKind::Type { default } => { |
156 | *default = None; | |
2b03887a | 157 | ast::GenericArg::Type(cx.ty_ident(param.ident.span.with_ctxt(ctxt), param.ident)) |
dfeec247 | 158 | } |
5869c6ff XL |
159 | ast::GenericParamKind::Const { ty: _, kw_span: _, default } => { |
160 | *default = None; | |
2b03887a FG |
161 | ast::GenericArg::Const( |
162 | cx.const_ident(param.ident.span.with_ctxt(ctxt), param.ident), | |
163 | ) | |
dfeec247 XL |
164 | } |
165 | }) | |
166 | .collect(); | |
e74abb32 XL |
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. | |
f2b60f7d | 180 | let mut attrs = ast::AttrVec::new(); |
dfeec247 XL |
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 | ); | |
2b03887a | 190 | // Mark as `automatically_derived` to avoid some silly lints. |
487cf647 | 191 | attrs.push(cx.attr_word(sym::automatically_derived, span)); |
dfeec247 XL |
192 | |
193 | let newitem = cx.item( | |
194 | span, | |
3c0e092e | 195 | Ident::empty(), |
dfeec247 | 196 | attrs, |
3c0e092e | 197 | ItemKind::Impl(Box::new(Impl { |
74b04a01 | 198 | unsafety: ast::Unsafe::No, |
dfeec247 XL |
199 | polarity: ast::ImplPolarity::Positive, |
200 | defaultness: ast::Defaultness::Final, | |
74b04a01 | 201 | constness: ast::Const::No, |
dfeec247 XL |
202 | generics, |
203 | of_trait: Some(trait_ref), | |
204 | self_ty: self_type, | |
205 | items: Vec::new(), | |
94222f64 | 206 | })), |
dfeec247 | 207 | ); |
e74abb32 XL |
208 | |
209 | push(Annotatable::Item(newitem)); | |
210 | } | |
064997fb FG |
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 | } |