]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_builtin_macros/src/deriving/encodable.rs
New upstream version 1.64.0+dfsg1
[rustc.git] / compiler / rustc_builtin_macros / src / deriving / encodable.rs
CommitLineData
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 88use crate::deriving::generic::ty::*;
dfeec247
XL
89use crate::deriving::generic::*;
90use crate::deriving::pathvec_std;
9cc50fc6 91
064997fb 92use rustc_ast::{ExprKind, MetaItem, Mutability};
dfeec247 93use rustc_expand::base::{Annotatable, ExtCtxt};
3dfed10e 94use rustc_span::symbol::{sym, Ident, Symbol};
dfeec247 95use rustc_span::Span;
970d7e83 96
dfeec247
XL
97pub fn expand_deriving_rustc_encodable(
98 cx: &mut ExtCtxt<'_>,
99 span: Span,
100 mitem: &MetaItem,
101 item: &Annotatable,
102 push: &mut dyn FnMut(Annotatable),
103) {
3dfed10e
XL
104 let krate = sym::rustc_serialize;
105 let typaram = sym::__S;
54a0048b 106
970d7e83 107 let trait_def = TraitDef {
3b2f2976 108 span,
1a4d82fc 109 attributes: Vec::new(),
064997fb 110 path: Path::new_(vec![krate, sym::Encodable], vec![], PathKind::Global),
1a4d82fc 111 additional_bounds: Vec::new(),
3dfed10e 112 generics: Bounds::empty(),
9e0c209e 113 supports_unions: false,
dfeec247 114 methods: vec![MethodDef {
3dfed10e
XL
115 name: sym::encode,
116 generics: Bounds {
dfeec247
XL
117 bounds: vec![(
118 typaram,
064997fb 119 vec![Path::new_(vec![krate, sym::Encoder], vec![], PathKind::Global)],
dfeec247
XL
120 )],
121 },
064997fb
FG
122 explicit_self: true,
123 nonself_args: vec![(
124 Ref(Box::new(Path(Path::new_local(typaram))), Mutability::Mut),
136023e0 125 sym::s,
dfeec247 126 )],
064997fb 127 ret_ty: Path(Path::new_(
3dfed10e 128 pathvec_std!(result::Result),
dfeec247 129 vec![
064997fb
FG
130 Box::new(Unit),
131 Box::new(Path(Path::new_(vec![typaram, sym::Error], vec![], PathKind::Local))),
dfeec247
XL
132 ],
133 PathKind::Std,
134 )),
135 attributes: Vec::new(),
dfeec247
XL
136 unify_fieldless_variants: false,
137 combine_substructure: combine_substructure(Box::new(|a, b, c| {
138 encodable_substructure(a, b, c, krate)
139 })),
140 }],
85aaf69f 141 associated_types: Vec::new(),
970d7e83
LB
142 };
143
62682a34 144 trait_def.expand(cx, mitem, item, push)
970d7e83
LB
145}
146
dfeec247
XL
147fn encodable_substructure(
148 cx: &mut ExtCtxt<'_>,
149 trait_span: Span,
150 substr: &Substructure<'_>,
3dfed10e 151 krate: Symbol,
064997fb
FG
152) -> BlockOrExpr {
153 let encoder = substr.nonselflike_args[0].clone();
970d7e83 154 // throw an underscore in front to suppress unused variable warnings
3dfed10e 155 let blkarg = Ident::new(sym::_e, trait_span);
1a4d82fc 156 let blkencoder = cx.expr_ident(trait_span, blkarg);
dfeec247
XL
157 let fn_path = cx.expr_path(cx.path_global(
158 trait_span,
159 vec![
3dfed10e
XL
160 Ident::new(krate, trait_span),
161 Ident::new(sym::Encodable, trait_span),
162 Ident::new(sym::encode, trait_span),
dfeec247
XL
163 ],
164 ));
970d7e83 165
ba9703b0 166 match *substr.fields {
7453a54e 167 Struct(_, ref fields) => {
6a06907d
XL
168 let fn_emit_struct_field_path =
169 cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_struct_field]);
1a4d82fc 170 let mut stmts = Vec::new();
064997fb 171 for (i, &FieldInfo { name, ref self_expr, span, .. }) in fields.iter().enumerate() {
1a4d82fc 172 let name = match name {
476ff2be
SL
173 Some(id) => id.name,
174 None => Symbol::intern(&format!("_field{}", i)),
970d7e83 175 };
064997fb 176 let self_ref = cx.expr_addr_of(span, self_expr.clone());
a7813a04 177 let enc = cx.expr_call(span, fn_path.clone(), vec![self_ref, blkencoder.clone()]);
476ff2be 178 let lambda = cx.lambda1(span, enc, blkarg);
6a06907d 179 let call = cx.expr_call_global(
dfeec247 180 span,
6a06907d
XL
181 fn_emit_struct_field_path.clone(),
182 vec![
183 blkencoder.clone(),
184 cx.expr_str(span, name),
185 cx.expr_usize(span, i),
186 lambda,
187 ],
dfeec247 188 );
1a4d82fc
JJ
189
190 // last call doesn't need a try!
c34b1796 191 let last = fields.len() - 1;
1a4d82fc
JJ
192 let call = if i != last {
193 cx.expr_try(span, call)
194 } else {
7453a54e 195 cx.expr(span, ExprKind::Ret(Some(call)))
1a4d82fc 196 };
2c00a5a8 197
94b46f34 198 let stmt = cx.stmt_expr(call);
2c00a5a8 199 stmts.push(stmt);
970d7e83
LB
200 }
201
1a4d82fc 202 // unit structs have no fields and need to return Ok()
0531ce1d 203 let blk = if stmts.is_empty() {
7453a54e 204 let ok = cx.expr_ok(trait_span, cx.expr_tuple(trait_span, vec![]));
0531ce1d
XL
205 cx.lambda1(trait_span, ok, blkarg)
206 } else {
207 cx.lambda_stmts_1(trait_span, stmts, blkarg)
208 };
1a4d82fc 209
6a06907d
XL
210 let fn_emit_struct_path =
211 cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_struct]);
212
064997fb 213 let expr = cx.expr_call_global(
dfeec247 214 trait_span,
6a06907d 215 fn_emit_struct_path,
dfeec247 216 vec![
6a06907d 217 encoder,
dfeec247
XL
218 cx.expr_str(trait_span, substr.type_ident.name),
219 cx.expr_usize(trait_span, fields.len()),
220 blk,
221 ],
064997fb
FG
222 );
223 BlockOrExpr::new_expr(expr)
970d7e83
LB
224 }
225
041b39d2 226 EnumMatching(idx, _, variant, ref fields) => {
970d7e83
LB
227 // We're not generating an AST that the borrow checker is expecting,
228 // so we need to generate a unique local variable to take the
229 // mutable loan out on, otherwise we get conflicts which don't
230 // actually exist.
1a4d82fc
JJ
231 let me = cx.stmt_let(trait_span, false, blkarg, encoder);
232 let encoder = cx.expr_ident(trait_span, blkarg);
6a06907d
XL
233
234 let fn_emit_enum_variant_arg_path: Vec<_> =
235 cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_enum_variant_arg]);
236
1a4d82fc 237 let mut stmts = Vec::new();
9346a6ac 238 if !fields.is_empty() {
c34b1796 239 let last = fields.len() - 1;
064997fb
FG
240 for (i, &FieldInfo { ref self_expr, span, .. }) in fields.iter().enumerate() {
241 let self_ref = cx.expr_addr_of(span, self_expr.clone());
5bcae85e
SL
242 let enc =
243 cx.expr_call(span, fn_path.clone(), vec![self_ref, blkencoder.clone()]);
476ff2be 244 let lambda = cx.lambda1(span, enc, blkarg);
6a06907d
XL
245
246 let call = cx.expr_call_global(
dfeec247 247 span,
6a06907d
XL
248 fn_emit_enum_variant_arg_path.clone(),
249 vec![blkencoder.clone(), cx.expr_usize(span, i), lambda],
dfeec247 250 );
c34b1796
AL
251 let call = if i != last {
252 cx.expr_try(span, call)
253 } else {
7453a54e 254 cx.expr(span, ExprKind::Ret(Some(call)))
c34b1796
AL
255 };
256 stmts.push(cx.stmt_expr(call));
257 }
258 } else {
7453a54e
SL
259 let ok = cx.expr_ok(trait_span, cx.expr_tuple(trait_span, vec![]));
260 let ret_ok = cx.expr(trait_span, ExprKind::Ret(Some(ok)));
1a4d82fc
JJ
261 stmts.push(cx.stmt_expr(ret_ok));
262 }
263
264 let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg);
e1599b0c 265 let name = cx.expr_str(trait_span, variant.ident.name);
6a06907d
XL
266
267 let fn_emit_enum_variant_path: Vec<_> =
268 cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_enum_variant]);
269
270 let call = cx.expr_call_global(
dfeec247 271 trait_span,
6a06907d 272 fn_emit_enum_variant_path,
dfeec247 273 vec![
6a06907d 274 blkencoder,
dfeec247
XL
275 name,
276 cx.expr_usize(trait_span, idx),
277 cx.expr_usize(trait_span, fields.len()),
278 blk,
279 ],
280 );
6a06907d 281
476ff2be 282 let blk = cx.lambda1(trait_span, call, blkarg);
6a06907d
XL
283 let fn_emit_enum_path: Vec<_> =
284 cx.def_site_path(&[sym::rustc_serialize, sym::Encoder, sym::emit_enum]);
064997fb 285 let expr = cx.expr_call_global(
dfeec247 286 trait_span,
6a06907d
XL
287 fn_emit_enum_path,
288 vec![encoder, cx.expr_str(trait_span, substr.type_ident.name), blk],
dfeec247 289 );
064997fb 290 BlockOrExpr::new_mixed(vec![me], Some(expr))
970d7e83
LB
291 }
292
5bcae85e 293 _ => cx.bug("expected Struct or EnumMatching in derive(Encodable)"),
ba9703b0 294 }
970d7e83 295}