]>
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, |
1a4d82fc | 141 | methods: vec!( |
970d7e83 LB |
142 | MethodDef { |
143 | name: "encode", | |
1a4d82fc JJ |
144 | generics: LifetimeBounds { |
145 | lifetimes: Vec::new(), | |
54a0048b SL |
146 | bounds: vec![(typaram, |
147 | vec![Path::new_(vec![krate, "Encoder"], None, vec!(), true)])] | |
1a4d82fc JJ |
148 | }, |
149 | explicit_self: borrowed_explicit_self(), | |
54a0048b | 150 | args: vec!(Ptr(Box::new(Literal(Path::new_local(typaram))), |
a7813a04 | 151 | Borrowed(None, Mutability::Mutable))), |
1a4d82fc | 152 | ret_ty: Literal(Path::new_( |
85aaf69f | 153 | pathvec_std!(cx, core::result::Result), |
1a4d82fc | 154 | None, |
d9579d0f | 155 | vec!(Box::new(Tuple(Vec::new())), Box::new(Literal(Path::new_( |
54a0048b | 156 | vec![typaram, "Error"], None, vec![], false |
d9579d0f | 157 | )))), |
1a4d82fc JJ |
158 | true |
159 | )), | |
160 | attributes: Vec::new(), | |
62682a34 | 161 | is_unsafe: false, |
a7813a04 | 162 | unify_fieldless_variants: false, |
c34b1796 | 163 | combine_substructure: combine_substructure(Box::new(|a, b, c| { |
a7813a04 | 164 | encodable_substructure(a, b, c, krate) |
c34b1796 | 165 | })), |
85aaf69f SL |
166 | } |
167 | ), | |
168 | associated_types: Vec::new(), | |
970d7e83 LB |
169 | }; |
170 | ||
62682a34 | 171 | trait_def.expand(cx, mitem, item, push) |
970d7e83 LB |
172 | } |
173 | ||
5bcae85e SL |
174 | fn encodable_substructure(cx: &mut ExtCtxt, |
175 | trait_span: Span, | |
176 | substr: &Substructure, | |
177 | krate: &'static str) | |
178 | -> P<Expr> { | |
1a4d82fc | 179 | let encoder = substr.nonself_args[0].clone(); |
970d7e83 LB |
180 | // throw an underscore in front to suppress unused variable warnings |
181 | let blkarg = cx.ident_of("_e"); | |
1a4d82fc | 182 | let blkencoder = cx.expr_ident(trait_span, blkarg); |
5bcae85e SL |
183 | let fn_path = cx.expr_path(cx.path_global(trait_span, |
184 | vec![cx.ident_of(krate), | |
185 | cx.ident_of("Encodable"), | |
186 | cx.ident_of("encode")])); | |
970d7e83 LB |
187 | |
188 | return match *substr.fields { | |
7453a54e | 189 | Struct(_, ref fields) => { |
970d7e83 | 190 | let emit_struct_field = cx.ident_of("emit_struct_field"); |
1a4d82fc | 191 | let mut stmts = Vec::new(); |
5bcae85e | 192 | for (i, &FieldInfo { name, ref self_, span, .. }) in fields.iter().enumerate() { |
1a4d82fc | 193 | let name = match name { |
c1a9b12d | 194 | Some(id) => id.name.as_str(), |
5bcae85e | 195 | None => token::intern_and_get_ident(&format!("_field{}", i)), |
970d7e83 | 196 | }; |
a7813a04 XL |
197 | let self_ref = cx.expr_addr_of(span, self_.clone()); |
198 | let enc = cx.expr_call(span, fn_path.clone(), vec![self_ref, blkencoder.clone()]); | |
970d7e83 | 199 | let lambda = cx.lambda_expr_1(span, enc, blkarg); |
5bcae85e SL |
200 | let call = cx.expr_method_call(span, |
201 | blkencoder.clone(), | |
970d7e83 | 202 | emit_struct_field, |
5bcae85e SL |
203 | vec![cx.expr_str(span, name), |
204 | cx.expr_usize(span, i), | |
205 | lambda]); | |
1a4d82fc JJ |
206 | |
207 | // last call doesn't need a try! | |
c34b1796 | 208 | let last = fields.len() - 1; |
1a4d82fc JJ |
209 | let call = if i != last { |
210 | cx.expr_try(span, call) | |
211 | } else { | |
7453a54e | 212 | cx.expr(span, ExprKind::Ret(Some(call))) |
1a4d82fc | 213 | }; |
970d7e83 LB |
214 | stmts.push(cx.stmt_expr(call)); |
215 | } | |
216 | ||
1a4d82fc JJ |
217 | // unit structs have no fields and need to return Ok() |
218 | if stmts.is_empty() { | |
7453a54e SL |
219 | let ok = cx.expr_ok(trait_span, cx.expr_tuple(trait_span, vec![])); |
220 | let ret_ok = cx.expr(trait_span, ExprKind::Ret(Some(ok))); | |
1a4d82fc JJ |
221 | stmts.push(cx.stmt_expr(ret_ok)); |
222 | } | |
223 | ||
224 | let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg); | |
225 | cx.expr_method_call(trait_span, | |
226 | encoder, | |
227 | cx.ident_of("emit_struct"), | |
5bcae85e SL |
228 | vec![cx.expr_str(trait_span, substr.type_ident.name.as_str()), |
229 | cx.expr_usize(trait_span, fields.len()), | |
230 | blk]) | |
970d7e83 LB |
231 | } |
232 | ||
233 | EnumMatching(idx, variant, ref fields) => { | |
234 | // We're not generating an AST that the borrow checker is expecting, | |
235 | // so we need to generate a unique local variable to take the | |
236 | // mutable loan out on, otherwise we get conflicts which don't | |
237 | // actually exist. | |
1a4d82fc JJ |
238 | let me = cx.stmt_let(trait_span, false, blkarg, encoder); |
239 | let encoder = cx.expr_ident(trait_span, blkarg); | |
970d7e83 | 240 | let emit_variant_arg = cx.ident_of("emit_enum_variant_arg"); |
1a4d82fc | 241 | let mut stmts = Vec::new(); |
9346a6ac | 242 | if !fields.is_empty() { |
c34b1796 AL |
243 | let last = fields.len() - 1; |
244 | for (i, &FieldInfo { ref self_, span, .. }) in fields.iter().enumerate() { | |
5bcae85e SL |
245 | let self_ref = cx.expr_addr_of(span, self_.clone()); |
246 | let enc = | |
247 | cx.expr_call(span, fn_path.clone(), vec![self_ref, blkencoder.clone()]); | |
c34b1796 | 248 | let lambda = cx.lambda_expr_1(span, enc, blkarg); |
5bcae85e SL |
249 | let call = cx.expr_method_call(span, |
250 | blkencoder.clone(), | |
c34b1796 | 251 | emit_variant_arg, |
5bcae85e | 252 | vec![cx.expr_usize(span, i), lambda]); |
c34b1796 AL |
253 | let call = if i != last { |
254 | cx.expr_try(span, call) | |
255 | } else { | |
7453a54e | 256 | cx.expr(span, ExprKind::Ret(Some(call))) |
c34b1796 AL |
257 | }; |
258 | stmts.push(cx.stmt_expr(call)); | |
259 | } | |
260 | } else { | |
7453a54e SL |
261 | let ok = cx.expr_ok(trait_span, cx.expr_tuple(trait_span, vec![])); |
262 | let ret_ok = cx.expr(trait_span, ExprKind::Ret(Some(ok))); | |
1a4d82fc JJ |
263 | stmts.push(cx.stmt_expr(ret_ok)); |
264 | } | |
265 | ||
266 | let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg); | |
c1a9b12d | 267 | let name = cx.expr_str(trait_span, variant.node.name.name.as_str()); |
5bcae85e SL |
268 | let call = cx.expr_method_call(trait_span, |
269 | blkencoder, | |
970d7e83 | 270 | cx.ident_of("emit_enum_variant"), |
5bcae85e SL |
271 | vec![name, |
272 | cx.expr_usize(trait_span, idx), | |
273 | cx.expr_usize(trait_span, fields.len()), | |
274 | blk]); | |
1a4d82fc JJ |
275 | let blk = cx.lambda_expr_1(trait_span, call, blkarg); |
276 | let ret = cx.expr_method_call(trait_span, | |
277 | encoder, | |
970d7e83 | 278 | cx.ident_of("emit_enum"), |
5bcae85e SL |
279 | vec![cx.expr_str(trait_span, |
280 | substr.type_ident.name.as_str()), | |
281 | blk]); | |
3157f602 | 282 | cx.expr_block(cx.block(trait_span, vec![me, cx.stmt_expr(ret)])) |
970d7e83 LB |
283 | } |
284 | ||
5bcae85e | 285 | _ => cx.bug("expected Struct or EnumMatching in derive(Encodable)"), |
970d7e83 LB |
286 | }; |
287 | } |