syntax_ext: Turn #[global_allocator] into a regular attribute macro

This commit is contained in:
Vadim Petrochenkov 2019-07-19 00:24:58 +03:00
parent a93fdfedf3
commit 433024147a
19 changed files with 206 additions and 236 deletions

View File

@ -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"]

View File

@ -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);

View File

@ -468,7 +468,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)
});
@ -494,19 +494,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", || {
syntax_ext::global_allocator::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 {

View File

@ -25,10 +25,9 @@ use std::{cmp, fs};
use syntax::ast;
use syntax::attr;
use syntax::ext::allocator::AllocatorKind;
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,

View File

@ -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"]

View File

@ -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
}

View File

@ -1,3 +1,7 @@
use crate::{ast, attr, visit};
use crate::symbol::{sym, Symbol};
use syntax_pos::Span;
#[derive(Clone, Copy)]
pub enum AllocatorKind {
Global,
@ -51,3 +55,21 @@ pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[
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
}

View File

@ -1117,7 +1117,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]` \

View File

@ -1,162 +1,95 @@
use log::debug;
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::{
allocator::{AllocatorKind, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS},
base::{ExtCtxt, MacroKind, Resolver},
build::AstBuilder,
expand::ExpansionConfig,
hygiene::ExpnId,
},
mut_visit::{self, MutVisitor},
parse::ParseSess,
ptr::P,
symbol::{kw, sym}
};
use errors::Applicability;
use syntax::ast::{self, Arg, Attribute, Expr, FnHeader, Generics, Ident, Item};
use syntax::ast::{ItemKind, Mutability, Ty, TyKind, Unsafety, VisibilityKind};
use syntax::source_map::respan;
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};
use syntax_pos::Span;
pub fn modify(
sess: &ParseSess,
resolver: &mut dyn Resolver,
krate: &mut Crate,
crate_name: String,
handler: &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 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]
pub fn expand(
ecx: &mut ExtCtxt<'_>,
span: Span,
meta_item: &ast::MetaItem,
item: Annotatable,
) -> Vec<Annotatable> {
if !meta_item.is_word() {
let msg = format!("malformed `{}` attribute input", meta_item.path);
ecx.parse_sess.span_diagnostic.struct_span_err(span, &msg)
.span_suggestion(
span,
"must be of the form",
format!("`#[{}]`", meta_item.path),
Applicability::MachineApplicable
).emit();
}
// 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");
}
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),
};
// `visit_mac` is disabled by default. Enable it here.
fn visit_mac(&mut self, mac: &mut Mac) {
mut_visit::noop_visit_mac(mac, self)
}
// Generate a bunch of new items using the AllocFnFactory
let span = item.span.with_ctxt(SyntaxContext::empty().apply_mark(ecx.current_expansion.mark));
let f = AllocFnFactory {
span,
kind: AllocatorKind::Global,
global: item.ident,
core: Ident::with_empty_ctxt(sym::core),
cx: ecx,
};
// 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);
// Return the item and new submodule
vec![Annotatable::Item(item), Annotatable::Item(module)]
}
struct AllocFnFactory<'a> {
struct AllocFnFactory<'a, 'b> {
span: Span,
kind: AllocatorKind,
global: Ident,
core: Ident,
cx: ExtCtxt<'a>,
cx: &'b ExtCtxt<'a>,
}
impl AllocFnFactory<'_> {
impl AllocFnFactory<'_, '_> {
fn allocator_fn(&self, method: &AllocatorMethod) -> P<Item> {
let mut abi_args = Vec::new();
let mut i = 0;

View File

@ -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;
@ -37,7 +38,6 @@ mod test_case;
mod trace_macros;
pub mod deriving;
pub mod global_allocator;
pub mod proc_macro_decls;
pub mod proc_macro_impl;
@ -152,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 {

View File

@ -0,0 +1,21 @@
// 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 s = Box::new(0);
helper::work_with(&s);
assert_eq!(GLOBAL.0.load(Ordering::SeqCst), 1);
drop(s);
assert_eq!(GLOBAL.0.load(Ordering::SeqCst), 2);
}

View File

@ -0,0 +1,25 @@
// 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 s = Box::new(0);
helper::work_with(&s);
assert_eq!(submodule::GLOBAL.0.load(Ordering::SeqCst), 1);
drop(s);
assert_eq!(submodule::GLOBAL.0.load(Ordering::SeqCst), 2);
}

View File

@ -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() {}

View File

@ -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

View 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() {}

View 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

View File

@ -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() {}

View File

@ -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

View File

@ -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(),