syntax_ext: Turn #[global_allocator]
into a regular attribute macro
This commit is contained in:
parent
a93fdfedf3
commit
433024147a
@ -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"]
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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,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
|
||||
}
|
||||
|
@ -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]` \
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
21
src/test/run-pass/allocator/custom-in-block.rs
Normal file
21
src/test/run-pass/allocator/custom-in-block.rs
Normal 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);
|
||||
}
|
25
src/test/run-pass/allocator/custom-in-submodule.rs
Normal file
25
src/test/run-pass/allocator/custom-in-submodule.rs
Normal 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);
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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