Rollup merge of #62735 - petrochenkov:galloc, r=alexcrichton
Turn `#[global_allocator]` into a regular attribute macro It was a 99% macro with exception of some diagnostic details. As a result of the change, `#[global_allocator]` now works in nested modules and even in nameless blocks. Fixes https://github.com/rust-lang/rust/issues/44113 Fixes https://github.com/rust-lang/rust/issues/58072
This commit is contained in:
commit
e1de70b045
18
Cargo.lock
18
Cargo.lock
@ -2751,20 +2751,6 @@ dependencies = [
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_allocator"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc 0.0.0",
|
||||
"rustc_data_structures 0.0.0",
|
||||
"rustc_errors 0.0.0",
|
||||
"rustc_target 0.0.0",
|
||||
"smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntax 0.0.0",
|
||||
"syntax_pos 0.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_apfloat"
|
||||
version = "0.0.0"
|
||||
@ -2822,7 +2808,6 @@ dependencies = [
|
||||
"num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc 0.0.0",
|
||||
"rustc_allocator 0.0.0",
|
||||
"rustc_apfloat 0.0.0",
|
||||
"rustc_codegen_utils 0.0.0",
|
||||
"rustc_data_structures 0.0.0",
|
||||
@ -2883,7 +2868,6 @@ dependencies = [
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc 0.0.0",
|
||||
"rustc-rayon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_allocator 0.0.0",
|
||||
"rustc_ast_borrowck 0.0.0",
|
||||
"rustc_codegen_utils 0.0.0",
|
||||
"rustc_data_structures 0.0.0",
|
||||
@ -2904,7 +2888,6 @@ dependencies = [
|
||||
"serialize 0.0.0",
|
||||
"smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntax 0.0.0",
|
||||
"syntax_ext 0.0.0",
|
||||
"syntax_pos 0.0.0",
|
||||
]
|
||||
|
||||
@ -2948,7 +2931,6 @@ dependencies = [
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc 0.0.0",
|
||||
"rustc-rayon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_allocator 0.0.0",
|
||||
"rustc_ast_borrowck 0.0.0",
|
||||
"rustc_codegen_ssa 0.0.0",
|
||||
"rustc_codegen_utils 0.0.0",
|
||||
|
@ -1281,6 +1281,13 @@ mod builtin {
|
||||
#[rustc_macro_transparency = "semitransparent"]
|
||||
pub macro test_case($item:item) { /* compiler built-in */ }
|
||||
|
||||
/// Attribute macro applied to a static to register it as a global allocator.
|
||||
#[stable(feature = "global_allocator", since = "1.28.0")]
|
||||
#[allow_internal_unstable(rustc_attrs)]
|
||||
#[rustc_builtin_macro]
|
||||
#[rustc_macro_transparency = "semitransparent"]
|
||||
pub macro global_allocator($item:item) { /* compiler built-in */ }
|
||||
|
||||
/// Derive macro generating an impl of the trait `Clone`.
|
||||
#[rustc_builtin_macro]
|
||||
#[rustc_macro_transparency = "semitransparent"]
|
||||
|
@ -104,7 +104,6 @@ pub mod infer;
|
||||
pub mod lint;
|
||||
|
||||
pub mod middle {
|
||||
pub mod allocator;
|
||||
pub mod borrowck;
|
||||
pub mod expr_use_visitor;
|
||||
pub mod cstore;
|
||||
|
@ -1,16 +0,0 @@
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AllocatorKind {
|
||||
Global,
|
||||
DefaultLib,
|
||||
DefaultExe,
|
||||
}
|
||||
|
||||
impl AllocatorKind {
|
||||
pub fn fn_name(&self, base: &str) -> String {
|
||||
match *self {
|
||||
AllocatorKind::Global => format!("__rg_{}", base),
|
||||
AllocatorKind::DefaultLib => format!("__rdl_{}", base),
|
||||
AllocatorKind::DefaultExe => format!("__rde_{}", base),
|
||||
}
|
||||
}
|
||||
}
|
@ -320,11 +320,6 @@ fn has_allow_dead_code_or_lang_attr(
|
||||
return true;
|
||||
}
|
||||
|
||||
// Don't lint about global allocators
|
||||
if attr::contains_name(attrs, sym::global_allocator) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let def_id = tcx.hir().local_def_id(id);
|
||||
let cg_attrs = tcx.codegen_fn_attrs(def_id);
|
||||
|
||||
|
@ -7,7 +7,6 @@ use rustc_data_structures::fingerprint::Fingerprint;
|
||||
|
||||
use crate::lint;
|
||||
use crate::lint::builtin::BuiltinLintDiagnostics;
|
||||
use crate::middle::allocator::AllocatorKind;
|
||||
use crate::middle::dependency_format;
|
||||
use crate::session::config::{OutputType, PrintRequest, SwitchWithOptPath};
|
||||
use crate::session::search_paths::{PathKind, SearchPath};
|
||||
@ -27,6 +26,7 @@ use errors::emitter::HumanReadableErrorType;
|
||||
use errors::annotate_snippet_emitter_writer::{AnnotateSnippetEmitterWriter};
|
||||
use syntax::ast::{self, NodeId};
|
||||
use syntax::edition::Edition;
|
||||
use syntax::ext::allocator::AllocatorKind;
|
||||
use syntax::feature_gate::{self, AttributeType};
|
||||
use syntax::json::JsonEmitter;
|
||||
use syntax::source_map;
|
||||
|
@ -1,19 +0,0 @@
|
||||
[package]
|
||||
authors = ["The Rust Project Developers"]
|
||||
name = "rustc_allocator"
|
||||
version = "0.0.0"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
test = false
|
||||
|
||||
[dependencies]
|
||||
rustc = { path = "../librustc" }
|
||||
rustc_data_structures = { path = "../librustc_data_structures" }
|
||||
rustc_errors = { path = "../librustc_errors" }
|
||||
rustc_target = { path = "../librustc_target" }
|
||||
syntax = { path = "../libsyntax" }
|
||||
syntax_pos = { path = "../libsyntax_pos" }
|
||||
log = "0.4"
|
||||
smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
|
@ -1,298 +0,0 @@
|
||||
use log::debug;
|
||||
use rustc::middle::allocator::AllocatorKind;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use syntax::{
|
||||
ast::{
|
||||
self, Arg, Attribute, Crate, Expr, FnHeader, Generics, Ident, Item, ItemKind,
|
||||
Mac, Mod, Mutability, Ty, TyKind, Unsafety, VisibilityKind,
|
||||
},
|
||||
attr,
|
||||
source_map::{
|
||||
respan, ExpnInfo, ExpnKind,
|
||||
},
|
||||
ext::{
|
||||
base::{ExtCtxt, MacroKind, Resolver},
|
||||
build::AstBuilder,
|
||||
expand::ExpansionConfig,
|
||||
hygiene::ExpnId,
|
||||
},
|
||||
mut_visit::{self, MutVisitor},
|
||||
parse::ParseSess,
|
||||
ptr::P,
|
||||
symbol::{kw, sym}
|
||||
};
|
||||
use syntax_pos::Span;
|
||||
|
||||
use crate::{AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS};
|
||||
|
||||
pub fn modify(
|
||||
sess: &ParseSess,
|
||||
resolver: &mut dyn Resolver,
|
||||
krate: &mut Crate,
|
||||
crate_name: String,
|
||||
handler: &rustc_errors::Handler,
|
||||
) {
|
||||
ExpandAllocatorDirectives {
|
||||
handler,
|
||||
sess,
|
||||
resolver,
|
||||
found: false,
|
||||
crate_name: Some(crate_name),
|
||||
in_submod: -1, // -1 to account for the "root" module
|
||||
}.visit_crate(krate);
|
||||
}
|
||||
|
||||
struct ExpandAllocatorDirectives<'a> {
|
||||
found: bool,
|
||||
handler: &'a rustc_errors::Handler,
|
||||
sess: &'a ParseSess,
|
||||
resolver: &'a mut dyn Resolver,
|
||||
crate_name: Option<String>,
|
||||
|
||||
// For now, we disallow `global_allocator` in submodules because hygiene is hard. Keep track of
|
||||
// whether we are in a submodule or not. If `in_submod > 0` we are in a submodule.
|
||||
in_submod: isize,
|
||||
}
|
||||
|
||||
impl MutVisitor for ExpandAllocatorDirectives<'_> {
|
||||
fn flat_map_item(&mut self, item: P<Item>) -> SmallVec<[P<Item>; 1]> {
|
||||
debug!("in submodule {}", self.in_submod);
|
||||
|
||||
if !attr::contains_name(&item.attrs, sym::global_allocator) {
|
||||
return mut_visit::noop_flat_map_item(item, self);
|
||||
}
|
||||
|
||||
match item.node {
|
||||
ItemKind::Static(..) => {}
|
||||
_ => {
|
||||
self.handler
|
||||
.span_err(item.span, "allocators must be statics");
|
||||
return smallvec![item];
|
||||
}
|
||||
}
|
||||
|
||||
if self.in_submod > 0 {
|
||||
self.handler
|
||||
.span_err(item.span, "`global_allocator` cannot be used in submodules");
|
||||
return smallvec![item];
|
||||
}
|
||||
|
||||
if self.found {
|
||||
self.handler
|
||||
.span_err(item.span, "cannot define more than one `#[global_allocator]`");
|
||||
return smallvec![item];
|
||||
}
|
||||
self.found = true;
|
||||
|
||||
// Create a new expansion for the generated allocator code.
|
||||
let span = item.span.fresh_expansion(ExpnId::root(), ExpnInfo::allow_unstable(
|
||||
ExpnKind::Macro(MacroKind::Attr, sym::global_allocator), item.span, self.sess.edition,
|
||||
[sym::rustc_attrs][..].into(),
|
||||
));
|
||||
|
||||
// Create an expansion config
|
||||
let ecfg = ExpansionConfig::default(self.crate_name.take().unwrap());
|
||||
|
||||
// Generate a bunch of new items using the AllocFnFactory
|
||||
let mut f = AllocFnFactory {
|
||||
span,
|
||||
kind: AllocatorKind::Global,
|
||||
global: item.ident,
|
||||
core: Ident::with_empty_ctxt(sym::core),
|
||||
cx: ExtCtxt::new(self.sess, ecfg, self.resolver),
|
||||
};
|
||||
|
||||
// We will generate a new submodule. To `use` the static from that module, we need to get
|
||||
// the `super::...` path.
|
||||
let super_path = f.cx.path(f.span, vec![Ident::with_empty_ctxt(kw::Super), f.global]);
|
||||
|
||||
// Generate the items in the submodule
|
||||
let mut items = vec![
|
||||
// import `core` to use allocators
|
||||
f.cx.item_extern_crate(f.span, f.core),
|
||||
// `use` the `global_allocator` in `super`
|
||||
f.cx.item_use_simple(
|
||||
f.span,
|
||||
respan(f.span.shrink_to_lo(), VisibilityKind::Inherited),
|
||||
super_path,
|
||||
),
|
||||
];
|
||||
|
||||
// Add the allocator methods to the submodule
|
||||
items.extend(
|
||||
ALLOCATOR_METHODS
|
||||
.iter()
|
||||
.map(|method| f.allocator_fn(method)),
|
||||
);
|
||||
|
||||
// Generate the submodule itself
|
||||
let name = f.kind.fn_name("allocator_abi");
|
||||
let allocator_abi = Ident::from_str(&name).gensym();
|
||||
let module = f.cx.item_mod(span, span, allocator_abi, Vec::new(), items);
|
||||
let module = f.cx.monotonic_expander().flat_map_item(module).pop().unwrap();
|
||||
|
||||
// Return the item and new submodule
|
||||
smallvec![item, module]
|
||||
}
|
||||
|
||||
// If we enter a submodule, take note.
|
||||
fn visit_mod(&mut self, m: &mut Mod) {
|
||||
debug!("enter submodule");
|
||||
self.in_submod += 1;
|
||||
mut_visit::noop_visit_mod(m, self);
|
||||
self.in_submod -= 1;
|
||||
debug!("exit submodule");
|
||||
}
|
||||
|
||||
// `visit_mac` is disabled by default. Enable it here.
|
||||
fn visit_mac(&mut self, mac: &mut Mac) {
|
||||
mut_visit::noop_visit_mac(mac, self)
|
||||
}
|
||||
}
|
||||
|
||||
struct AllocFnFactory<'a> {
|
||||
span: Span,
|
||||
kind: AllocatorKind,
|
||||
global: Ident,
|
||||
core: Ident,
|
||||
cx: ExtCtxt<'a>,
|
||||
}
|
||||
|
||||
impl AllocFnFactory<'_> {
|
||||
fn allocator_fn(&self, method: &AllocatorMethod) -> P<Item> {
|
||||
let mut abi_args = Vec::new();
|
||||
let mut i = 0;
|
||||
let ref mut mk = || {
|
||||
let name = Ident::from_str(&format!("arg{}", i));
|
||||
i += 1;
|
||||
name
|
||||
};
|
||||
let args = method
|
||||
.inputs
|
||||
.iter()
|
||||
.map(|ty| self.arg_ty(ty, &mut abi_args, mk))
|
||||
.collect();
|
||||
let result = self.call_allocator(method.name, args);
|
||||
let (output_ty, output_expr) = self.ret_ty(&method.output, result);
|
||||
let kind = ItemKind::Fn(
|
||||
self.cx.fn_decl(abi_args, ast::FunctionRetTy::Ty(output_ty)),
|
||||
FnHeader {
|
||||
unsafety: Unsafety::Unsafe,
|
||||
..FnHeader::default()
|
||||
},
|
||||
Generics::default(),
|
||||
self.cx.block_expr(output_expr),
|
||||
);
|
||||
self.cx.item(
|
||||
self.span,
|
||||
Ident::from_str(&self.kind.fn_name(method.name)),
|
||||
self.attrs(),
|
||||
kind,
|
||||
)
|
||||
}
|
||||
|
||||
fn call_allocator(&self, method: &str, mut args: Vec<P<Expr>>) -> P<Expr> {
|
||||
let method = self.cx.path(
|
||||
self.span,
|
||||
vec![
|
||||
self.core,
|
||||
Ident::from_str("alloc"),
|
||||
Ident::from_str("GlobalAlloc"),
|
||||
Ident::from_str(method),
|
||||
],
|
||||
);
|
||||
let method = self.cx.expr_path(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(self.span, special)]
|
||||
}
|
||||
|
||||
fn arg_ty(
|
||||
&self,
|
||||
ty: &AllocatorTy,
|
||||
args: &mut Vec<Arg>,
|
||||
ident: &mut dyn FnMut() -> Ident,
|
||||
) -> P<Expr> {
|
||||
match *ty {
|
||||
AllocatorTy::Layout => {
|
||||
let usize = self.cx.path_ident(self.span, Ident::with_empty_ctxt(sym::usize));
|
||||
let ty_usize = self.cx.ty_path(usize);
|
||||
let size = ident();
|
||||
let align = ident();
|
||||
args.push(self.cx.arg(self.span, size, ty_usize.clone()));
|
||||
args.push(self.cx.arg(self.span, align, ty_usize));
|
||||
|
||||
let layout_new = self.cx.path(
|
||||
self.span,
|
||||
vec![
|
||||
self.core,
|
||||
Ident::from_str("alloc"),
|
||||
Ident::from_str("Layout"),
|
||||
Ident::from_str("from_size_align_unchecked"),
|
||||
],
|
||||
);
|
||||
let layout_new = self.cx.expr_path(layout_new);
|
||||
let size = self.cx.expr_ident(self.span, size);
|
||||
let align = self.cx.expr_ident(self.span, align);
|
||||
let layout = self.cx.expr_call(self.span, layout_new, vec![size, align]);
|
||||
layout
|
||||
}
|
||||
|
||||
AllocatorTy::Ptr => {
|
||||
let ident = ident();
|
||||
args.push(self.cx.arg(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.arg(self.span, ident, self.usize()));
|
||||
self.cx.expr_ident(self.span, ident)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
AllocatorTy::Unit => (self.cx.ty(self.span, TyKind::Tup(Vec::new())), expr),
|
||||
|
||||
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
|
||||
panic!("can't convert `AllocatorTy` to an output")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn usize(&self) -> P<Ty> {
|
||||
let usize = self.cx.path_ident(self.span, Ident::with_empty_ctxt(sym::usize));
|
||||
self.cx.ty_path(usize)
|
||||
}
|
||||
|
||||
fn ptr_u8(&self) -> P<Ty> {
|
||||
let u8 = self.cx.path_ident(self.span, Ident::with_empty_ctxt(sym::u8));
|
||||
let ty_u8 = self.cx.ty_path(u8);
|
||||
self.cx.ty_ptr(self.span, ty_u8, Mutability::Mutable)
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
#![feature(nll)]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![deny(unused_lifetimes)]
|
||||
|
||||
pub mod expand;
|
||||
|
||||
pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[
|
||||
AllocatorMethod {
|
||||
name: "alloc",
|
||||
inputs: &[AllocatorTy::Layout],
|
||||
output: AllocatorTy::ResultPtr,
|
||||
},
|
||||
AllocatorMethod {
|
||||
name: "dealloc",
|
||||
inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout],
|
||||
output: AllocatorTy::Unit,
|
||||
},
|
||||
AllocatorMethod {
|
||||
name: "realloc",
|
||||
inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Usize],
|
||||
output: AllocatorTy::ResultPtr,
|
||||
},
|
||||
AllocatorMethod {
|
||||
name: "alloc_zeroed",
|
||||
inputs: &[AllocatorTy::Layout],
|
||||
output: AllocatorTy::ResultPtr,
|
||||
},
|
||||
];
|
||||
|
||||
pub struct AllocatorMethod {
|
||||
pub name: &'static str,
|
||||
pub inputs: &'static [AllocatorTy],
|
||||
pub output: AllocatorTy,
|
||||
}
|
||||
|
||||
pub enum AllocatorTy {
|
||||
Layout,
|
||||
Ptr,
|
||||
ResultPtr,
|
||||
Unit,
|
||||
Usize,
|
||||
}
|
@ -2,9 +2,8 @@ use std::ffi::CString;
|
||||
|
||||
use crate::attributes;
|
||||
use libc::c_uint;
|
||||
use rustc::middle::allocator::AllocatorKind;
|
||||
use rustc::ty::TyCtxt;
|
||||
use rustc_allocator::{ALLOCATOR_METHODS, AllocatorTy};
|
||||
use syntax::ext::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS};
|
||||
|
||||
use crate::ModuleLlvm;
|
||||
use crate::llvm::{self, False, True};
|
||||
|
@ -32,7 +32,6 @@ extern crate flate2;
|
||||
#[macro_use] extern crate bitflags;
|
||||
extern crate libc;
|
||||
#[macro_use] extern crate rustc;
|
||||
extern crate rustc_allocator;
|
||||
extern crate rustc_target;
|
||||
#[macro_use] extern crate rustc_data_structures;
|
||||
extern crate rustc_incremental;
|
||||
@ -52,13 +51,13 @@ use rustc_codegen_ssa::back::lto::{SerializedModule, LtoModuleCodegen, ThinModul
|
||||
use rustc_codegen_ssa::CompiledModule;
|
||||
use errors::{FatalError, Handler};
|
||||
use rustc::dep_graph::WorkProduct;
|
||||
use syntax::ext::allocator::AllocatorKind;
|
||||
use syntax_pos::symbol::InternedString;
|
||||
pub use llvm_util::target_features;
|
||||
use std::any::Any;
|
||||
use std::sync::{mpsc, Arc};
|
||||
|
||||
use rustc::dep_graph::DepGraph;
|
||||
use rustc::middle::allocator::AllocatorKind;
|
||||
use rustc::middle::cstore::{EncodedMetadata, MetadataLoader};
|
||||
use rustc::session::Session;
|
||||
use rustc::session::config::{OutputFilenames, OutputType, PrintRequest, OptLevel};
|
||||
|
@ -24,7 +24,6 @@ rustc_serialize = { path = "../libserialize", package = "serialize" }
|
||||
syntax = { path = "../libsyntax" }
|
||||
syntax_pos = { path = "../libsyntax_pos" }
|
||||
rustc = { path = "../librustc" }
|
||||
rustc_allocator = { path = "../librustc_allocator" }
|
||||
rustc_apfloat = { path = "../librustc_apfloat" }
|
||||
rustc_codegen_utils = { path = "../librustc_codegen_utils" }
|
||||
rustc_data_structures = { path = "../librustc_data_structures"}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use std::collections::hash_map::Entry::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
use rustc::ty::Instance;
|
||||
@ -12,9 +13,8 @@ use rustc::ty::{TyCtxt, SymbolName};
|
||||
use rustc::ty::query::Providers;
|
||||
use rustc::ty::subst::SubstsRef;
|
||||
use rustc::util::nodemap::{FxHashMap, DefIdMap};
|
||||
use rustc_allocator::ALLOCATOR_METHODS;
|
||||
use rustc_data_structures::indexed_vec::IndexVec;
|
||||
use std::collections::hash_map::Entry::*;
|
||||
use syntax::ext::allocator::ALLOCATOR_METHODS;
|
||||
|
||||
pub type ExportedSymbols = FxHashMap<
|
||||
CrateNum,
|
||||
|
@ -3,12 +3,12 @@ use rustc::ty::Ty;
|
||||
|
||||
use super::write::WriteBackendMethods;
|
||||
use super::CodegenObject;
|
||||
use rustc::middle::allocator::AllocatorKind;
|
||||
use rustc::middle::cstore::EncodedMetadata;
|
||||
use rustc::session::{Session, config};
|
||||
use rustc::ty::TyCtxt;
|
||||
use rustc_codegen_utils::codegen_backend::CodegenBackend;
|
||||
use std::sync::Arc;
|
||||
use syntax::ext::allocator::AllocatorKind;
|
||||
use syntax_pos::symbol::InternedString;
|
||||
|
||||
pub trait BackendTypes {
|
||||
|
@ -16,7 +16,6 @@ log = "0.4"
|
||||
env_logger = { version = "0.5", default-features = false }
|
||||
rayon = { version = "0.2.0", package = "rustc-rayon" }
|
||||
rustc = { path = "../librustc" }
|
||||
rustc_allocator = { path = "../librustc_allocator" }
|
||||
rustc_target = { path = "../librustc_target" }
|
||||
rustc_ast_borrowck = { path = "../librustc_ast_borrowck" }
|
||||
rustc_data_structures = { path = "../librustc_data_structures" }
|
||||
@ -37,5 +36,4 @@ rustc_interface = { path = "../librustc_interface" }
|
||||
rustc_serialize = { path = "../libserialize", package = "serialize" }
|
||||
syntax = { path = "../libsyntax" }
|
||||
smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
|
||||
syntax_ext = { path = "../libsyntax_ext" }
|
||||
syntax_pos = { path = "../libsyntax_pos" }
|
||||
|
@ -18,7 +18,6 @@ syntax_ext = { path = "../libsyntax_ext" }
|
||||
syntax_pos = { path = "../libsyntax_pos" }
|
||||
rustc_serialize = { path = "../libserialize", package = "serialize" }
|
||||
rustc = { path = "../librustc" }
|
||||
rustc_allocator = { path = "../librustc_allocator" }
|
||||
rustc_ast_borrowck = { path = "../librustc_ast_borrowck" }
|
||||
rustc_incremental = { path = "../librustc_incremental" }
|
||||
rustc_traits = { path = "../librustc_traits" }
|
||||
|
@ -469,7 +469,7 @@ fn configure_and_expand_inner<'a>(
|
||||
util::ReplaceBodyWithLoop::new(sess).visit_crate(&mut krate);
|
||||
}
|
||||
|
||||
let (has_proc_macro_decls, has_global_allocator) = time(sess, "AST validation", || {
|
||||
let has_proc_macro_decls = time(sess, "AST validation", || {
|
||||
ast_validation::check_crate(sess, &krate)
|
||||
});
|
||||
|
||||
@ -495,19 +495,6 @@ fn configure_and_expand_inner<'a>(
|
||||
});
|
||||
}
|
||||
|
||||
if has_global_allocator {
|
||||
// Expand global allocators, which are treated as an in-tree proc macro
|
||||
time(sess, "creating allocators", || {
|
||||
allocator::expand::modify(
|
||||
&sess.parse_sess,
|
||||
&mut resolver,
|
||||
&mut krate,
|
||||
crate_name.to_string(),
|
||||
sess.diagnostic(),
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
// Done with macro expansion!
|
||||
|
||||
if sess.opts.debugging_opts.input_stats {
|
||||
|
@ -36,10 +36,10 @@ use syntax::tokenstream::{TokenTree, TokenStream};
|
||||
use syntax::ast;
|
||||
use syntax::ptr::P;
|
||||
use syntax::ast::Expr;
|
||||
use syntax::attr::{self, HasAttrs};
|
||||
use syntax::attr::{self, HasAttrs, AttributeTemplate};
|
||||
use syntax::source_map::Spanned;
|
||||
use syntax::edition::Edition;
|
||||
use syntax::feature_gate::{AttributeGate, AttributeTemplate, AttributeType};
|
||||
use syntax::feature_gate::{AttributeGate, AttributeType};
|
||||
use syntax::feature_gate::{Stability, deprecated_attributes};
|
||||
use syntax_pos::{BytePos, Span, SyntaxContext};
|
||||
use syntax::symbol::{Symbol, kw, sym};
|
||||
|
@ -8,7 +8,6 @@ use rustc_data_structures::sync::{Lrc, RwLock, Lock};
|
||||
|
||||
use rustc::hir::def_id::CrateNum;
|
||||
use rustc_data_structures::svh::Svh;
|
||||
use rustc::middle::allocator::AllocatorKind;
|
||||
use rustc::middle::cstore::DepKind;
|
||||
use rustc::mir::interpret::AllocDecodingState;
|
||||
use rustc::session::{Session, CrateDisambiguator};
|
||||
@ -26,9 +25,9 @@ use std::{cmp, fs};
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::attr;
|
||||
use syntax::ext::allocator::{global_allocator_spans, AllocatorKind};
|
||||
use syntax::ext::base::{SyntaxExtension, SyntaxExtensionKind};
|
||||
use syntax::symbol::{Symbol, sym};
|
||||
use syntax::visit;
|
||||
use syntax::{span_err, span_fatal};
|
||||
use syntax_pos::{Span, DUMMY_SP};
|
||||
use log::{debug, info, log_enabled};
|
||||
@ -888,7 +887,14 @@ impl<'a> CrateLoader<'a> {
|
||||
}
|
||||
|
||||
fn inject_allocator_crate(&mut self, krate: &ast::Crate) {
|
||||
let has_global_allocator = has_global_allocator(krate);
|
||||
let has_global_allocator = match &*global_allocator_spans(krate) {
|
||||
[span1, span2, ..] => {
|
||||
self.sess.struct_span_err(*span2, "cannot define multiple global allocators")
|
||||
.span_note(*span1, "the previous global allocator is defined here").emit();
|
||||
true
|
||||
}
|
||||
spans => !spans.is_empty()
|
||||
};
|
||||
self.sess.has_global_allocator.set(has_global_allocator);
|
||||
|
||||
// Check to see if we actually need an allocator. This desire comes
|
||||
@ -975,25 +981,8 @@ impl<'a> CrateLoader<'a> {
|
||||
that implements the GlobalAlloc trait.");
|
||||
}
|
||||
self.sess.allocator_kind.set(Some(AllocatorKind::DefaultLib));
|
||||
|
||||
fn has_global_allocator(krate: &ast::Crate) -> bool {
|
||||
struct Finder(bool);
|
||||
let mut f = Finder(false);
|
||||
visit::walk_crate(&mut f, krate);
|
||||
return f.0;
|
||||
|
||||
impl<'ast> visit::Visitor<'ast> for Finder {
|
||||
fn visit_item(&mut self, i: &'ast ast::Item) {
|
||||
if attr::contains_name(&i.attrs, sym::global_allocator) {
|
||||
self.0 = true;
|
||||
}
|
||||
visit::walk_item(self, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn inject_dependency_if(&self,
|
||||
krate: CrateNum,
|
||||
what: &str,
|
||||
|
@ -1,6 +1,7 @@
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
|
||||
|
||||
#![feature(box_patterns)]
|
||||
#![feature(crate_visibility_modifier)]
|
||||
#![feature(drain_filter)]
|
||||
#![feature(in_band_lifetimes)]
|
||||
#![feature(libc)]
|
||||
@ -8,9 +9,9 @@
|
||||
#![feature(proc_macro_internals)]
|
||||
#![feature(proc_macro_quote)]
|
||||
#![feature(rustc_diagnostic_macros)]
|
||||
#![feature(crate_visibility_modifier)]
|
||||
#![feature(specialization)]
|
||||
#![feature(rustc_private)]
|
||||
#![feature(slice_patterns)]
|
||||
#![feature(specialization)]
|
||||
|
||||
#![recursion_limit="256"]
|
||||
|
||||
|
@ -51,7 +51,6 @@ impl OuterImplTrait {
|
||||
struct AstValidator<'a> {
|
||||
session: &'a Session,
|
||||
has_proc_macro_decls: bool,
|
||||
has_global_allocator: bool,
|
||||
|
||||
/// Used to ban nested `impl Trait`, e.g., `impl Into<impl Debug>`.
|
||||
/// Nested `impl Trait` _is_ allowed in associated type position,
|
||||
@ -539,10 +538,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
self.has_proc_macro_decls = true;
|
||||
}
|
||||
|
||||
if attr::contains_name(&item.attrs, sym::global_allocator) {
|
||||
self.has_global_allocator = true;
|
||||
}
|
||||
|
||||
match item.node {
|
||||
ItemKind::Impl(unsafety, polarity, _, _, Some(..), ref ty, ref impl_items) => {
|
||||
self.invalid_visibility(&item.vis, None);
|
||||
@ -848,11 +843,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_crate(session: &Session, krate: &Crate) -> (bool, bool) {
|
||||
pub fn check_crate(session: &Session, krate: &Crate) -> bool {
|
||||
let mut validator = AstValidator {
|
||||
session,
|
||||
has_proc_macro_decls: false,
|
||||
has_global_allocator: false,
|
||||
outer_impl_trait: None,
|
||||
is_impl_trait_banned: false,
|
||||
is_assoc_ty_bound_banned: false,
|
||||
@ -861,5 +855,5 @@ pub fn check_crate(session: &Session, krate: &Crate) -> (bool, bool) {
|
||||
};
|
||||
visit::walk_crate(&mut validator, krate);
|
||||
|
||||
(validator.has_proc_macro_decls, validator.has_global_allocator)
|
||||
validator.has_proc_macro_decls
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
//! Parsing and validation of builtin attributes
|
||||
|
||||
use crate::ast::{self, Attribute, MetaItem, NestedMetaItem};
|
||||
use crate::early_buffered_lints::BufferedEarlyLintId;
|
||||
use crate::ext::base::ExtCtxt;
|
||||
use crate::ext::build::AstBuilder;
|
||||
use crate::feature_gate::{Features, GatedCfg};
|
||||
use crate::parse::ParseSess;
|
||||
|
||||
@ -19,6 +22,27 @@ enum AttrError {
|
||||
UnsupportedLiteral(&'static str, /* is_bytestr */ bool),
|
||||
}
|
||||
|
||||
/// A template that the attribute input must match.
|
||||
/// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct AttributeTemplate {
|
||||
crate word: bool,
|
||||
crate list: Option<&'static str>,
|
||||
crate name_value_str: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl AttributeTemplate {
|
||||
/// Checks that the given meta-item is compatible with this template.
|
||||
fn compatible(&self, meta_item_kind: &ast::MetaItemKind) -> bool {
|
||||
match meta_item_kind {
|
||||
ast::MetaItemKind::Word => self.word,
|
||||
ast::MetaItemKind::List(..) => self.list.is_some(),
|
||||
ast::MetaItemKind::NameValue(lit) if lit.node.is_str() => self.name_value_str.is_some(),
|
||||
ast::MetaItemKind::NameValue(..) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_errors(sess: &ParseSess, span: Span, error: AttrError) {
|
||||
let diag = &sess.span_diagnostic;
|
||||
match error {
|
||||
@ -901,3 +925,76 @@ pub fn find_transparency(
|
||||
let fallback = if is_legacy { Transparency::SemiTransparent } else { Transparency::Opaque };
|
||||
(transparency.map_or(fallback, |t| t.0), error)
|
||||
}
|
||||
|
||||
pub fn check_builtin_macro_attribute(ecx: &ExtCtxt<'_>, meta_item: &MetaItem, name: Symbol) {
|
||||
// All the built-in macro attributes are "words" at the moment.
|
||||
let template = AttributeTemplate { word: true, list: None, name_value_str: None };
|
||||
let attr = ecx.attribute(meta_item.span, meta_item.clone());
|
||||
check_builtin_attribute(ecx.parse_sess, &attr, name, template);
|
||||
}
|
||||
|
||||
crate fn check_builtin_attribute(
|
||||
sess: &ParseSess, attr: &ast::Attribute, name: Symbol, template: AttributeTemplate
|
||||
) {
|
||||
// Some special attributes like `cfg` must be checked
|
||||
// before the generic check, so we skip them here.
|
||||
let should_skip = |name| name == sym::cfg;
|
||||
// Some of previously accepted forms were used in practice,
|
||||
// report them as warnings for now.
|
||||
let should_warn = |name| name == sym::doc || name == sym::ignore ||
|
||||
name == sym::inline || name == sym::link ||
|
||||
name == sym::test || name == sym::bench;
|
||||
|
||||
match attr.parse_meta(sess) {
|
||||
Ok(meta) => if !should_skip(name) && !template.compatible(&meta.node) {
|
||||
let error_msg = format!("malformed `{}` attribute input", name);
|
||||
let mut msg = "attribute must be of the form ".to_owned();
|
||||
let mut suggestions = vec![];
|
||||
let mut first = true;
|
||||
if template.word {
|
||||
first = false;
|
||||
let code = format!("#[{}]", name);
|
||||
msg.push_str(&format!("`{}`", &code));
|
||||
suggestions.push(code);
|
||||
}
|
||||
if let Some(descr) = template.list {
|
||||
if !first {
|
||||
msg.push_str(" or ");
|
||||
}
|
||||
first = false;
|
||||
let code = format!("#[{}({})]", name, descr);
|
||||
msg.push_str(&format!("`{}`", &code));
|
||||
suggestions.push(code);
|
||||
}
|
||||
if let Some(descr) = template.name_value_str {
|
||||
if !first {
|
||||
msg.push_str(" or ");
|
||||
}
|
||||
let code = format!("#[{} = \"{}\"]", name, descr);
|
||||
msg.push_str(&format!("`{}`", &code));
|
||||
suggestions.push(code);
|
||||
}
|
||||
if should_warn(name) {
|
||||
sess.buffer_lint(
|
||||
BufferedEarlyLintId::IllFormedAttributeInput,
|
||||
meta.span,
|
||||
ast::CRATE_NODE_ID,
|
||||
&msg,
|
||||
);
|
||||
} else {
|
||||
sess.span_diagnostic.struct_span_err(meta.span, &error_msg)
|
||||
.span_suggestions(
|
||||
meta.span,
|
||||
if suggestions.len() == 1 {
|
||||
"must be of the form"
|
||||
} else {
|
||||
"the following are the possible correct uses"
|
||||
},
|
||||
suggestions.into_iter(),
|
||||
Applicability::HasPlaceholders,
|
||||
).emit();
|
||||
}
|
||||
}
|
||||
Err(mut err) => err.emit(),
|
||||
}
|
||||
}
|
||||
|
75
src/libsyntax/ext/allocator.rs
Normal file
75
src/libsyntax/ext/allocator.rs
Normal file
@ -0,0 +1,75 @@
|
||||
use crate::{ast, attr, visit};
|
||||
use crate::symbol::{sym, Symbol};
|
||||
use syntax_pos::Span;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AllocatorKind {
|
||||
Global,
|
||||
DefaultLib,
|
||||
DefaultExe,
|
||||
}
|
||||
|
||||
impl AllocatorKind {
|
||||
pub fn fn_name(&self, base: &str) -> String {
|
||||
match *self {
|
||||
AllocatorKind::Global => format!("__rg_{}", base),
|
||||
AllocatorKind::DefaultLib => format!("__rdl_{}", base),
|
||||
AllocatorKind::DefaultExe => format!("__rde_{}", base),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AllocatorTy {
|
||||
Layout,
|
||||
Ptr,
|
||||
ResultPtr,
|
||||
Unit,
|
||||
Usize,
|
||||
}
|
||||
|
||||
pub struct AllocatorMethod {
|
||||
pub name: &'static str,
|
||||
pub inputs: &'static [AllocatorTy],
|
||||
pub output: AllocatorTy,
|
||||
}
|
||||
|
||||
pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[
|
||||
AllocatorMethod {
|
||||
name: "alloc",
|
||||
inputs: &[AllocatorTy::Layout],
|
||||
output: AllocatorTy::ResultPtr,
|
||||
},
|
||||
AllocatorMethod {
|
||||
name: "dealloc",
|
||||
inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout],
|
||||
output: AllocatorTy::Unit,
|
||||
},
|
||||
AllocatorMethod {
|
||||
name: "realloc",
|
||||
inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Usize],
|
||||
output: AllocatorTy::ResultPtr,
|
||||
},
|
||||
AllocatorMethod {
|
||||
name: "alloc_zeroed",
|
||||
inputs: &[AllocatorTy::Layout],
|
||||
output: AllocatorTy::ResultPtr,
|
||||
},
|
||||
];
|
||||
|
||||
pub fn global_allocator_spans(krate: &ast::Crate) -> Vec<Span> {
|
||||
struct Finder { name: Symbol, spans: Vec<Span> }
|
||||
impl<'ast> visit::Visitor<'ast> for Finder {
|
||||
fn visit_item(&mut self, item: &'ast ast::Item) {
|
||||
if item.ident.name == self.name &&
|
||||
attr::contains_name(&item.attrs, sym::rustc_std_internal_symbol) {
|
||||
self.spans.push(item.span);
|
||||
}
|
||||
visit::walk_item(self, item)
|
||||
}
|
||||
}
|
||||
|
||||
let name = Symbol::intern(&AllocatorKind::Global.fn_name("alloc"));
|
||||
let mut f = Finder { name, spans: Vec::new() };
|
||||
visit::walk_crate(&mut f, krate);
|
||||
f.spans
|
||||
}
|
@ -19,8 +19,7 @@ use crate::ast::{
|
||||
self, AssocTyConstraint, AssocTyConstraintKind, NodeId, GenericParam, GenericParamKind,
|
||||
PatKind, RangeEnd,
|
||||
};
|
||||
use crate::attr;
|
||||
use crate::early_buffered_lints::BufferedEarlyLintId;
|
||||
use crate::attr::{self, check_builtin_attribute, AttributeTemplate};
|
||||
use crate::source_map::Spanned;
|
||||
use crate::edition::{ALL_EDITIONS, Edition};
|
||||
use crate::visit::{self, FnKind, Visitor};
|
||||
@ -906,27 +905,6 @@ pub enum AttributeGate {
|
||||
Ungated,
|
||||
}
|
||||
|
||||
/// A template that the attribute input must match.
|
||||
/// Only top-level shape (`#[attr]` vs `#[attr(...)]` vs `#[attr = ...]`) is considered now.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct AttributeTemplate {
|
||||
word: bool,
|
||||
list: Option<&'static str>,
|
||||
name_value_str: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl AttributeTemplate {
|
||||
/// Checks that the given meta-item is compatible with this template.
|
||||
fn compatible(&self, meta_item_kind: &ast::MetaItemKind) -> bool {
|
||||
match meta_item_kind {
|
||||
ast::MetaItemKind::Word => self.word,
|
||||
ast::MetaItemKind::List(..) => self.list.is_some(),
|
||||
ast::MetaItemKind::NameValue(lit) if lit.node.is_str() => self.name_value_str.is_some(),
|
||||
ast::MetaItemKind::NameValue(..) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A convenience macro for constructing attribute templates.
|
||||
/// E.g., `template!(Word, List: "description")` means that the attribute
|
||||
/// supports forms `#[attr]` and `#[attr(description)]`.
|
||||
@ -1117,7 +1095,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||
"the `#[rustc_const_unstable]` attribute \
|
||||
is an internal feature",
|
||||
cfg_fn!(rustc_const_unstable))),
|
||||
(sym::global_allocator, Normal, template!(Word), Ungated),
|
||||
(sym::default_lib_allocator, Whitelisted, template!(Word), Gated(Stability::Unstable,
|
||||
sym::allocator_internals,
|
||||
"the `#[default_lib_allocator]` \
|
||||
@ -1902,70 +1879,6 @@ impl<'a> PostExpansionVisitor<'a> {
|
||||
Abi::System => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_builtin_attribute(&mut self, attr: &ast::Attribute, name: Symbol,
|
||||
template: AttributeTemplate) {
|
||||
// Some special attributes like `cfg` must be checked
|
||||
// before the generic check, so we skip them here.
|
||||
let should_skip = |name| name == sym::cfg;
|
||||
// Some of previously accepted forms were used in practice,
|
||||
// report them as warnings for now.
|
||||
let should_warn = |name| name == sym::doc || name == sym::ignore ||
|
||||
name == sym::inline || name == sym::link;
|
||||
|
||||
match attr.parse_meta(self.context.parse_sess) {
|
||||
Ok(meta) => if !should_skip(name) && !template.compatible(&meta.node) {
|
||||
let error_msg = format!("malformed `{}` attribute input", name);
|
||||
let mut msg = "attribute must be of the form ".to_owned();
|
||||
let mut suggestions = vec![];
|
||||
let mut first = true;
|
||||
if template.word {
|
||||
first = false;
|
||||
let code = format!("#[{}]", name);
|
||||
msg.push_str(&format!("`{}`", &code));
|
||||
suggestions.push(code);
|
||||
}
|
||||
if let Some(descr) = template.list {
|
||||
if !first {
|
||||
msg.push_str(" or ");
|
||||
}
|
||||
first = false;
|
||||
let code = format!("#[{}({})]", name, descr);
|
||||
msg.push_str(&format!("`{}`", &code));
|
||||
suggestions.push(code);
|
||||
}
|
||||
if let Some(descr) = template.name_value_str {
|
||||
if !first {
|
||||
msg.push_str(" or ");
|
||||
}
|
||||
let code = format!("#[{} = \"{}\"]", name, descr);
|
||||
msg.push_str(&format!("`{}`", &code));
|
||||
suggestions.push(code);
|
||||
}
|
||||
if should_warn(name) {
|
||||
self.context.parse_sess.buffer_lint(
|
||||
BufferedEarlyLintId::IllFormedAttributeInput,
|
||||
meta.span,
|
||||
ast::CRATE_NODE_ID,
|
||||
&msg,
|
||||
);
|
||||
} else {
|
||||
self.context.parse_sess.span_diagnostic.struct_span_err(meta.span, &error_msg)
|
||||
.span_suggestions(
|
||||
meta.span,
|
||||
if suggestions.len() == 1 {
|
||||
"must be of the form"
|
||||
} else {
|
||||
"the following are the possible correct uses"
|
||||
},
|
||||
suggestions.into_iter(),
|
||||
Applicability::HasPlaceholders,
|
||||
).emit();
|
||||
}
|
||||
}
|
||||
Err(mut err) => err.emit(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
||||
@ -2006,7 +1919,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
|
||||
match attr_info {
|
||||
// `rustc_dummy` doesn't have any restrictions specific to built-in attributes.
|
||||
Some(&(name, _, template, _)) if name != sym::rustc_dummy =>
|
||||
self.check_builtin_attribute(attr, name, template),
|
||||
check_builtin_attribute(self.context.parse_sess, attr, name, template),
|
||||
_ => if let Some(TokenTree::Token(token)) = attr.tokens.trees().next() {
|
||||
if token == token::Eq {
|
||||
// All key-value attributes are restricted to meta-item syntax.
|
||||
|
@ -162,6 +162,7 @@ pub mod print {
|
||||
|
||||
pub mod ext {
|
||||
pub use syntax_pos::hygiene;
|
||||
pub mod allocator;
|
||||
pub mod base;
|
||||
pub mod build;
|
||||
pub mod derive;
|
||||
|
191
src/libsyntax_ext/global_allocator.rs
Normal file
191
src/libsyntax_ext/global_allocator.rs
Normal file
@ -0,0 +1,191 @@
|
||||
use syntax::ast::{ItemKind, Mutability, Stmt, Ty, TyKind, Unsafety};
|
||||
use syntax::ast::{self, Arg, Attribute, Expr, FnHeader, Generics, Ident};
|
||||
use syntax::attr::check_builtin_macro_attribute;
|
||||
use syntax::ext::allocator::{AllocatorKind, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS};
|
||||
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||
use syntax::ext::build::AstBuilder;
|
||||
use syntax::ext::hygiene::SyntaxContext;
|
||||
use syntax::ptr::P;
|
||||
use syntax::symbol::{kw, sym, Symbol};
|
||||
use syntax_pos::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.parse_sess.span_diagnostic.span_err(item.span(), "allocators must be statics");
|
||||
vec![item]
|
||||
};
|
||||
let item = match item {
|
||||
Annotatable::Item(item) => match item.node {
|
||||
ItemKind::Static(..) => item,
|
||||
_ => return not_static(Annotatable::Item(item)),
|
||||
}
|
||||
_ => return not_static(item),
|
||||
};
|
||||
|
||||
// Generate a bunch of new items using the AllocFnFactory
|
||||
let span = item.span.with_ctxt(SyntaxContext::empty().apply_mark(ecx.current_expansion.id));
|
||||
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));
|
||||
let const_item =
|
||||
ecx.item_const(span, Ident::with_empty_ctxt(kw::Underscore), const_ty, const_body);
|
||||
|
||||
// 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 ref mut mk = || {
|
||||
let name = Ident::from_str(&format!("arg{}", i));
|
||||
i += 1;
|
||||
name
|
||||
};
|
||||
let args = method
|
||||
.inputs
|
||||
.iter()
|
||||
.map(|ty| self.arg_ty(ty, &mut abi_args, mk))
|
||||
.collect();
|
||||
let result = self.call_allocator(method.name, args);
|
||||
let (output_ty, output_expr) = self.ret_ty(&method.output, result);
|
||||
let kind = ItemKind::Fn(
|
||||
self.cx.fn_decl(abi_args, ast::FunctionRetTy::Ty(output_ty)),
|
||||
FnHeader {
|
||||
unsafety: Unsafety::Unsafe,
|
||||
..FnHeader::default()
|
||||
},
|
||||
Generics::default(),
|
||||
self.cx.block_expr(output_expr),
|
||||
);
|
||||
let item = self.cx.item(
|
||||
self.span,
|
||||
Ident::from_str(&self.kind.fn_name(method.name)),
|
||||
self.attrs(),
|
||||
kind,
|
||||
);
|
||||
self.cx.stmt_item(self.span, item)
|
||||
}
|
||||
|
||||
fn call_allocator(&self, method: &str, mut args: Vec<P<Expr>>) -> P<Expr> {
|
||||
let method = self.cx.std_path(&[
|
||||
Symbol::intern("alloc"),
|
||||
Symbol::intern("GlobalAlloc"),
|
||||
Symbol::intern(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(self.span, special)]
|
||||
}
|
||||
|
||||
fn arg_ty(
|
||||
&self,
|
||||
ty: &AllocatorTy,
|
||||
args: &mut Vec<Arg>,
|
||||
ident: &mut dyn FnMut() -> Ident,
|
||||
) -> P<Expr> {
|
||||
match *ty {
|
||||
AllocatorTy::Layout => {
|
||||
let usize = self.cx.path_ident(self.span, Ident::with_empty_ctxt(sym::usize));
|
||||
let ty_usize = self.cx.ty_path(usize);
|
||||
let size = ident();
|
||||
let align = ident();
|
||||
args.push(self.cx.arg(self.span, size, ty_usize.clone()));
|
||||
args.push(self.cx.arg(self.span, align, ty_usize));
|
||||
|
||||
let layout_new = self.cx.std_path(&[
|
||||
Symbol::intern("alloc"),
|
||||
Symbol::intern("Layout"),
|
||||
Symbol::intern("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);
|
||||
let layout = self.cx.expr_call(self.span, layout_new, vec![size, align]);
|
||||
layout
|
||||
}
|
||||
|
||||
AllocatorTy::Ptr => {
|
||||
let ident = ident();
|
||||
args.push(self.cx.arg(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.arg(self.span, ident, self.usize()));
|
||||
self.cx.expr_ident(self.span, ident)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
AllocatorTy::Unit => (self.cx.ty(self.span, TyKind::Tup(Vec::new())), expr),
|
||||
|
||||
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
|
||||
panic!("can't convert `AllocatorTy` to an output")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn usize(&self) -> P<Ty> {
|
||||
let usize = self.cx.path_ident(self.span, Ident::with_empty_ctxt(sym::usize));
|
||||
self.cx.ty_path(usize)
|
||||
}
|
||||
|
||||
fn ptr_u8(&self) -> P<Ty> {
|
||||
let u8 = self.cx.path_ident(self.span, Ident::with_empty_ctxt(sym::u8));
|
||||
let ty_u8 = self.cx.ty_path(u8);
|
||||
self.cx.ty_ptr(self.span, ty_u8, Mutability::Mutable)
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ mod concat_idents;
|
||||
mod env;
|
||||
mod format;
|
||||
mod format_foreign;
|
||||
mod global_allocator;
|
||||
mod global_asm;
|
||||
mod log_syntax;
|
||||
mod proc_macro_server;
|
||||
@ -151,6 +152,12 @@ pub fn register_builtins(resolver: &mut dyn syntax::ext::base::Resolver,
|
||||
SyntaxExtensionKind::LegacyAttr(Box::new(test::expand_bench)), edition
|
||||
)
|
||||
});
|
||||
register(sym::global_allocator, SyntaxExtension {
|
||||
allow_internal_unstable: Some([sym::rustc_attrs][..].into()),
|
||||
..SyntaxExtension::default(
|
||||
SyntaxExtensionKind::LegacyAttr(Box::new(global_allocator::expand)), edition
|
||||
)
|
||||
});
|
||||
|
||||
let allow_internal_unstable = Some([sym::fmt_internals][..].into());
|
||||
register(sym::format_args, SyntaxExtension {
|
||||
|
@ -1,31 +1,34 @@
|
||||
/// The expansion from a test function to the appropriate test struct for libtest
|
||||
/// Ideally, this code would be in libtest but for efficiency and error messages it lives here.
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::attr::{self, check_builtin_macro_attribute};
|
||||
use syntax::ext::base::*;
|
||||
use syntax::ext::build::AstBuilder;
|
||||
use syntax::ext::hygiene::SyntaxContext;
|
||||
use syntax::attr;
|
||||
use syntax::ast;
|
||||
use syntax::print::pprust;
|
||||
use syntax::symbol::{Symbol, sym};
|
||||
use syntax_pos::Span;
|
||||
|
||||
use std::iter;
|
||||
|
||||
pub fn expand_test(
|
||||
cx: &mut ExtCtxt<'_>,
|
||||
attr_sp: Span,
|
||||
_meta_item: &ast::MetaItem,
|
||||
meta_item: &ast::MetaItem,
|
||||
item: Annotatable,
|
||||
) -> Vec<Annotatable> {
|
||||
check_builtin_macro_attribute(cx, meta_item, sym::test);
|
||||
expand_test_or_bench(cx, attr_sp, item, false)
|
||||
}
|
||||
|
||||
pub fn expand_bench(
|
||||
cx: &mut ExtCtxt<'_>,
|
||||
attr_sp: Span,
|
||||
_meta_item: &ast::MetaItem,
|
||||
meta_item: &ast::MetaItem,
|
||||
item: Annotatable,
|
||||
) -> Vec<Annotatable> {
|
||||
check_builtin_macro_attribute(cx, meta_item, sym::bench);
|
||||
expand_test_or_bench(cx, attr_sp, item, true)
|
||||
}
|
||||
|
||||
|
@ -9,10 +9,11 @@
|
||||
// We mark item with an inert attribute "rustc_test_marker" which the test generation
|
||||
// logic will pick up on.
|
||||
|
||||
use syntax::ast;
|
||||
use syntax::attr::check_builtin_macro_attribute;
|
||||
use syntax::ext::base::*;
|
||||
use syntax::ext::build::AstBuilder;
|
||||
use syntax::ext::hygiene::SyntaxContext;
|
||||
use syntax::ast;
|
||||
use syntax::source_map::respan;
|
||||
use syntax::symbol::sym;
|
||||
use syntax_pos::Span;
|
||||
@ -20,9 +21,11 @@ use syntax_pos::Span;
|
||||
pub fn expand(
|
||||
ecx: &mut ExtCtxt<'_>,
|
||||
attr_sp: Span,
|
||||
_meta_item: &ast::MetaItem,
|
||||
meta_item: &ast::MetaItem,
|
||||
anno_item: Annotatable
|
||||
) -> Vec<Annotatable> {
|
||||
check_builtin_macro_attribute(ecx, meta_item, sym::test_case);
|
||||
|
||||
if !ecx.ecfg.should_test { return vec![]; }
|
||||
|
||||
let sp = attr_sp.with_ctxt(SyntaxContext::empty().apply_mark(ecx.current_expansion.id));
|
||||
|
22
src/test/run-pass/allocator/custom-in-block.rs
Normal file
22
src/test/run-pass/allocator/custom-in-block.rs
Normal file
@ -0,0 +1,22 @@
|
||||
// run-pass
|
||||
// no-prefer-dynamic
|
||||
// aux-build:custom.rs
|
||||
// aux-build:helper.rs
|
||||
|
||||
extern crate custom;
|
||||
extern crate helper;
|
||||
|
||||
use custom::A;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
fn main() {
|
||||
#[global_allocator]
|
||||
pub static GLOBAL: A = A(AtomicUsize::new(0));
|
||||
|
||||
let n = GLOBAL.0.load(Ordering::SeqCst);
|
||||
let s = Box::new(0);
|
||||
helper::work_with(&s);
|
||||
assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 1);
|
||||
drop(s);
|
||||
assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2);
|
||||
}
|
26
src/test/run-pass/allocator/custom-in-submodule.rs
Normal file
26
src/test/run-pass/allocator/custom-in-submodule.rs
Normal file
@ -0,0 +1,26 @@
|
||||
// run-pass
|
||||
// no-prefer-dynamic
|
||||
// aux-build:custom.rs
|
||||
// aux-build:helper.rs
|
||||
|
||||
extern crate custom;
|
||||
extern crate helper;
|
||||
|
||||
use custom::A;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
mod submodule {
|
||||
use super::*;
|
||||
|
||||
#[global_allocator]
|
||||
pub static GLOBAL: A = A(AtomicUsize::new(0));
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let n = submodule::GLOBAL.0.load(Ordering::SeqCst);
|
||||
let s = Box::new(0);
|
||||
helper::work_with(&s);
|
||||
assert_eq!(submodule::GLOBAL.0.load(Ordering::SeqCst), n + 1);
|
||||
drop(s);
|
||||
assert_eq!(submodule::GLOBAL.0.load(Ordering::SeqCst), n + 2);
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
// Tests that it is possible to create a global allocator in a submodule, rather than in the crate
|
||||
// root.
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use std::{
|
||||
alloc::{GlobalAlloc, Layout},
|
||||
ptr,
|
||||
};
|
||||
|
||||
struct MyAlloc;
|
||||
|
||||
unsafe impl GlobalAlloc for MyAlloc {
|
||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {}
|
||||
}
|
||||
|
||||
mod submod {
|
||||
use super::MyAlloc;
|
||||
|
||||
#[global_allocator]
|
||||
static MY_HEAP: MyAlloc = MyAlloc; //~ ERROR global_allocator
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -1,8 +0,0 @@
|
||||
error: `global_allocator` cannot be used in submodules
|
||||
--> $DIR/allocator-submodule.rs:25:5
|
||||
|
|
||||
LL | static MY_HEAP: MyAlloc = MyAlloc;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
13
src/test/ui/allocator/allocator-args.rs
Normal file
13
src/test/ui/allocator/allocator-args.rs
Normal file
@ -0,0 +1,13 @@
|
||||
use std::alloc::{GlobalAlloc, Layout};
|
||||
|
||||
struct A;
|
||||
|
||||
unsafe impl GlobalAlloc for A {
|
||||
unsafe fn alloc(&self, _: Layout) -> *mut u8 { panic!() }
|
||||
unsafe fn dealloc(&self, _: *mut u8, _: Layout) { panic!() }
|
||||
}
|
||||
|
||||
#[global_allocator(malloc)] //~ ERROR malformed `global_allocator` attribute input
|
||||
static S: A = A;
|
||||
|
||||
fn main() {}
|
8
src/test/ui/allocator/allocator-args.stderr
Normal file
8
src/test/ui/allocator/allocator-args.stderr
Normal file
@ -0,0 +1,8 @@
|
||||
error: malformed `global_allocator` attribute input
|
||||
--> $DIR/allocator-args.rs:10:1
|
||||
|
|
||||
LL | #[global_allocator(malloc)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[global_allocator]`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -4,6 +4,6 @@ use std::alloc::System;
|
||||
static A: System = System;
|
||||
#[global_allocator]
|
||||
static B: System = System;
|
||||
//~^ ERROR: cannot define more than one `#[global_allocator]`
|
||||
//~^ ERROR: cannot define multiple global allocators
|
||||
|
||||
fn main() {}
|
||||
|
@ -1,8 +1,14 @@
|
||||
error: cannot define more than one `#[global_allocator]`
|
||||
error: cannot define multiple global allocators
|
||||
--> $DIR/two-allocators.rs:6:1
|
||||
|
|
||||
LL | static B: System = System;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: the previous global allocator is defined here
|
||||
--> $DIR/two-allocators.rs:4:1
|
||||
|
|
||||
LL | static A: System = System;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -1,3 +1,13 @@
|
||||
warning: attribute must be of the form `#[bench]`
|
||||
--> $DIR/issue-43106-gating-of-bench.rs:15:1
|
||||
|
|
||||
LL | #![bench = "4100"]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(ill_formed_attribute_input)]` on by default
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
|
||||
|
||||
error[E0601]: `main` function not found in crate `issue_43106_gating_of_bench`
|
||||
|
|
||||
= note: consider adding a `main` function to `$DIR/issue-43106-gating-of-bench.rs`
|
||||
|
@ -1,3 +1,13 @@
|
||||
warning: attribute must be of the form `#[test]`
|
||||
--> $DIR/issue-43106-gating-of-test.rs:10:1
|
||||
|
|
||||
LL | #![test = "4200"]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `#[warn(ill_formed_attribute_input)]` on by default
|
||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||
= note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
|
||||
|
||||
error[E0601]: `main` function not found in crate `issue_43106_gating_of_test`
|
||||
|
|
||||
= note: consider adding a `main` function to `$DIR/issue-43106-gating-of-test.rs`
|
||||
|
@ -344,7 +344,7 @@ fn get_and_check_lib_features(base_src_path: &Path,
|
||||
Ok((name, f)) => {
|
||||
let mut check_features = |f: &Feature, list: &Features, display: &str| {
|
||||
if let Some(ref s) = list.get(name) {
|
||||
if f.tracking_issue != s.tracking_issue {
|
||||
if f.tracking_issue != s.tracking_issue && f.level != Status::Stable {
|
||||
tidy_error!(bad,
|
||||
"{}:{}: mismatches the `issue` in {}",
|
||||
file.display(),
|
||||
|
Loading…
Reference in New Issue
Block a user