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