]>
Commit | Line | Data |
---|---|---|
e1599b0c XL |
1 | //! The compiler code necessary to implement the `#[derive(RustcEncodable)]` |
2 | //! (and `RustcDecodable`, in `decodable.rs`) extension. The idea here is that | |
3 | //! type-defining items may be tagged with | |
4 | //! `#[derive(RustcEncodable, RustcDecodable)]`. | |
1a4d82fc JJ |
5 | //! |
6 | //! For example, a type like: | |
7 | //! | |
04454e1e | 8 | //! ```ignore (old code) |
e1599b0c | 9 | //! #[derive(RustcEncodable, RustcDecodable)] |
85aaf69f | 10 | //! struct Node { id: usize } |
1a4d82fc JJ |
11 | //! ``` |
12 | //! | |
13 | //! would generate two implementations like: | |
14 | //! | |
04454e1e | 15 | //! ```ignore (old code) |
041b39d2 | 16 | //! # struct Node { id: usize } |
1a4d82fc JJ |
17 | //! impl<S: Encoder<E>, E> Encodable<S, E> for Node { |
18 | //! fn encode(&self, s: &mut S) -> Result<(), E> { | |
19 | //! s.emit_struct("Node", 1, |this| { | |
20 | //! this.emit_struct_field("id", 0, |this| { | |
21 | //! Encodable::encode(&self.id, this) | |
85aaf69f | 22 | //! /* this.emit_usize(self.id) can also be used */ |
1a4d82fc JJ |
23 | //! }) |
24 | //! }) | |
25 | //! } | |
26 | //! } | |
27 | //! | |
28 | //! impl<D: Decoder<E>, E> Decodable<D, E> for Node { | |
29 | //! fn decode(d: &mut D) -> Result<Node, E> { | |
30 | //! d.read_struct("Node", 1, |this| { | |
31 | //! match this.read_struct_field("id", 0, |this| Decodable::decode(this)) { | |
32 | //! Ok(id) => Ok(Node { id: id }), | |
33 | //! Err(e) => Err(e), | |
34 | //! } | |
35 | //! }) | |
36 | //! } | |
37 | //! } | |
38 | //! ``` | |
39 | //! | |
40 | //! Other interesting scenarios are when the item has type parameters or | |
9fa01778 | 41 | //! references other non-built-in types. A type definition like: |
1a4d82fc | 42 | //! |
04454e1e | 43 | //! ```ignore (old code) |
e1599b0c XL |
44 | //! # #[derive(RustcEncodable, RustcDecodable)] |
45 | //! # struct Span; | |
46 | //! #[derive(RustcEncodable, RustcDecodable)] | |
1a4d82fc JJ |
47 | //! struct Spanned<T> { node: T, span: Span } |
48 | //! ``` | |
49 | //! | |
50 | //! would yield functions like: | |
51 | //! | |
04454e1e | 52 | //! ```ignore (old code) |
e1599b0c XL |
53 | //! # #[derive(RustcEncodable, RustcDecodable)] |
54 | //! # struct Span; | |
041b39d2 | 55 | //! # struct Spanned<T> { node: T, span: Span } |
1a4d82fc JJ |
56 | //! impl< |
57 | //! S: Encoder<E>, | |
58 | //! E, | |
59 | //! T: Encodable<S, E> | |
60 | //! > Encodable<S, E> for Spanned<T> { | |
61 | //! fn encode(&self, s: &mut S) -> Result<(), E> { | |
62 | //! s.emit_struct("Spanned", 2, |this| { | |
63 | //! this.emit_struct_field("node", 0, |this| self.node.encode(this)) | |
c34b1796 | 64 | //! .unwrap(); |
1a4d82fc JJ |
65 | //! this.emit_struct_field("span", 1, |this| self.span.encode(this)) |
66 | //! }) | |
67 | //! } | |
68 | //! } | |
69 | //! | |
70 | //! impl< | |
71 | //! D: Decoder<E>, | |
72 | //! E, | |
73 | //! T: Decodable<D, E> | |
74 | //! > Decodable<D, E> for Spanned<T> { | |
75 | //! fn decode(d: &mut D) -> Result<Spanned<T>, E> { | |
76 | //! d.read_struct("Spanned", 2, |this| { | |
77 | //! Ok(Spanned { | |
78 | //! node: this.read_struct_field("node", 0, |this| Decodable::decode(this)) | |
c34b1796 | 79 | //! .unwrap(), |
1a4d82fc | 80 | //! span: this.read_struct_field("span", 1, |this| Decodable::decode(this)) |
c34b1796 | 81 | //! .unwrap(), |
1a4d82fc JJ |
82 | //! }) |
83 | //! }) | |
84 | //! } | |
85 | //! } | |
86 | //! ``` | |
970d7e83 | 87 | |
9fa01778 | 88 | use crate::deriving::generic::ty::*; |
dfeec247 XL |
89 | use crate::deriving::generic::*; |
90 | use crate::deriving::pathvec_std; | |
f2b60f7d | 91 | use rustc_ast::{AttrVec, ExprKind, MetaItem, Mutability}; |
dfeec247 | 92 | use rustc_expand::base::{Annotatable, ExtCtxt}; |
3dfed10e | 93 | use rustc_span::symbol::{sym, Ident, Symbol}; |
dfeec247 | 94 | use rustc_span::Span; |
9ffffee4 | 95 | use thin_vec::{thin_vec, ThinVec}; |
970d7e83 | 96 | |
dfeec247 XL |
97 | pub fn expand_deriving_rustc_encodable( |
98 | cx: &mut ExtCtxt<'_>, | |
99 | span: Span, | |
100 | mitem: &MetaItem, | |
101 | item: &Annotatable, | |
102 | push: &mut dyn FnMut(Annotatable), | |
487cf647 | 103 | is_const: bool, |
dfeec247 | 104 | ) { |
3dfed10e XL |
105 | let krate = sym::rustc_serialize; |
106 | let typaram = sym::__S; | |
54a0048b | 107 | |
970d7e83 | 108 | let trait_def = TraitDef { |
3b2f2976 | 109 | span, |
064997fb | 110 | path: Path::new_(vec![krate, sym::Encodable], vec![], PathKind::Global), |
2b03887a | 111 | skip_path_as_bound: false, |
9ffffee4 | 112 | needs_copy_as_bound_if_packed: true, |
1a4d82fc | 113 | additional_bounds: Vec::new(), |
9e0c209e | 114 | supports_unions: false, |
dfeec247 | 115 | methods: vec![MethodDef { |
3dfed10e XL |
116 | name: sym::encode, |
117 | generics: Bounds { | |
dfeec247 XL |
118 | bounds: vec![( |
119 | typaram, | |
064997fb | 120 | vec![Path::new_(vec![krate, sym::Encoder], vec![], PathKind::Global)], |
dfeec247 XL |
121 | )], |
122 | }, | |
064997fb FG |
123 | explicit_self: true, |
124 | nonself_args: vec![( | |
125 | Ref(Box::new(Path(Path::new_local(typaram))), Mutability::Mut), | |
136023e0 | 126 | sym::s, |
dfeec247 | 127 | )], |
064997fb | 128 | ret_ty: Path(Path::new_( |
3dfed10e | 129 | pathvec_std!(result::Result), |
dfeec247 | 130 | vec![ |
064997fb FG |
131 | Box::new(Unit), |
132 | Box::new(Path(Path::new_(vec![typaram, sym::Error], vec![], PathKind::Local))), | |
dfeec247 XL |
133 | ], |
134 | PathKind::Std, | |
135 | )), | |
f2b60f7d | 136 | attributes: AttrVec::new(), |
9c376795 | 137 | fieldless_variants_strategy: FieldlessVariantsStrategy::Default, |
dfeec247 XL |
138 | combine_substructure: combine_substructure(Box::new(|a, b, c| { |
139 | encodable_substructure(a, b, c, krate) | |
140 | })), | |
141 | }], | |
85aaf69f | 142 | associated_types: Vec::new(), |
487cf647 | 143 | is_const, |
970d7e83 LB |
144 | }; |
145 | ||
62682a34 | 146 | trait_def.expand(cx, mitem, item, push) |
970d7e83 LB |
147 | } |
148 | ||
dfeec247 XL |
149 | fn encodable_substructure( |
150 | cx: &mut ExtCtxt<'_>, | |
151 | trait_span: Span, | |
152 | substr: &Substructure<'_>, | |
3dfed10e | 153 | krate: Symbol, |
064997fb FG |
154 | ) -> BlockOrExpr { |
155 | let encoder = substr.nonselflike_args[0].clone(); | |
970d7e83 | 156 | // throw an underscore in front to suppress unused variable warnings |
3dfed10e | 157 | let blkarg = Ident::new(sym::_e, trait_span); |
1a4d82fc | 158 | let blkencoder = cx.expr_ident(trait_span, blkarg); |
dfeec247 XL |
159 | let fn_path = cx.expr_path(cx.path_global( |
160 | trait_span, | |
161 | vec![ | |
3dfed10e XL |
162 | Ident::new(krate, trait_span), |
163 | Ident::new(sym::Encodable, trait_span), | |
164 | Ident::new(sym::encode, trait_span), | |
dfeec247 XL |
165 | ], |
166 | )); | |
970d7e83 | 167 | |
487cf647 FG |
168 | match substr.fields { |
169 | Struct(_, fields) => { | |
6a06907d XL |
170 | let fn_emit_struct_field_path = |
171 | cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_struct_field]); | |
9ffffee4 | 172 | let mut stmts = ThinVec::new(); |
064997fb | 173 | for (i, &FieldInfo { name, ref self_expr, span, .. }) in fields.iter().enumerate() { |
1a4d82fc | 174 | let name = match name { |
476ff2be SL |
175 | Some(id) => id.name, |
176 | None => Symbol::intern(&format!("_field{}", i)), | |
970d7e83 | 177 | }; |
064997fb | 178 | let self_ref = cx.expr_addr_of(span, self_expr.clone()); |
9ffffee4 FG |
179 | let enc = |
180 | cx.expr_call(span, fn_path.clone(), thin_vec![self_ref, blkencoder.clone()]); | |
476ff2be | 181 | let lambda = cx.lambda1(span, enc, blkarg); |
6a06907d | 182 | let call = cx.expr_call_global( |
dfeec247 | 183 | span, |
6a06907d | 184 | fn_emit_struct_field_path.clone(), |
9ffffee4 | 185 | thin_vec![ |
6a06907d XL |
186 | blkencoder.clone(), |
187 | cx.expr_str(span, name), | |
188 | cx.expr_usize(span, i), | |
189 | lambda, | |
190 | ], | |
dfeec247 | 191 | ); |
1a4d82fc JJ |
192 | |
193 | // last call doesn't need a try! | |
c34b1796 | 194 | let last = fields.len() - 1; |
1a4d82fc JJ |
195 | let call = if i != last { |
196 | cx.expr_try(span, call) | |
197 | } else { | |
7453a54e | 198 | cx.expr(span, ExprKind::Ret(Some(call))) |
1a4d82fc | 199 | }; |
2c00a5a8 | 200 | |
94b46f34 | 201 | let stmt = cx.stmt_expr(call); |
2c00a5a8 | 202 | stmts.push(stmt); |
970d7e83 LB |
203 | } |
204 | ||
1a4d82fc | 205 | // unit structs have no fields and need to return Ok() |
0531ce1d | 206 | let blk = if stmts.is_empty() { |
9ffffee4 | 207 | let ok = cx.expr_ok(trait_span, cx.expr_tuple(trait_span, ThinVec::new())); |
0531ce1d XL |
208 | cx.lambda1(trait_span, ok, blkarg) |
209 | } else { | |
210 | cx.lambda_stmts_1(trait_span, stmts, blkarg) | |
211 | }; | |
1a4d82fc | 212 | |
6a06907d XL |
213 | let fn_emit_struct_path = |
214 | cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_struct]); | |
215 | ||
064997fb | 216 | let expr = cx.expr_call_global( |
dfeec247 | 217 | trait_span, |
6a06907d | 218 | fn_emit_struct_path, |
9ffffee4 | 219 | thin_vec![ |
6a06907d | 220 | encoder, |
dfeec247 XL |
221 | cx.expr_str(trait_span, substr.type_ident.name), |
222 | cx.expr_usize(trait_span, fields.len()), | |
223 | blk, | |
224 | ], | |
064997fb FG |
225 | ); |
226 | BlockOrExpr::new_expr(expr) | |
970d7e83 LB |
227 | } |
228 | ||
487cf647 | 229 | EnumMatching(idx, _, variant, fields) => { |
970d7e83 LB |
230 | // We're not generating an AST that the borrow checker is expecting, |
231 | // so we need to generate a unique local variable to take the | |
232 | // mutable loan out on, otherwise we get conflicts which don't | |
233 | // actually exist. | |
1a4d82fc JJ |
234 | let me = cx.stmt_let(trait_span, false, blkarg, encoder); |
235 | let encoder = cx.expr_ident(trait_span, blkarg); | |
6a06907d XL |
236 | |
237 | let fn_emit_enum_variant_arg_path: Vec<_> = | |
238 | cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_enum_variant_arg]); | |
239 | ||
9ffffee4 | 240 | let mut stmts = ThinVec::new(); |
9346a6ac | 241 | if !fields.is_empty() { |
c34b1796 | 242 | let last = fields.len() - 1; |
064997fb FG |
243 | for (i, &FieldInfo { ref self_expr, span, .. }) in fields.iter().enumerate() { |
244 | let self_ref = cx.expr_addr_of(span, self_expr.clone()); | |
9ffffee4 FG |
245 | let enc = cx.expr_call( |
246 | span, | |
247 | fn_path.clone(), | |
248 | thin_vec![self_ref, blkencoder.clone()], | |
249 | ); | |
476ff2be | 250 | let lambda = cx.lambda1(span, enc, blkarg); |
6a06907d XL |
251 | |
252 | let call = cx.expr_call_global( | |
dfeec247 | 253 | span, |
6a06907d | 254 | fn_emit_enum_variant_arg_path.clone(), |
9ffffee4 | 255 | thin_vec![blkencoder.clone(), cx.expr_usize(span, i), lambda], |
dfeec247 | 256 | ); |
c34b1796 AL |
257 | let call = if i != last { |
258 | cx.expr_try(span, call) | |
259 | } else { | |
7453a54e | 260 | cx.expr(span, ExprKind::Ret(Some(call))) |
c34b1796 AL |
261 | }; |
262 | stmts.push(cx.stmt_expr(call)); | |
263 | } | |
264 | } else { | |
9ffffee4 | 265 | let ok = cx.expr_ok(trait_span, cx.expr_tuple(trait_span, ThinVec::new())); |
7453a54e | 266 | let ret_ok = cx.expr(trait_span, ExprKind::Ret(Some(ok))); |
1a4d82fc JJ |
267 | stmts.push(cx.stmt_expr(ret_ok)); |
268 | } | |
269 | ||
270 | let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg); | |
e1599b0c | 271 | let name = cx.expr_str(trait_span, variant.ident.name); |
6a06907d XL |
272 | |
273 | let fn_emit_enum_variant_path: Vec<_> = | |
274 | cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_enum_variant]); | |
275 | ||
276 | let call = cx.expr_call_global( | |
dfeec247 | 277 | trait_span, |
6a06907d | 278 | fn_emit_enum_variant_path, |
9ffffee4 | 279 | thin_vec![ |
6a06907d | 280 | blkencoder, |
dfeec247 | 281 | name, |
487cf647 | 282 | cx.expr_usize(trait_span, *idx), |
dfeec247 XL |
283 | cx.expr_usize(trait_span, fields.len()), |
284 | blk, | |
285 | ], | |
286 | ); | |
6a06907d | 287 | |
476ff2be | 288 | let blk = cx.lambda1(trait_span, call, blkarg); |
6a06907d XL |
289 | let fn_emit_enum_path: Vec<_> = |
290 | cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_enum]); | |
064997fb | 291 | let expr = cx.expr_call_global( |
dfeec247 | 292 | trait_span, |
6a06907d | 293 | fn_emit_enum_path, |
9ffffee4 | 294 | thin_vec![encoder, cx.expr_str(trait_span, substr.type_ident.name), blk], |
dfeec247 | 295 | ); |
9ffffee4 | 296 | BlockOrExpr::new_mixed(thin_vec![me], Some(expr)) |
970d7e83 LB |
297 | } |
298 | ||
5bcae85e | 299 | _ => cx.bug("expected Struct or EnumMatching in derive(Encodable)"), |
ba9703b0 | 300 | } |
970d7e83 | 301 | } |