rust/compiler/rustc_builtin_macros/src/global_allocator.rs

172 lines
6.4 KiB
Rust
Raw Normal View History

2019-10-08 14:15:26 +02:00
use crate::util::check_builtin_macro_attribute;
use rustc_ast::expand::allocator::{
AllocatorKind, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS,
};
use rustc_ast::ptr::P;
2020-04-27 23:26:11 +05:30
use rustc_ast::{self as ast, Attribute, Expr, FnHeader, FnSig, Generics, Param};
use rustc_ast::{ItemKind, Mutability, Stmt, Ty, TyKind, Unsafe};
use rustc_expand::base::{Annotatable, ExtCtxt};
2020-04-19 13:00:18 +02:00
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::Span;
pub fn expand(
ecx: &mut ExtCtxt<'_>,
_span: Span,
meta_item: &ast::MetaItem,
item: Annotatable,
) -> Vec<Annotatable> {
check_builtin_macro_attribute(ecx, meta_item, sym::global_allocator);
let not_static = |item: Annotatable| {
ecx.sess.parse_sess.span_diagnostic.span_err(item.span(), "allocators must be statics");
vec![item]
};
let item = match item {
2019-09-26 17:51:36 +01:00
Annotatable::Item(item) => match item.kind {
ItemKind::Static(..) => item,
_ => return not_static(Annotatable::Item(item)),
2019-12-22 17:42:04 -05:00
},
_ => return not_static(item),
};
// Generate a bunch of new items using the AllocFnFactory
2019-09-14 21:17:11 +01:00
let span = ecx.with_def_site_ctxt(item.span);
2019-12-22 17:42:04 -05:00
let f = AllocFnFactory { span, kind: AllocatorKind::Global, global: item.ident, cx: ecx };
// Generate item statements for the allocator methods.
let stmts = ALLOCATOR_METHODS.iter().map(|method| f.allocator_fn(method)).collect();
// Generate anonymous constant serving as container for the allocator methods.
let const_ty = ecx.ty(span, TyKind::Tup(Vec::new()));
let const_body = ecx.expr_block(ecx.block(span, stmts));
2019-12-22 17:42:04 -05:00
let const_item = ecx.item_const(span, Ident::new(kw::Underscore, span), const_ty, const_body);
2018-06-05 19:24:10 -05:00
// Return the original item and the new methods.
vec![Annotatable::Item(item), Annotatable::Item(const_item)]
}
struct AllocFnFactory<'a, 'b> {
span: Span,
kind: AllocatorKind,
global: Ident,
cx: &'b ExtCtxt<'a>,
}
impl AllocFnFactory<'_, '_> {
fn allocator_fn(&self, method: &AllocatorMethod) -> Stmt {
let mut abi_args = Vec::new();
let mut i = 0;
let mut mk = || {
let name = Ident::from_str_and_span(&format!("arg{}", i), self.span);
i += 1;
name
};
let args = method.inputs.iter().map(|ty| self.arg_ty(ty, &mut abi_args, &mut mk)).collect();
let result = self.call_allocator(method.name, args);
let (output_ty, output_expr) = self.ret_ty(&method.output, result);
2020-02-15 12:10:59 +09:00
let decl = self.cx.fn_decl(abi_args, ast::FnRetTy::Ty(output_ty));
let header = FnHeader { unsafety: Unsafe::Yes(self.span), ..FnHeader::default() };
Use smaller def span for functions Currently, the def span of a funtion encompasses the entire function signature and body. However, this is usually unnecessarily verbose - when we are pointing at an entire function in a diagnostic, we almost always want to point at the signature. The actual contents of the body tends to be irrelevant to the diagnostic we are emitting, and just takes up additional screen space. This commit changes the `def_span` of all function items (freestanding functions, `impl`-block methods, and `trait`-block methods) to be the span of the signature. For example, the function ```rust pub fn foo<T>(val: T) -> T { val } ``` now has a `def_span` corresponding to `pub fn foo<T>(val: T) -> T` (everything before the opening curly brace). Trait methods without a body have a `def_span` which includes the trailing semicolon. For example: ```rust trait Foo { fn bar(); }``` the function definition `Foo::bar` has a `def_span` of `fn bar();` This makes our diagnostic output much shorter, and emphasizes information that is relevant to whatever diagnostic we are reporting. We continue to use the full span (including the body) in a few of places: * MIR building uses the full span when building source scopes. * 'Outlives suggestions' use the full span to sort the diagnostics being emitted. * The `#[rustc_on_unimplemented(enclosing_scope="in this scope")]` attribute points the entire scope body. * The 'unconditional recursion' lint uses the full span to show additional context for the recursive call. All of these cases work only with local items, so we don't need to add anything extra to crate metadata.
2020-08-12 17:02:14 -04:00
let sig = FnSig { decl, header, span: self.span };
let block = Some(self.cx.block_expr(output_expr));
let kind = ItemKind::Fn(ast::Defaultness::Final, sig, Generics::default(), block);
let item = self.cx.item(
2018-04-13 15:58:16 -05:00
self.span,
Ident::from_str_and_span(&self.kind.fn_name(method.name), self.span),
2018-04-13 15:58:16 -05:00
self.attrs(),
kind,
);
self.cx.stmt_item(self.span, item)
}
fn call_allocator(&self, method: Symbol, mut args: Vec<P<Expr>>) -> P<Expr> {
let method = self.cx.std_path(&[sym::alloc, sym::GlobalAlloc, method]);
let method = self.cx.expr_path(self.cx.path(self.span, method));
let allocator = self.cx.path_ident(self.span, self.global);
let allocator = self.cx.expr_path(allocator);
let allocator = self.cx.expr_addr_of(self.span, allocator);
args.insert(0, allocator);
self.cx.expr_call(self.span, method, args)
}
fn attrs(&self) -> Vec<Attribute> {
let special = sym::rustc_std_internal_symbol;
let special = self.cx.meta_word(self.span, special);
vec![self.cx.attribute(special)]
}
2018-04-13 15:58:16 -05:00
fn arg_ty(
&self,
ty: &AllocatorTy,
args: &mut Vec<Param>,
ident: &mut dyn FnMut() -> Ident,
2018-04-13 15:58:16 -05:00
) -> P<Expr> {
match *ty {
AllocatorTy::Layout => {
2019-09-14 21:16:51 +01:00
let usize = self.cx.path_ident(self.span, Ident::new(sym::usize, self.span));
let ty_usize = self.cx.ty_path(usize);
let size = ident();
let align = ident();
args.push(self.cx.param(self.span, size, ty_usize.clone()));
args.push(self.cx.param(self.span, align, ty_usize));
let layout_new =
self.cx.std_path(&[sym::alloc, sym::Layout, sym::from_size_align_unchecked]);
let layout_new = self.cx.expr_path(self.cx.path(self.span, layout_new));
let size = self.cx.expr_ident(self.span, size);
let align = self.cx.expr_ident(self.span, align);
2018-04-13 15:58:16 -05:00
let layout = self.cx.expr_call(self.span, layout_new, vec![size, align]);
layout
}
AllocatorTy::Ptr => {
let ident = ident();
args.push(self.cx.param(self.span, ident, self.ptr_u8()));
let arg = self.cx.expr_ident(self.span, ident);
self.cx.expr_cast(self.span, arg, self.ptr_u8())
}
AllocatorTy::Usize => {
let ident = ident();
args.push(self.cx.param(self.span, ident, self.usize()));
self.cx.expr_ident(self.span, ident)
}
2018-04-21 15:16:59 -07:00
AllocatorTy::ResultPtr | AllocatorTy::Unit => {
panic!("can't convert AllocatorTy to an argument")
}
}
}
fn ret_ty(&self, ty: &AllocatorTy, expr: P<Expr>) -> (P<Ty>, P<Expr>) {
match *ty {
AllocatorTy::ResultPtr => {
// We're creating:
//
// #expr as *mut u8
let expr = self.cx.expr_cast(self.span, expr, self.ptr_u8());
(self.ptr_u8(), expr)
}
2018-04-13 15:58:16 -05:00
AllocatorTy::Unit => (self.cx.ty(self.span, TyKind::Tup(Vec::new())), expr),
2018-04-13 15:58:16 -05:00
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
panic!("can't convert `AllocatorTy` to an output")
}
}
}
fn usize(&self) -> P<Ty> {
2019-09-14 21:16:51 +01:00
let usize = self.cx.path_ident(self.span, Ident::new(sym::usize, self.span));
self.cx.ty_path(usize)
}
fn ptr_u8(&self) -> P<Ty> {
2019-09-14 21:16:51 +01:00
let u8 = self.cx.path_ident(self.span, Ident::new(sym::u8, self.span));
let ty_u8 = self.cx.ty_path(u8);
self.cx.ty_ptr(self.span, ty_u8, Mutability::Mut)
}
}