From a93fdfedf36dcb909d90cbf963b087c5873bec1d Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 16 Jul 2019 21:06:17 +0300 Subject: Merge `rustc_allocator` into `libsyntax_ext` --- src/libsyntax_ext/global_allocator.rs | 296 ++++++++++++++++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 src/libsyntax_ext/global_allocator.rs (limited to 'src/libsyntax_ext/global_allocator.rs') diff --git a/src/libsyntax_ext/global_allocator.rs b/src/libsyntax_ext/global_allocator.rs new file mode 100644 index 00000000000..e8f94bff144 --- /dev/null +++ b/src/libsyntax_ext/global_allocator.rs @@ -0,0 +1,296 @@ +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 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, + + // 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) -> SmallVec<[P; 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 { + 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 { + 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 { + 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, + ident: &mut dyn FnMut() -> Ident, + ) -> P { + 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) -> (P, P) { + 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 { + let usize = self.cx.path_ident(self.span, Ident::with_empty_ctxt(sym::usize)); + self.cx.ty_path(usize) + } + + fn ptr_u8(&self) -> P { + 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) + } +} -- cgit 1.4.1-3-g733a5 From 433024147ae1e9795fe7e94cb1810a17fd37fa51 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 19 Jul 2019 00:24:58 +0300 Subject: syntax_ext: Turn `#[global_allocator]` into a regular attribute macro --- src/libcore/macros.rs | 7 + src/librustc/middle/dead.rs | 5 - src/librustc_interface/passes.rs | 15 +- src/librustc_metadata/creader.rs | 29 +-- src/librustc_metadata/lib.rs | 5 +- src/librustc_passes/ast_validation.rs | 10 +- src/libsyntax/ext/allocator.rs | 22 ++ src/libsyntax/feature_gate.rs | 1 - src/libsyntax_ext/global_allocator.rs | 223 +++++++-------------- src/libsyntax_ext/lib.rs | 8 +- src/test/run-pass/allocator/custom-in-block.rs | 21 ++ src/test/run-pass/allocator/custom-in-submodule.rs | 25 +++ src/test/ui/allocator-submodule.rs | 28 --- src/test/ui/allocator-submodule.stderr | 8 - src/test/ui/allocator/allocator-args.rs | 13 ++ src/test/ui/allocator/allocator-args.stderr | 8 + src/test/ui/allocator/two-allocators.rs | 2 +- src/test/ui/allocator/two-allocators.stderr | 8 +- src/tools/tidy/src/features.rs | 2 +- 19 files changed, 205 insertions(+), 235 deletions(-) create mode 100644 src/test/run-pass/allocator/custom-in-block.rs create mode 100644 src/test/run-pass/allocator/custom-in-submodule.rs delete mode 100644 src/test/ui/allocator-submodule.rs delete mode 100644 src/test/ui/allocator-submodule.stderr create mode 100644 src/test/ui/allocator/allocator-args.rs create mode 100644 src/test/ui/allocator/allocator-args.stderr (limited to 'src/libsyntax_ext/global_allocator.rs') diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index 296bb43f9fa..17781798946 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -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"] diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index 4c27318c3e1..88de77829a6 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -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); diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs index d4a922ddd49..b460a908f1b 100644 --- a/src/librustc_interface/passes.rs +++ b/src/librustc_interface/passes.rs @@ -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 { diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 31f70155b4e..3404ec5e173 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -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, diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index 826349362db..30bb37e4680 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -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"] diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 56063596299..b550029d978 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -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`. /// 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 } diff --git a/src/libsyntax/ext/allocator.rs b/src/libsyntax/ext/allocator.rs index f2c6bf27cee..99aeb5414c5 100644 --- a/src/libsyntax/ext/allocator.rs +++ b/src/libsyntax/ext/allocator.rs @@ -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 { + struct Finder { name: Symbol, spans: Vec } + 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 +} diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 43f0eaae7c9..214160f1079 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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]` \ diff --git a/src/libsyntax_ext/global_allocator.rs b/src/libsyntax_ext/global_allocator.rs index e8f94bff144..785636abb12 100644 --- a/src/libsyntax_ext/global_allocator.rs +++ b/src/libsyntax_ext/global_allocator.rs @@ -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, - - // 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) -> SmallVec<[P; 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]; - } +pub fn expand( + ecx: &mut ExtCtxt<'_>, + span: Span, + meta_item: &ast::MetaItem, + item: Annotatable, +) -> Vec { + 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 self.found { - self.handler - .span_err(item.span, "cannot define more than one `#[global_allocator]`"); - return smallvec![item]; + 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)), } - 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] - } + _ => 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.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)), + ); - // 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"); - } + // 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); - // `visit_mac` is disabled by default. Enable it here. - fn visit_mac(&mut self, mac: &mut Mac) { - mut_visit::noop_visit_mac(mac, self) - } + // 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 { let mut abi_args = Vec::new(); let mut i = 0; diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index cd7ac5fe2c6..400bfe796bb 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -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 { diff --git a/src/test/run-pass/allocator/custom-in-block.rs b/src/test/run-pass/allocator/custom-in-block.rs new file mode 100644 index 00000000000..506154ec601 --- /dev/null +++ b/src/test/run-pass/allocator/custom-in-block.rs @@ -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); +} diff --git a/src/test/run-pass/allocator/custom-in-submodule.rs b/src/test/run-pass/allocator/custom-in-submodule.rs new file mode 100644 index 00000000000..f9c4ad7668b --- /dev/null +++ b/src/test/run-pass/allocator/custom-in-submodule.rs @@ -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); +} diff --git a/src/test/ui/allocator-submodule.rs b/src/test/ui/allocator-submodule.rs deleted file mode 100644 index 7a8d86b8da1..00000000000 --- a/src/test/ui/allocator-submodule.rs +++ /dev/null @@ -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() {} diff --git a/src/test/ui/allocator-submodule.stderr b/src/test/ui/allocator-submodule.stderr deleted file mode 100644 index 91c7c0f6b8e..00000000000 --- a/src/test/ui/allocator-submodule.stderr +++ /dev/null @@ -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 - diff --git a/src/test/ui/allocator/allocator-args.rs b/src/test/ui/allocator/allocator-args.rs new file mode 100644 index 00000000000..1033f947c5f --- /dev/null +++ b/src/test/ui/allocator/allocator-args.rs @@ -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() {} diff --git a/src/test/ui/allocator/allocator-args.stderr b/src/test/ui/allocator/allocator-args.stderr new file mode 100644 index 00000000000..d8ae7130e5d --- /dev/null +++ b/src/test/ui/allocator/allocator-args.stderr @@ -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 + diff --git a/src/test/ui/allocator/two-allocators.rs b/src/test/ui/allocator/two-allocators.rs index 0f81fc41823..aa1291e77ae 100644 --- a/src/test/ui/allocator/two-allocators.rs +++ b/src/test/ui/allocator/two-allocators.rs @@ -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() {} diff --git a/src/test/ui/allocator/two-allocators.stderr b/src/test/ui/allocator/two-allocators.stderr index 6b0c2b2a25d..ed0aa13eb80 100644 --- a/src/test/ui/allocator/two-allocators.stderr +++ b/src/test/ui/allocator/two-allocators.stderr @@ -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 diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs index 1841beb1fd1..ade61abb748 100644 --- a/src/tools/tidy/src/features.rs +++ b/src/tools/tidy/src/features.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(), -- cgit 1.4.1-3-g733a5 From 76b1ffaf6c70abd3fa4da2e694dc709116258098 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 19 Jul 2019 02:10:36 +0300 Subject: syntax_ext: Reuse built-in attribute template checking for macro attributes --- src/librustc_lint/builtin.rs | 4 +- src/libsyntax/attr/builtin.rs | 96 ++++++++++++++++++++++ src/libsyntax/feature_gate.rs | 90 +------------------- src/libsyntax_ext/global_allocator.rs | 19 ++--- src/libsyntax_ext/test.rs | 11 ++- src/libsyntax_ext/test_case.rs | 7 +- src/test/ui/allocator/allocator-args.stderr | 2 +- .../issue-43106-gating-of-bench.stderr | 8 +- .../feature-gate/issue-43106-gating-of-test.stderr | 8 +- 9 files changed, 132 insertions(+), 113 deletions(-) (limited to 'src/libsyntax_ext/global_allocator.rs') diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 4105e030477..b63d14ca949 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -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}; diff --git a/src/libsyntax/attr/builtin.rs b/src/libsyntax/attr/builtin.rs index b41f1047fcb..71309441652 100644 --- a/src/libsyntax/attr/builtin.rs +++ b/src/libsyntax/attr/builtin.rs @@ -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,75 @@ 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; + + 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(), + } +} diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 214160f1079..72184b0bd64 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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)]`. @@ -1901,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> { @@ -2005,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. diff --git a/src/libsyntax_ext/global_allocator.rs b/src/libsyntax_ext/global_allocator.rs index 785636abb12..196db3d7baa 100644 --- a/src/libsyntax_ext/global_allocator.rs +++ b/src/libsyntax_ext/global_allocator.rs @@ -1,31 +1,22 @@ -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::ast::{self, Arg, Attribute, Expr, FnHeader, Generics, Ident, Item}; +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::source_map::respan; use syntax::symbol::{kw, sym}; use syntax_pos::Span; pub fn expand( ecx: &mut ExtCtxt<'_>, - span: Span, + _span: Span, meta_item: &ast::MetaItem, item: Annotatable, ) -> Vec { - 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(); - } + 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"); diff --git a/src/libsyntax_ext/test.rs b/src/libsyntax_ext/test.rs index f8755a1d1d7..d381c42f9ce 100644 --- a/src/libsyntax_ext/test.rs +++ b/src/libsyntax_ext/test.rs @@ -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 { + 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 { + check_builtin_macro_attribute(cx, meta_item, sym::bench); expand_test_or_bench(cx, attr_sp, item, true) } diff --git a/src/libsyntax_ext/test_case.rs b/src/libsyntax_ext/test_case.rs index 355f2428e08..ea4a8d541ab 100644 --- a/src/libsyntax_ext/test_case.rs +++ b/src/libsyntax_ext/test_case.rs @@ -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 { + 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)); diff --git a/src/test/ui/allocator/allocator-args.stderr b/src/test/ui/allocator/allocator-args.stderr index d8ae7130e5d..dfff2a7e709 100644 --- a/src/test/ui/allocator/allocator-args.stderr +++ b/src/test/ui/allocator/allocator-args.stderr @@ -2,7 +2,7 @@ error: malformed `global_allocator` attribute input --> $DIR/allocator-args.rs:10:1 | LL | #[global_allocator(malloc)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: ``#[global_allocator]`` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[global_allocator]` error: aborting due to previous error diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-bench.stderr b/src/test/ui/feature-gate/issue-43106-gating-of-bench.stderr index 503ef020d96..e82cb93c635 100644 --- a/src/test/ui/feature-gate/issue-43106-gating-of-bench.stderr +++ b/src/test/ui/feature-gate/issue-43106-gating-of-bench.stderr @@ -1,7 +1,13 @@ +error: malformed `bench` attribute input + --> $DIR/issue-43106-gating-of-bench.rs:15:1 + | +LL | #![bench = "4100"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[bench]` + 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` -error: aborting due to previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0601`. diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-test.stderr b/src/test/ui/feature-gate/issue-43106-gating-of-test.stderr index 2ab35be43c5..9866fa3730e 100644 --- a/src/test/ui/feature-gate/issue-43106-gating-of-test.stderr +++ b/src/test/ui/feature-gate/issue-43106-gating-of-test.stderr @@ -1,7 +1,13 @@ +error: malformed `test` attribute input + --> $DIR/issue-43106-gating-of-test.rs:10:1 + | +LL | #![test = "4200"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[test]` + 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` -error: aborting due to previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0601`. -- cgit 1.4.1-3-g733a5 From bf8fc8adfc76f31a8fb6a4cb0dee02f262bd766a Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 19 Jul 2019 02:51:07 +0300 Subject: syntax_ext: Improve and simplify code generated by `#[global_allocator]` Instead of ``` mod allocator_abi { /* methods */ } ``` we now generate ``` const _: () = { /* methods */ } ``` and use `std_path` for paths referring to standard library entities. This way we no longer need to generate `use` and `extern crate` imports, and `#[global_allocator]` starts working inside unnamed blocks. --- src/libsyntax_ext/global_allocator.rs | 85 +++++++--------------- src/test/run-pass/allocator/custom-in-block.rs | 5 +- src/test/run-pass/allocator/custom-in-submodule.rs | 5 +- 3 files changed, 34 insertions(+), 61 deletions(-) (limited to 'src/libsyntax_ext/global_allocator.rs') diff --git a/src/libsyntax_ext/global_allocator.rs b/src/libsyntax_ext/global_allocator.rs index 196db3d7baa..71c9e6cd23d 100644 --- a/src/libsyntax_ext/global_allocator.rs +++ b/src/libsyntax_ext/global_allocator.rs @@ -1,13 +1,12 @@ -use syntax::ast::{ItemKind, Mutability, Ty, TyKind, Unsafety, VisibilityKind}; -use syntax::ast::{self, Arg, Attribute, Expr, FnHeader, Generics, Ident, Item}; +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::source_map::respan; -use syntax::symbol::{kw, sym}; +use syntax::symbol::{kw, sym, Symbol}; use syntax_pos::Span; pub fn expand( @@ -36,52 +35,31 @@ pub fn expand( 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 item statements for the allocator methods. + let stmts = ALLOCATOR_METHODS.iter().map(|method| f.allocator_fn(method)).collect(); - // 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); + // 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 item and new submodule - vec![Annotatable::Item(item), Annotatable::Item(module)] + // 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, - core: Ident, cx: &'b ExtCtxt<'a>, } impl AllocFnFactory<'_, '_> { - fn allocator_fn(&self, method: &AllocatorMethod) -> P { + fn allocator_fn(&self, method: &AllocatorMethod) -> Stmt { let mut abi_args = Vec::new(); let mut i = 0; let ref mut mk = || { @@ -105,25 +83,22 @@ impl AllocFnFactory<'_, '_> { Generics::default(), self.cx.block_expr(output_expr), ); - self.cx.item( + 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 { - 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 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); @@ -153,16 +128,12 @@ impl AllocFnFactory<'_, '_> { 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 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]); diff --git a/src/test/run-pass/allocator/custom-in-block.rs b/src/test/run-pass/allocator/custom-in-block.rs index 506154ec601..12813a1fc8b 100644 --- a/src/test/run-pass/allocator/custom-in-block.rs +++ b/src/test/run-pass/allocator/custom-in-block.rs @@ -13,9 +13,10 @@ 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), 1); + assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 1); drop(s); - assert_eq!(GLOBAL.0.load(Ordering::SeqCst), 2); + assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2); } diff --git a/src/test/run-pass/allocator/custom-in-submodule.rs b/src/test/run-pass/allocator/custom-in-submodule.rs index f9c4ad7668b..ea341b1ac14 100644 --- a/src/test/run-pass/allocator/custom-in-submodule.rs +++ b/src/test/run-pass/allocator/custom-in-submodule.rs @@ -17,9 +17,10 @@ mod submodule { } 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), 1); + assert_eq!(submodule::GLOBAL.0.load(Ordering::SeqCst), n + 1); drop(s); - assert_eq!(submodule::GLOBAL.0.load(Ordering::SeqCst), 2); + assert_eq!(submodule::GLOBAL.0.load(Ordering::SeqCst), n + 2); } -- cgit 1.4.1-3-g733a5 From a0c2c640d54fa1622c2fea4accae1025bf109c47 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 20 Jul 2019 13:44:11 +0300 Subject: Fix rebase --- src/libsyntax_ext/global_allocator.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/libsyntax_ext/global_allocator.rs') diff --git a/src/libsyntax_ext/global_allocator.rs b/src/libsyntax_ext/global_allocator.rs index 71c9e6cd23d..33072487e19 100644 --- a/src/libsyntax_ext/global_allocator.rs +++ b/src/libsyntax_ext/global_allocator.rs @@ -30,7 +30,7 @@ pub fn expand( }; // Generate a bunch of new items using the AllocFnFactory - let span = item.span.with_ctxt(SyntaxContext::empty().apply_mark(ecx.current_expansion.mark)); + let span = item.span.with_ctxt(SyntaxContext::empty().apply_mark(ecx.current_expansion.id)); let f = AllocFnFactory { span, kind: AllocatorKind::Global, -- cgit 1.4.1-3-g733a5