]>
Commit | Line | Data |
---|---|---|
970d7e83 LB |
1 | // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
1a4d82fc JJ |
11 | //! The compiler code necessary to implement the `#[derive(Encodable)]` |
12 | //! (and `Decodable`, in decodable.rs) extension. The idea here is that | |
13 | //! type-defining items may be tagged with `#[derive(Encodable, Decodable)]`. | |
14 | //! | |
15 | //! For example, a type like: | |
16 | //! | |
17 | //! ```ignore | |
18 | //! #[derive(Encodable, Decodable)] | |
85aaf69f | 19 | //! struct Node { id: usize } |
1a4d82fc JJ |
20 | //! ``` |
21 | //! | |
22 | //! would generate two implementations like: | |
23 | //! | |
24 | //! ```ignore | |
25 | //! impl<S: Encoder<E>, E> Encodable<S, E> for Node { | |
26 | //! fn encode(&self, s: &mut S) -> Result<(), E> { | |
27 | //! s.emit_struct("Node", 1, |this| { | |
28 | //! this.emit_struct_field("id", 0, |this| { | |
29 | //! Encodable::encode(&self.id, this) | |
85aaf69f | 30 | //! /* this.emit_usize(self.id) can also be used */ |
1a4d82fc JJ |
31 | //! }) |
32 | //! }) | |
33 | //! } | |
34 | //! } | |
35 | //! | |
36 | //! impl<D: Decoder<E>, E> Decodable<D, E> for Node { | |
37 | //! fn decode(d: &mut D) -> Result<Node, E> { | |
38 | //! d.read_struct("Node", 1, |this| { | |
39 | //! match this.read_struct_field("id", 0, |this| Decodable::decode(this)) { | |
40 | //! Ok(id) => Ok(Node { id: id }), | |
41 | //! Err(e) => Err(e), | |
42 | //! } | |
43 | //! }) | |
44 | //! } | |
45 | //! } | |
46 | //! ``` | |
47 | //! | |
48 | //! Other interesting scenarios are when the item has type parameters or | |
49 | //! references other non-built-in types. A type definition like: | |
50 | //! | |
51 | //! ```ignore | |
52 | //! #[derive(Encodable, Decodable)] | |
53 | //! struct Spanned<T> { node: T, span: Span } | |
54 | //! ``` | |
55 | //! | |
56 | //! would yield functions like: | |
57 | //! | |
58 | //! ```ignore | |
59 | //! impl< | |
60 | //! S: Encoder<E>, | |
61 | //! E, | |
62 | //! T: Encodable<S, E> | |
63 | //! > Encodable<S, E> for Spanned<T> { | |
64 | //! fn encode(&self, s: &mut S) -> Result<(), E> { | |
65 | //! s.emit_struct("Spanned", 2, |this| { | |
66 | //! this.emit_struct_field("node", 0, |this| self.node.encode(this)) | |
c34b1796 | 67 | //! .unwrap(); |
1a4d82fc JJ |
68 | //! this.emit_struct_field("span", 1, |this| self.span.encode(this)) |
69 | //! }) | |
70 | //! } | |
71 | //! } | |
72 | //! | |
73 | //! impl< | |
74 | //! D: Decoder<E>, | |
75 | //! E, | |
76 | //! T: Decodable<D, E> | |
77 | //! > Decodable<D, E> for Spanned<T> { | |
78 | //! fn decode(d: &mut D) -> Result<Spanned<T>, E> { | |
79 | //! d.read_struct("Spanned", 2, |this| { | |
80 | //! Ok(Spanned { | |
81 | //! node: this.read_struct_field("node", 0, |this| Decodable::decode(this)) | |
c34b1796 | 82 | //! .unwrap(), |
1a4d82fc | 83 | //! span: this.read_struct_field("span", 1, |this| Decodable::decode(this)) |
c34b1796 | 84 | //! .unwrap(), |
1a4d82fc JJ |
85 | //! }) |
86 | //! }) | |
87 | //! } | |
88 | //! } | |
89 | //! ``` | |
970d7e83 | 90 | |
54a0048b | 91 | use deriving; |
9cc50fc6 SL |
92 | use deriving::generic::*; |
93 | use deriving::generic::ty::*; | |
94 | ||
5bcae85e SL |
95 | use syntax::ast::{Expr, ExprKind, MetaItem, Mutability}; |
96 | use syntax::ext::base::{Annotatable, ExtCtxt}; | |
9cc50fc6 SL |
97 | use syntax::ext::build::AstBuilder; |
98 | use syntax::parse::token; | |
99 | use syntax::ptr::P; | |
3157f602 | 100 | use syntax_pos::Span; |
970d7e83 | 101 | |
d9579d0f AL |
102 | pub fn expand_deriving_rustc_encodable(cx: &mut ExtCtxt, |
103 | span: Span, | |
104 | mitem: &MetaItem, | |
62682a34 | 105 | item: &Annotatable, |
5bcae85e | 106 | push: &mut FnMut(Annotatable)) { |
1a4d82fc | 107 | expand_deriving_encodable_imp(cx, span, mitem, item, push, "rustc_serialize") |
970d7e83 LB |
108 | } |
109 | ||
d9579d0f AL |
110 | pub fn expand_deriving_encodable(cx: &mut ExtCtxt, |
111 | span: Span, | |
112 | mitem: &MetaItem, | |
62682a34 | 113 | item: &Annotatable, |
5bcae85e | 114 | push: &mut FnMut(Annotatable)) { |
1a4d82fc | 115 | expand_deriving_encodable_imp(cx, span, mitem, item, push, "serialize") |
970d7e83 LB |
116 | } |
117 | ||
d9579d0f AL |
118 | fn expand_deriving_encodable_imp(cx: &mut ExtCtxt, |
119 | span: Span, | |
120 | mitem: &MetaItem, | |
62682a34 | 121 | item: &Annotatable, |
d9579d0f | 122 | push: &mut FnMut(Annotatable), |
5bcae85e | 123 | krate: &'static str) { |
e9174d1e | 124 | if cx.crate_root != Some("std") { |
85aaf69f | 125 | // FIXME(#21880): lift this requirement. |
5bcae85e SL |
126 | cx.span_err(span, |
127 | "this trait cannot be derived with #![no_std] \ | |
e9174d1e | 128 | or #![no_core]"); |
85aaf69f SL |
129 | return; |
130 | } | |
131 | ||
54a0048b SL |
132 | let typaram = &*deriving::hygienic_type_parameter(item, "__S"); |
133 | ||
970d7e83 | 134 | let trait_def = TraitDef { |
1a4d82fc JJ |
135 | span: span, |
136 | attributes: Vec::new(), | |
5bcae85e | 137 | path: Path::new_(vec![krate, "Encodable"], None, vec![], true), |
1a4d82fc JJ |
138 | additional_bounds: Vec::new(), |
139 | generics: LifetimeBounds::empty(), | |
e9174d1e | 140 | is_unsafe: false, |
9e0c209e | 141 | supports_unions: false, |
c30ab7b3 | 142 | methods: vec![ |
970d7e83 LB |
143 | MethodDef { |
144 | name: "encode", | |
1a4d82fc JJ |
145 | generics: LifetimeBounds { |
146 | lifetimes: Vec::new(), | |
54a0048b | 147 | bounds: vec![(typaram, |
c30ab7b3 | 148 | vec![Path::new_(vec![krate, "Encoder"], None, vec![], true)])] |
1a4d82fc JJ |
149 | }, |
150 | explicit_self: borrowed_explicit_self(), | |
c30ab7b3 SL |
151 | args: vec![Ptr(Box::new(Literal(Path::new_local(typaram))), |
152 | Borrowed(None, Mutability::Mutable))], | |
1a4d82fc | 153 | ret_ty: Literal(Path::new_( |
85aaf69f | 154 | pathvec_std!(cx, core::result::Result), |
1a4d82fc | 155 | None, |
c30ab7b3 | 156 | vec![Box::new(Tuple(Vec::new())), Box::new(Literal(Path::new_( |
54a0048b | 157 | vec![typaram, "Error"], None, vec![], false |
c30ab7b3 | 158 | )))], |
1a4d82fc JJ |
159 | true |
160 | )), | |
161 | attributes: Vec::new(), | |
62682a34 | 162 | is_unsafe: false, |
a7813a04 | 163 | unify_fieldless_variants: false, |
c34b1796 | 164 | combine_substructure: combine_substructure(Box::new(|a, b, c| { |
a7813a04 | 165 | encodable_substructure(a, b, c, krate) |
c34b1796 | 166 | })), |
85aaf69f | 167 | } |
c30ab7b3 | 168 | ], |
85aaf69f | 169 | associated_types: Vec::new(), |
970d7e83 LB |
170 | }; |
171 | ||
62682a34 | 172 | trait_def.expand(cx, mitem, item, push) |
970d7e83 LB |
173 | } |
174 | ||
5bcae85e SL |
175 | fn encodable_substructure(cx: &mut ExtCtxt, |
176 | trait_span: Span, | |
177 | substr: &Substructure, | |
178 | krate: &'static str) | |
179 | -> P<Expr> { | |
1a4d82fc | 180 | let encoder = substr.nonself_args[0].clone(); |
970d7e83 LB |
181 | // throw an underscore in front to suppress unused variable warnings |
182 | let blkarg = cx.ident_of("_e"); | |
1a4d82fc | 183 | let blkencoder = cx.expr_ident(trait_span, blkarg); |
5bcae85e SL |
184 | let fn_path = cx.expr_path(cx.path_global(trait_span, |
185 | vec![cx.ident_of(krate), | |
186 | cx.ident_of("Encodable"), | |
187 | cx.ident_of("encode")])); | |
970d7e83 LB |
188 | |
189 | return match *substr.fields { | |
7453a54e | 190 | Struct(_, ref fields) => { |
970d7e83 | 191 | let emit_struct_field = cx.ident_of("emit_struct_field"); |
1a4d82fc | 192 | let mut stmts = Vec::new(); |
5bcae85e | 193 | for (i, &FieldInfo { name, ref self_, span, .. }) in fields.iter().enumerate() { |
1a4d82fc | 194 | let name = match name { |
c1a9b12d | 195 | Some(id) => id.name.as_str(), |
5bcae85e | 196 | None => token::intern_and_get_ident(&format!("_field{}", i)), |
970d7e83 | 197 | }; |
a7813a04 XL |
198 | let self_ref = cx.expr_addr_of(span, self_.clone()); |
199 | let enc = cx.expr_call(span, fn_path.clone(), vec![self_ref, blkencoder.clone()]); | |
970d7e83 | 200 | let lambda = cx.lambda_expr_1(span, enc, blkarg); |
5bcae85e SL |
201 | let call = cx.expr_method_call(span, |
202 | blkencoder.clone(), | |
970d7e83 | 203 | emit_struct_field, |
5bcae85e SL |
204 | vec![cx.expr_str(span, name), |
205 | cx.expr_usize(span, i), | |
206 | lambda]); | |
1a4d82fc JJ |
207 | |
208 | // last call doesn't need a try! | |
c34b1796 | 209 | let last = fields.len() - 1; |
1a4d82fc JJ |
210 | let call = if i != last { |
211 | cx.expr_try(span, call) | |
212 | } else { | |
7453a54e | 213 | cx.expr(span, ExprKind::Ret(Some(call))) |
1a4d82fc | 214 | }; |
970d7e83 LB |
215 | stmts.push(cx.stmt_expr(call)); |
216 | } | |
217 | ||
1a4d82fc JJ |
218 | // unit structs have no fields and need to return Ok() |
219 | if stmts.is_empty() { | |
7453a54e SL |
220 | let ok = cx.expr_ok(trait_span, cx.expr_tuple(trait_span, vec![])); |
221 | let ret_ok = cx.expr(trait_span, ExprKind::Ret(Some(ok))); | |
1a4d82fc JJ |
222 | stmts.push(cx.stmt_expr(ret_ok)); |
223 | } | |
224 | ||
225 | let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg); | |
226 | cx.expr_method_call(trait_span, | |
227 | encoder, | |
228 | cx.ident_of("emit_struct"), | |
5bcae85e SL |
229 | vec![cx.expr_str(trait_span, substr.type_ident.name.as_str()), |
230 | cx.expr_usize(trait_span, fields.len()), | |
231 | blk]) | |
970d7e83 LB |
232 | } |
233 | ||
234 | EnumMatching(idx, variant, ref fields) => { | |
235 | // We're not generating an AST that the borrow checker is expecting, | |
236 | // so we need to generate a unique local variable to take the | |
237 | // mutable loan out on, otherwise we get conflicts which don't | |
238 | // actually exist. | |
1a4d82fc JJ |
239 | let me = cx.stmt_let(trait_span, false, blkarg, encoder); |
240 | let encoder = cx.expr_ident(trait_span, blkarg); | |
970d7e83 | 241 | let emit_variant_arg = cx.ident_of("emit_enum_variant_arg"); |
1a4d82fc | 242 | let mut stmts = Vec::new(); |
9346a6ac | 243 | if !fields.is_empty() { |
c34b1796 AL |
244 | let last = fields.len() - 1; |
245 | for (i, &FieldInfo { ref self_, span, .. }) in fields.iter().enumerate() { | |
5bcae85e SL |
246 | let self_ref = cx.expr_addr_of(span, self_.clone()); |
247 | let enc = | |
248 | cx.expr_call(span, fn_path.clone(), vec![self_ref, blkencoder.clone()]); | |
c34b1796 | 249 | let lambda = cx.lambda_expr_1(span, enc, blkarg); |
5bcae85e SL |
250 | let call = cx.expr_method_call(span, |
251 | blkencoder.clone(), | |
c34b1796 | 252 | emit_variant_arg, |
5bcae85e | 253 | vec![cx.expr_usize(span, i), lambda]); |
c34b1796 AL |
254 | let call = if i != last { |
255 | cx.expr_try(span, call) | |
256 | } else { | |
7453a54e | 257 | cx.expr(span, ExprKind::Ret(Some(call))) |
c34b1796 AL |
258 | }; |
259 | stmts.push(cx.stmt_expr(call)); | |
260 | } | |
261 | } else { | |
7453a54e SL |
262 | let ok = cx.expr_ok(trait_span, cx.expr_tuple(trait_span, vec![])); |
263 | let ret_ok = cx.expr(trait_span, ExprKind::Ret(Some(ok))); | |
1a4d82fc JJ |
264 | stmts.push(cx.stmt_expr(ret_ok)); |
265 | } | |
266 | ||
267 | let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg); | |
c1a9b12d | 268 | let name = cx.expr_str(trait_span, variant.node.name.name.as_str()); |
5bcae85e SL |
269 | let call = cx.expr_method_call(trait_span, |
270 | blkencoder, | |
970d7e83 | 271 | cx.ident_of("emit_enum_variant"), |
5bcae85e SL |
272 | vec![name, |
273 | cx.expr_usize(trait_span, idx), | |
274 | cx.expr_usize(trait_span, fields.len()), | |
275 | blk]); | |
1a4d82fc JJ |
276 | let blk = cx.lambda_expr_1(trait_span, call, blkarg); |
277 | let ret = cx.expr_method_call(trait_span, | |
278 | encoder, | |
970d7e83 | 279 | cx.ident_of("emit_enum"), |
5bcae85e SL |
280 | vec![cx.expr_str(trait_span, |
281 | substr.type_ident.name.as_str()), | |
282 | blk]); | |
3157f602 | 283 | cx.expr_block(cx.block(trait_span, vec![me, cx.stmt_expr(ret)])) |
970d7e83 LB |
284 | } |
285 | ||
5bcae85e | 286 | _ => cx.bug("expected Struct or EnumMatching in derive(Encodable)"), |
970d7e83 LB |
287 | }; |
288 | } |