]> git.lizzy.rs Git - rust.git/blob - src/librustc_allocator/expand.rs
Attempt to fix hygiene for global_allocator
[rust.git] / src / librustc_allocator / expand.rs
1 // Copyright 2016 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
11 #![allow(unused_imports, unused_variables, dead_code)]
12
13 use rustc::middle::allocator::AllocatorKind;
14 use rustc_errors;
15 use syntax::ast::{Attribute, Crate, LitKind, StrStyle};
16 use syntax::ast::{Arg, FnHeader, Generics, Mac, Mutability, Ty, Unsafety};
17 use syntax::ast::{self, Expr, Ident, Item, ItemKind, TyKind, VisibilityKind};
18 use syntax::attr;
19 use syntax::codemap::respan;
20 use syntax::codemap::{ExpnInfo, MacroAttribute};
21 use syntax::ext::base::ExtCtxt;
22 use syntax::ext::base::Resolver;
23 use syntax::ext::build::AstBuilder;
24 use syntax::ext::expand::ExpansionConfig;
25 use syntax::ext::hygiene::{self, Mark, SyntaxContext};
26 use syntax::fold::{self, Folder};
27 use syntax::parse::ParseSess;
28 use syntax::ptr::P;
29 use syntax::symbol::Symbol;
30 use syntax::util::small_vector::SmallVector;
31 use syntax_pos::{Span, DUMMY_SP};
32
33 use {AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS};
34
35 pub fn modify(
36     sess: &ParseSess,
37     resolver: &mut Resolver,
38     krate: Crate,
39     crate_name: String,
40     handler: &rustc_errors::Handler,
41 ) -> ast::Crate {
42     ExpandAllocatorDirectives {
43         handler,
44         sess,
45         resolver,
46         found: false,
47         crate_name: Some(crate_name),
48     }.fold_crate(krate)
49 }
50
51 struct ExpandAllocatorDirectives<'a> {
52     found: bool,
53     handler: &'a rustc_errors::Handler,
54     sess: &'a ParseSess,
55     resolver: &'a mut Resolver,
56     crate_name: Option<String>,
57 }
58
59 impl<'a> Folder for ExpandAllocatorDirectives<'a> {
60     fn fold_item(&mut self, item: P<Item>) -> SmallVector<P<Item>> {
61         let name = if attr::contains_name(&item.attrs, "global_allocator") {
62             "global_allocator"
63         } else {
64             return fold::noop_fold_item(item, self);
65         };
66         match item.node {
67             ItemKind::Static(..) => {}
68             _ => {
69                 self.handler
70                     .span_err(item.span, "allocators must be statics");
71                 return SmallVector::one(item);
72             }
73         }
74
75         if self.found {
76             self.handler.span_err(
77                 item.span,
78                 "cannot define more than one \
79                  #[global_allocator]",
80             );
81             return SmallVector::one(item);
82         }
83         self.found = true;
84
85         // Create a fresh Mark for the new macro expansion we are about to do
86         let mark = Mark::fresh(Mark::root());
87         mark.set_expn_info(ExpnInfo {
88             call_site: item.span,
89             def_site: None,
90             format: MacroAttribute(Symbol::intern(name)),
91             allow_internal_unstable: true,
92             allow_internal_unsafe: false,
93             edition: hygiene::default_edition(),
94         });
95
96         // Tie the span to the macro expansion info we just created
97         let span = item.span.with_ctxt(SyntaxContext::empty().apply_mark(mark));
98
99         // Create an expansion config
100         let ecfg = ExpansionConfig::default(self.crate_name.take().unwrap());
101
102         // Generate a bunch of new items using the AllocFnFactory
103         let mut f = AllocFnFactory {
104             span,
105             kind: AllocatorKind::Global,
106             global: item.ident,
107             core: Ident::with_empty_ctxt(Symbol::gensym("core")),
108             cx: ExtCtxt::new(self.sess, ecfg, self.resolver),
109         };
110
111         let extcore = {
112             let extcore = f.cx.item_extern_crate(item.span, f.core);
113             f.cx.monotonic_expander().fold_item(extcore).pop().unwrap()
114         };
115
116         let mut ret = SmallVector::new();
117         ret.push(item);
118         ret.push(extcore);
119         ret.extend(ALLOCATOR_METHODS.iter().map(|method| {
120             let method = f.allocator_fn(method);
121             f.cx.monotonic_expander().fold_item(method).pop().unwrap()
122         }));
123         return ret;
124     }
125
126     fn fold_mac(&mut self, mac: Mac) -> Mac {
127         fold::noop_fold_mac(mac, self)
128     }
129 }
130
131 struct AllocFnFactory<'a> {
132     span: Span,
133     kind: AllocatorKind,
134     global: Ident,
135     core: Ident,
136     cx: ExtCtxt<'a>,
137 }
138
139 impl<'a> AllocFnFactory<'a> {
140     fn allocator_fn(&self, method: &AllocatorMethod) -> P<Item> {
141         let mut abi_args = Vec::new();
142         let mut i = 0;
143         let ref mut mk = || {
144             let name = Ident::from_str(&format!("arg{}", i));
145             i += 1;
146             name
147         };
148         let args = method
149             .inputs
150             .iter()
151             .map(|ty| self.arg_ty(ty, &mut abi_args, mk))
152             .collect();
153         let result = self.call_allocator(method.name, args);
154         let (output_ty, output_expr) = self.ret_ty(&method.output, result);
155         let kind = ItemKind::Fn(
156             self.cx.fn_decl(abi_args, ast::FunctionRetTy::Ty(output_ty)),
157             FnHeader {
158                 unsafety: Unsafety::Unsafe,
159                 ..FnHeader::default()
160             },
161             Generics::default(),
162             self.cx.block_expr(output_expr),
163         );
164         self.cx.item(
165             self.span,
166             Ident::from_str(&self.kind.fn_name(method.name)),
167             self.attrs(),
168             kind,
169         )
170     }
171
172     fn call_allocator(&self, method: &str, mut args: Vec<P<Expr>>) -> P<Expr> {
173         let method = self.cx.path(
174             self.span,
175             vec![
176                 Ident::from_str("self"),
177                 self.core,
178                 Ident::from_str("alloc"),
179                 Ident::from_str("GlobalAlloc"),
180                 Ident::from_str(method),
181             ],
182         );
183         let method = self.cx.expr_path(method);
184         let allocator = self.cx.path_ident(self.span, self.global);
185         let allocator = self.cx.expr_path(allocator);
186         let allocator = self.cx.expr_addr_of(self.span, allocator);
187         args.insert(0, allocator);
188
189         self.cx.expr_call(self.span, method, args)
190     }
191
192     fn attrs(&self) -> Vec<Attribute> {
193         let key = Symbol::intern("linkage");
194         let value = LitKind::Str(Symbol::intern("external"), StrStyle::Cooked);
195         let linkage = self.cx.meta_name_value(self.span, key, value);
196
197         let no_mangle = Symbol::intern("no_mangle");
198         let no_mangle = self.cx.meta_word(self.span, no_mangle);
199
200         let special = Symbol::intern("rustc_std_internal_symbol");
201         let special = self.cx.meta_word(self.span, special);
202         vec![
203             self.cx.attribute(self.span, linkage),
204             self.cx.attribute(self.span, no_mangle),
205             self.cx.attribute(self.span, special),
206         ]
207     }
208
209     fn arg_ty(
210         &self,
211         ty: &AllocatorTy,
212         args: &mut Vec<Arg>,
213         ident: &mut FnMut() -> Ident,
214     ) -> P<Expr> {
215         match *ty {
216             AllocatorTy::Layout => {
217                 let usize = self.cx.path_ident(self.span, Ident::from_str("usize"));
218                 let ty_usize = self.cx.ty_path(usize);
219                 let size = ident();
220                 let align = ident();
221                 args.push(self.cx.arg(self.span, size, ty_usize.clone()));
222                 args.push(self.cx.arg(self.span, align, ty_usize));
223
224                 let layout_new = self.cx.path(
225                     self.span,
226                     vec![
227                         Ident::from_str("self"),
228                         self.core,
229                         Ident::from_str("alloc"),
230                         Ident::from_str("Layout"),
231                         Ident::from_str("from_size_align_unchecked"),
232                     ],
233                 );
234                 let layout_new = self.cx.expr_path(layout_new);
235                 let size = self.cx.expr_ident(self.span, size);
236                 let align = self.cx.expr_ident(self.span, align);
237                 let layout = self.cx.expr_call(self.span, layout_new, vec![size, align]);
238                 layout
239             }
240
241             AllocatorTy::Ptr => {
242                 let ident = ident();
243                 args.push(self.cx.arg(self.span, ident, self.ptr_u8()));
244                 let arg = self.cx.expr_ident(self.span, ident);
245                 self.cx.expr_cast(self.span, arg, self.ptr_u8())
246             }
247
248             AllocatorTy::Usize => {
249                 let ident = ident();
250                 args.push(self.cx.arg(self.span, ident, self.usize()));
251                 self.cx.expr_ident(self.span, ident)
252             }
253
254             AllocatorTy::ResultPtr | AllocatorTy::Unit => {
255                 panic!("can't convert AllocatorTy to an argument")
256             }
257         }
258     }
259
260     fn ret_ty(&self, ty: &AllocatorTy, expr: P<Expr>) -> (P<Ty>, P<Expr>) {
261         match *ty {
262             AllocatorTy::ResultPtr => {
263                 // We're creating:
264                 //
265                 //      #expr as *mut u8
266
267                 let expr = self.cx.expr_cast(self.span, expr, self.ptr_u8());
268                 (self.ptr_u8(), expr)
269             }
270
271             AllocatorTy::Unit => (self.cx.ty(self.span, TyKind::Tup(Vec::new())), expr),
272
273             AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
274                 panic!("can't convert AllocatorTy to an output")
275             }
276         }
277     }
278
279     fn usize(&self) -> P<Ty> {
280         let usize = self.cx.path_ident(self.span, Ident::from_str("usize"));
281         self.cx.ty_path(usize)
282     }
283
284     fn ptr_u8(&self) -> P<Ty> {
285         let u8 = self.cx.path_ident(self.span, Ident::from_str("u8"));
286         let ty_u8 = self.cx.ty_path(u8);
287         self.cx.ty_ptr(self.span, ty_u8, Mutability::Mutable)
288     }
289 }