Skip to content

Commit 446aef6

Browse files
committed
Auto merge of #51335 - mark-i-m:allocator, r=oli-obk
Prohibit `global_allocator` in submodules Background: #44113 is caused by weird interactions with hygiene. Hygiene is hard. After a lot of playing around, we decided that the best path forward would be to prohibit `global_allocator`s from being in submodules for now. When somebody gets it working, we can re-enable it. This PR contains the following - Some hygiene "fixes" -- things I suspect are the correct thing to do that will make life easier in the future. This includes using call_site hygiene for the generated module and passing the correct crate name to the expansion config. - Comments and minor formatting fixes - Some debugging code - Code to prohibit `global_allocator` in submodules - Test checking that the proper error occurs. cc #44113 #49320 #51241 r? @alexcrichton
2 parents 5f9c7f9 + 16d7f87 commit 446aef6

File tree

7 files changed

+140
-29
lines changed

7 files changed

+140
-29
lines changed

src/Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -2035,6 +2035,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
20352035
name = "rustc_allocator"
20362036
version = "0.0.0"
20372037
dependencies = [
2038+
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
20382039
"rustc 0.0.0",
20392040
"rustc_errors 0.0.0",
20402041
"rustc_target 0.0.0",

src/librustc_allocator/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ rustc_errors = { path = "../librustc_errors" }
1414
rustc_target = { path = "../librustc_target" }
1515
syntax = { path = "../libsyntax" }
1616
syntax_pos = { path = "../libsyntax_pos" }
17+
log = "0.4"

src/librustc_allocator/expand.rs

+79-28
Original file line numberDiff line numberDiff line change
@@ -10,37 +10,45 @@
1010

1111
use rustc::middle::allocator::AllocatorKind;
1212
use rustc_errors;
13-
use syntax::ast::{Attribute, Crate, LitKind, StrStyle};
14-
use syntax::ast::{Arg, FnHeader, Generics, Mac, Mutability, Ty, Unsafety};
15-
use syntax::ast::{self, Expr, Ident, Item, ItemKind, TyKind, VisibilityKind};
16-
use syntax::attr;
17-
use syntax::codemap::respan;
18-
use syntax::codemap::{ExpnInfo, MacroAttribute};
19-
use syntax::ext::base::ExtCtxt;
20-
use syntax::ext::base::Resolver;
21-
use syntax::ext::build::AstBuilder;
22-
use syntax::ext::expand::ExpansionConfig;
23-
use syntax::ext::hygiene::{self, Mark, SyntaxContext};
24-
use syntax::fold::{self, Folder};
25-
use syntax::parse::ParseSess;
26-
use syntax::ptr::P;
27-
use syntax::symbol::Symbol;
28-
use syntax::util::small_vector::SmallVector;
29-
use syntax_pos::{Span, DUMMY_SP};
13+
use syntax::{
14+
ast::{
15+
self, Arg, Attribute, Crate, Expr, FnHeader, Generics, Ident, Item, ItemKind,
16+
LitKind, Mac, Mod, Mutability, StrStyle, Ty, TyKind, Unsafety, VisibilityKind,
17+
},
18+
attr,
19+
codemap::{
20+
respan, ExpnInfo, MacroAttribute,
21+
},
22+
ext::{
23+
base::{ExtCtxt, Resolver},
24+
build::AstBuilder,
25+
expand::ExpansionConfig,
26+
hygiene::{self, Mark, SyntaxContext},
27+
},
28+
fold::{self, Folder},
29+
parse::ParseSess,
30+
ptr::P,
31+
symbol::Symbol,
32+
util::small_vector::SmallVector,
33+
};
34+
use syntax_pos::Span;
3035

3136
use {AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS};
3237

3338
pub fn modify(
3439
sess: &ParseSess,
3540
resolver: &mut Resolver,
3641
krate: Crate,
42+
crate_name: String,
3743
handler: &rustc_errors::Handler,
3844
) -> ast::Crate {
3945
ExpandAllocatorDirectives {
4046
handler,
4147
sess,
4248
resolver,
4349
found: false,
50+
crate_name: Some(crate_name),
51+
in_submod: -1, // -1 to account for the "root" module
4452
}.fold_crate(krate)
4553
}
4654

@@ -49,10 +57,17 @@ struct ExpandAllocatorDirectives<'a> {
4957
handler: &'a rustc_errors::Handler,
5058
sess: &'a ParseSess,
5159
resolver: &'a mut Resolver,
60+
crate_name: Option<String>,
61+
62+
// For now, we disallow `global_allocator` in submodules because hygiene is hard. Keep track of
63+
// whether we are in a submodule or not. If `in_submod > 0` we are in a submodule.
64+
in_submod: isize,
5265
}
5366

5467
impl<'a> Folder for ExpandAllocatorDirectives<'a> {
5568
fn fold_item(&mut self, item: P<Item>) -> SmallVector<P<Item>> {
69+
debug!("in submodule {}", self.in_submod);
70+
5671
let name = if attr::contains_name(&item.attrs, "global_allocator") {
5772
"global_allocator"
5873
} else {
@@ -67,57 +82,93 @@ impl<'a> Folder for ExpandAllocatorDirectives<'a> {
6782
}
6883
}
6984

85+
if self.in_submod > 0 {
86+
self.handler
87+
.span_err(item.span, "`global_allocator` cannot be used in submodules");
88+
return SmallVector::one(item);
89+
}
90+
7091
if self.found {
71-
self.handler.span_err(
72-
item.span,
73-
"cannot define more than one \
74-
#[global_allocator]",
75-
);
92+
self.handler
93+
.span_err(item.span, "cannot define more than one #[global_allocator]");
7694
return SmallVector::one(item);
7795
}
7896
self.found = true;
7997

98+
// Create a fresh Mark for the new macro expansion we are about to do
8099
let mark = Mark::fresh(Mark::root());
81100
mark.set_expn_info(ExpnInfo {
82-
call_site: DUMMY_SP,
101+
call_site: item.span, // use the call site of the static
83102
def_site: None,
84103
format: MacroAttribute(Symbol::intern(name)),
85104
allow_internal_unstable: true,
86105
allow_internal_unsafe: false,
87106
edition: hygiene::default_edition(),
88107
});
108+
109+
// Tie the span to the macro expansion info we just created
89110
let span = item.span.with_ctxt(SyntaxContext::empty().apply_mark(mark));
90-
let ecfg = ExpansionConfig::default(name.to_string());
111+
112+
// Create an expansion config
113+
let ecfg = ExpansionConfig::default(self.crate_name.take().unwrap());
114+
115+
// Generate a bunch of new items using the AllocFnFactory
91116
let mut f = AllocFnFactory {
92117
span,
93118
kind: AllocatorKind::Global,
94119
global: item.ident,
95120
core: Ident::from_str("core"),
96121
cx: ExtCtxt::new(self.sess, ecfg, self.resolver),
97122
};
123+
124+
// We will generate a new submodule. To `use` the static from that module, we need to get
125+
// the `super::...` path.
98126
let super_path = f.cx.path(f.span, vec![Ident::from_str("super"), f.global]);
127+
128+
// Generate the items in the submodule
99129
let mut items = vec![
130+
// import `core` to use allocators
100131
f.cx.item_extern_crate(f.span, f.core),
132+
// `use` the `global_allocator` in `super`
101133
f.cx.item_use_simple(
102134
f.span,
103135
respan(f.span.shrink_to_lo(), VisibilityKind::Inherited),
104136
super_path,
105137
),
106138
];
107-
for method in ALLOCATOR_METHODS {
108-
items.push(f.allocator_fn(method));
109-
}
139+
140+
// Add the allocator methods to the submodule
141+
items.extend(
142+
ALLOCATOR_METHODS
143+
.iter()
144+
.map(|method| f.allocator_fn(method)),
145+
);
146+
147+
// Generate the submodule itself
110148
let name = f.kind.fn_name("allocator_abi");
111149
let allocator_abi = Ident::with_empty_ctxt(Symbol::gensym(&name));
112150
let module = f.cx.item_mod(span, span, allocator_abi, Vec::new(), items);
113151
let module = f.cx.monotonic_expander().fold_item(module).pop().unwrap();
114152

115-
let mut ret = SmallVector::new();
153+
// Return the item and new submodule
154+
let mut ret = SmallVector::with_capacity(2);
116155
ret.push(item);
117156
ret.push(module);
157+
118158
return ret;
119159
}
120160

161+
// If we enter a submodule, take note.
162+
fn fold_mod(&mut self, m: Mod) -> Mod {
163+
debug!("enter submodule");
164+
self.in_submod += 1;
165+
let ret = fold::noop_fold_mod(m, self);
166+
self.in_submod -= 1;
167+
debug!("exit submodule");
168+
ret
169+
}
170+
171+
// `fold_mac` is disabled by default. Enable it here.
121172
fn fold_mac(&mut self, mac: Mac) -> Mac {
122173
fold::noop_fold_mac(mac, self)
123174
}

src/librustc_allocator/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#![feature(rustc_private)]
1212

13+
#[macro_use] extern crate log;
1314
extern crate rustc;
1415
extern crate rustc_errors;
1516
extern crate rustc_target;

src/librustc_driver/driver.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -1051,10 +1051,19 @@ where
10511051
});
10521052
}
10531053

1054+
// Expand global allocators, which are treated as an in-tree proc macro
10541055
krate = time(sess, "creating allocators", || {
1055-
allocator::expand::modify(&sess.parse_sess, &mut resolver, krate, sess.diagnostic())
1056+
allocator::expand::modify(
1057+
&sess.parse_sess,
1058+
&mut resolver,
1059+
krate,
1060+
crate_name.to_string(),
1061+
sess.diagnostic(),
1062+
)
10561063
});
10571064

1065+
// Done with macro expansion!
1066+
10581067
after_expand(&krate)?;
10591068

10601069
if sess.opts.debugging_opts.input_stats {

src/test/ui/allocator-submodule.rs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Tests that it is possible to create a global allocator in a submodule, rather than in the crate
12+
// root.
13+
14+
#![feature(alloc, allocator_api, global_allocator)]
15+
16+
extern crate alloc;
17+
18+
use std::{
19+
alloc::{GlobalAlloc, Layout},
20+
ptr,
21+
};
22+
23+
struct MyAlloc;
24+
25+
unsafe impl GlobalAlloc for MyAlloc {
26+
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
27+
ptr::null_mut()
28+
}
29+
30+
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {}
31+
}
32+
33+
mod submod {
34+
use super::MyAlloc;
35+
36+
#[global_allocator]
37+
static MY_HEAP: MyAlloc = MyAlloc; //~ ERROR global_allocator
38+
}
39+
40+
fn main() {}
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: `global_allocator` cannot be used in submodules
2+
--> $DIR/allocator-submodule.rs:37:5
3+
|
4+
LL | static MY_HEAP: MyAlloc = MyAlloc; //~ ERROR global_allocator
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
7+
error: aborting due to previous error
8+

0 commit comments

Comments
 (0)