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:
Mazdak Farrokhzad 2019-07-25 23:21:00 +02:00 committed by GitHub
commit e1de70b045
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 513 additions and 593 deletions

View File

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

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

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

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

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

View File

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

View File

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

View 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);
}

View 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);
}

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

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

View File

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

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