]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/global_allocator.rs
Remove `with_legacy_ctxt`
[rust.git] / src / libsyntax_ext / global_allocator.rs
1 use syntax::ast::{ItemKind, Mutability, Stmt, Ty, TyKind, Unsafety};
2 use syntax::ast::{self, Param, Attribute, Expr, FnHeader, Generics, Ident};
3 use syntax::attr::check_builtin_macro_attribute;
4 use syntax::ext::allocator::{AllocatorKind, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS};
5 use syntax::ext::base::{Annotatable, ExtCtxt};
6 use syntax::ptr::P;
7 use syntax::symbol::{kw, sym, Symbol};
8 use syntax_pos::Span;
9
10 pub fn expand(
11     ecx: &mut ExtCtxt<'_>,
12     _span: Span,
13     meta_item: &ast::MetaItem,
14     item: Annotatable,
15 ) -> Vec<Annotatable> {
16     check_builtin_macro_attribute(ecx, meta_item, sym::global_allocator);
17
18     let not_static = |item: Annotatable| {
19         ecx.parse_sess.span_diagnostic.span_err(item.span(), "allocators must be statics");
20         vec![item]
21     };
22     let item = match item {
23         Annotatable::Item(item) => match item.node {
24             ItemKind::Static(..) => item,
25             _ => return not_static(Annotatable::Item(item)),
26         }
27         _ => return not_static(item),
28     };
29
30     // Generate a bunch of new items using the AllocFnFactory
31     let span = ecx.with_def_site_ctxt(item.span);
32     let f = AllocFnFactory {
33         span,
34         kind: AllocatorKind::Global,
35         global: item.ident,
36         cx: ecx,
37     };
38
39     // Generate item statements for the allocator methods.
40     let stmts = ALLOCATOR_METHODS.iter().map(|method| f.allocator_fn(method)).collect();
41
42     // Generate anonymous constant serving as container for the allocator methods.
43     let const_ty = ecx.ty(span, TyKind::Tup(Vec::new()));
44     let const_body = ecx.expr_block(ecx.block(span, stmts));
45     let const_item =
46         ecx.item_const(span, Ident::new(kw::Underscore, span), const_ty, const_body);
47
48     // Return the original item and the new methods.
49     vec![Annotatable::Item(item), Annotatable::Item(const_item)]
50 }
51
52 struct AllocFnFactory<'a, 'b> {
53     span: Span,
54     kind: AllocatorKind,
55     global: Ident,
56     cx: &'b ExtCtxt<'a>,
57 }
58
59 impl AllocFnFactory<'_, '_> {
60     fn allocator_fn(&self, method: &AllocatorMethod) -> Stmt {
61         let mut abi_args = Vec::new();
62         let mut i = 0;
63         let ref mut mk = || {
64             let name = self.cx.ident_of(&format!("arg{}", i), self.span);
65             i += 1;
66             name
67         };
68         let args = method
69             .inputs
70             .iter()
71             .map(|ty| self.arg_ty(ty, &mut abi_args, mk))
72             .collect();
73         let result = self.call_allocator(method.name, args);
74         let (output_ty, output_expr) = self.ret_ty(&method.output, result);
75         let kind = ItemKind::Fn(
76             self.cx.fn_decl(abi_args, ast::FunctionRetTy::Ty(output_ty)),
77             FnHeader {
78                 unsafety: Unsafety::Unsafe,
79                 ..FnHeader::default()
80             },
81             Generics::default(),
82             self.cx.block_expr(output_expr),
83         );
84         let item = self.cx.item(
85             self.span,
86             self.cx.ident_of(&self.kind.fn_name(method.name), self.span),
87             self.attrs(),
88             kind,
89         );
90         self.cx.stmt_item(self.span, item)
91     }
92
93     fn call_allocator(&self, method: &str, mut args: Vec<P<Expr>>) -> P<Expr> {
94         let method = self.cx.std_path(&[
95             Symbol::intern("alloc"),
96             Symbol::intern("GlobalAlloc"),
97             Symbol::intern(method),
98         ]);
99         let method = self.cx.expr_path(self.cx.path(self.span, method));
100         let allocator = self.cx.path_ident(self.span, self.global);
101         let allocator = self.cx.expr_path(allocator);
102         let allocator = self.cx.expr_addr_of(self.span, allocator);
103         args.insert(0, allocator);
104
105         self.cx.expr_call(self.span, method, args)
106     }
107
108     fn attrs(&self) -> Vec<Attribute> {
109         let special = sym::rustc_std_internal_symbol;
110         let special = self.cx.meta_word(self.span, special);
111         vec![self.cx.attribute(special)]
112     }
113
114     fn arg_ty(
115         &self,
116         ty: &AllocatorTy,
117         args: &mut Vec<Param>,
118         ident: &mut dyn FnMut() -> Ident,
119     ) -> P<Expr> {
120         match *ty {
121             AllocatorTy::Layout => {
122                 let usize = self.cx.path_ident(self.span, Ident::new(sym::usize, self.span));
123                 let ty_usize = self.cx.ty_path(usize);
124                 let size = ident();
125                 let align = ident();
126                 args.push(self.cx.param(self.span, size, ty_usize.clone()));
127                 args.push(self.cx.param(self.span, align, ty_usize));
128
129                 let layout_new = self.cx.std_path(&[
130                     Symbol::intern("alloc"),
131                     Symbol::intern("Layout"),
132                     Symbol::intern("from_size_align_unchecked"),
133                 ]);
134                 let layout_new = self.cx.expr_path(self.cx.path(self.span, layout_new));
135                 let size = self.cx.expr_ident(self.span, size);
136                 let align = self.cx.expr_ident(self.span, align);
137                 let layout = self.cx.expr_call(self.span, layout_new, vec![size, align]);
138                 layout
139             }
140
141             AllocatorTy::Ptr => {
142                 let ident = ident();
143                 args.push(self.cx.param(self.span, ident, self.ptr_u8()));
144                 let arg = self.cx.expr_ident(self.span, ident);
145                 self.cx.expr_cast(self.span, arg, self.ptr_u8())
146             }
147
148             AllocatorTy::Usize => {
149                 let ident = ident();
150                 args.push(self.cx.param(self.span, ident, self.usize()));
151                 self.cx.expr_ident(self.span, ident)
152             }
153
154             AllocatorTy::ResultPtr | AllocatorTy::Unit => {
155                 panic!("can't convert AllocatorTy to an argument")
156             }
157         }
158     }
159
160     fn ret_ty(&self, ty: &AllocatorTy, expr: P<Expr>) -> (P<Ty>, P<Expr>) {
161         match *ty {
162             AllocatorTy::ResultPtr => {
163                 // We're creating:
164                 //
165                 //      #expr as *mut u8
166
167                 let expr = self.cx.expr_cast(self.span, expr, self.ptr_u8());
168                 (self.ptr_u8(), expr)
169             }
170
171             AllocatorTy::Unit => (self.cx.ty(self.span, TyKind::Tup(Vec::new())), expr),
172
173             AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
174                 panic!("can't convert `AllocatorTy` to an output")
175             }
176         }
177     }
178
179     fn usize(&self) -> P<Ty> {
180         let usize = self.cx.path_ident(self.span, Ident::new(sym::usize, self.span));
181         self.cx.ty_path(usize)
182     }
183
184     fn ptr_u8(&self) -> P<Ty> {
185         let u8 = self.cx.path_ident(self.span, Ident::new(sym::u8, self.span));
186         let ty_u8 = self.cx.ty_path(u8);
187         self.cx.ty_ptr(self.span, ty_u8, Mutability::Mutable)
188     }
189 }