Skip to content

Commit 65b3abe

Browse files
committed
Auto merge of #40847 - jseyfried:decl_macro, r=nrc
Initial implementation of declarative macros 2.0 Implement declarative macros 2.0 (rust-lang/rfcs#1584) behind `#![feature(decl_macro)]`. Differences from `macro_rules!` include: - new syntax: `macro m(..) { .. }` instead of `macro_rules! m { (..) => { .. } }` - declarative macros are items: ```rust // crate A: mod foo { m!(); // use before definition; declaration order is irrelevant pub macro m() {} // `pub`, `pub(super)`, etc. work } fn main() { foo::m!(); // named like other items { use foo::m as n; n!(); } // imported like other items } pub use foo::m; // re-exported like other items // crate B: extern crate A; // no need for `#[macro_use]` A::foo::m!(); A::m!(); ``` - Racket-like hygiene for items, imports, methods, fields, type parameters, privacy, etc. - Intuitively, names in a macro definition are resolved in the macro definition's scope, not the scope in which the macro is used. - This [explaination](http://beautifulracket.com/explainer/hygiene.html) of hygiene for Racket applies here (except for the "Breaking Hygiene" section). I wrote a similar [explanation](https://github.com/jseyfried/rfcs/blob/hygiene/text/0000-hygiene.md) for Rust. - Generally speaking, if `fn f() { <body> }` resolves, `pub macro m() { <body> } ... m!()` also resolves, even if `m!()` is in a separate crate. - `::foo::bar` in a `macro` behaves like `$crate::foo::bar` in a `macro_rules!`, except it can access everything visible from the `macro` (thus more permissive). - See [`src/test/{run-pass, compile-fail}/hygiene`](afe7d89) for examples. Small example: ```rust mod foo { fn f() { println!("hello world"); } pub macro m() { f(); } } fn main() { foo::m!(); } ``` Limitations: - This does not address planned changes to matchers (`expr`,`ty`, etc.), c.f. #26361. - Lints (including stability and deprecation) and `unsafe` are not hygienic. - adding hygiene here will be mostly or entirely backwards compatible - Nested macro definitions (a `macro` inside another `macro`) don't always work correctly when invoked from external crates. - pending improvements in how we encode macro definitions in crate metadata - There is no way to "escape" hygiene without using a procedural macro. r? @nrc
2 parents 41976e2 + 97e01f6 commit 65b3abe

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+1557
-479
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# `decl_macro`
2+
3+
The tracking issue for this feature is: [#39412]
4+
5+
[#39412]: https://github.com/rust-lang/rust/issues/39412
6+
7+
------------------------
8+
9+
10+

src/librustc/hir/def.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ pub type ExportMap = NodeMap<Vec<Export>>;
117117

118118
#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
119119
pub struct Export {
120-
pub name: ast::Name, // The name of the target.
120+
pub ident: ast::Ident, // The name of the target.
121121
pub def: Def, // The definition of the target.
122122
pub span: Span, // The span of the target definition.
123123
}

src/librustc/hir/lowering.rs

+43-24
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ use hir::def_id::{DefIndex, DefId, CRATE_DEF_INDEX};
4747
use hir::def::{Def, PathResolution};
4848
use rustc_data_structures::indexed_vec::IndexVec;
4949
use session::Session;
50-
use util::nodemap::{DefIdMap, NodeMap};
50+
use util::nodemap::{DefIdMap, FxHashMap, NodeMap};
5151

5252
use std::collections::BTreeMap;
5353
use std::fmt::Debug;
@@ -77,6 +77,7 @@ pub struct LoweringContext<'a> {
7777
// a definition, then we can properly create the def id.
7878
parent_def: Option<DefIndex>,
7979
resolver: &'a mut Resolver,
80+
name_map: FxHashMap<Ident, Name>,
8081

8182
/// The items being lowered are collected here.
8283
items: BTreeMap<NodeId, hir::Item>,
@@ -126,6 +127,7 @@ pub fn lower_crate(sess: &Session,
126127
sess: sess,
127128
parent_def: None,
128129
resolver: resolver,
130+
name_map: FxHashMap(),
129131
items: BTreeMap::new(),
130132
trait_items: BTreeMap::new(),
131133
impl_items: BTreeMap::new(),
@@ -393,7 +395,7 @@ impl<'a> LoweringContext<'a> {
393395
}
394396

395397
fn allow_internal_unstable(&self, reason: &'static str, mut span: Span) -> Span {
396-
let mark = Mark::fresh();
398+
let mark = Mark::fresh(Mark::root());
397399
mark.set_expn_info(codemap::ExpnInfo {
398400
call_site: span,
399401
callee: codemap::NameAndSpan {
@@ -495,6 +497,14 @@ impl<'a> LoweringContext<'a> {
495497
}
496498
}
497499

500+
fn lower_ident(&mut self, ident: Ident) -> Name {
501+
let ident = ident.modern();
502+
if ident.ctxt == SyntaxContext::empty() {
503+
return ident.name;
504+
}
505+
*self.name_map.entry(ident).or_insert_with(|| Symbol::from_ident(ident))
506+
}
507+
498508
fn lower_opt_sp_ident(&mut self, o_id: Option<Spanned<Ident>>) -> Option<Spanned<Name>> {
499509
o_id.map(|sp_ident| respan(sp_ident.span, sp_ident.node.name))
500510
}
@@ -546,7 +556,7 @@ impl<'a> LoweringContext<'a> {
546556
fn lower_ty_binding(&mut self, b: &TypeBinding) -> hir::TypeBinding {
547557
hir::TypeBinding {
548558
id: self.lower_node_id(b.id),
549-
name: b.ident.name,
559+
name: self.lower_ident(b.ident),
550560
ty: self.lower_ty(&b.ty),
551561
span: b.span,
552562
}
@@ -844,7 +854,7 @@ impl<'a> LoweringContext<'a> {
844854
}
845855

846856
hir::PathSegment {
847-
name: segment.identifier.name,
857+
name: self.lower_ident(segment.identifier),
848858
parameters: parameters,
849859
}
850860
}
@@ -941,7 +951,7 @@ impl<'a> LoweringContext<'a> {
941951
}
942952

943953
fn lower_ty_param(&mut self, tp: &TyParam, add_bounds: &[TyParamBound]) -> hir::TyParam {
944-
let mut name = tp.ident.name;
954+
let mut name = self.lower_ident(tp.ident);
945955

946956
// Don't expose `Self` (recovered "keyword used as ident" parse error).
947957
// `rustc::ty` expects `Self` to be only used for a trait's `Self`.
@@ -975,7 +985,7 @@ impl<'a> LoweringContext<'a> {
975985
fn lower_lifetime(&mut self, l: &Lifetime) -> hir::Lifetime {
976986
hir::Lifetime {
977987
id: self.lower_node_id(l.id),
978-
name: l.name,
988+
name: self.lower_ident(l.ident),
979989
span: l.span,
980990
}
981991
}
@@ -1137,7 +1147,11 @@ impl<'a> LoweringContext<'a> {
11371147
hir::StructField {
11381148
span: f.span,
11391149
id: self.lower_node_id(f.id),
1140-
name: f.ident.map(|ident| ident.name).unwrap_or(Symbol::intern(&index.to_string())),
1150+
name: self.lower_ident(match f.ident {
1151+
Some(ident) => ident,
1152+
// FIXME(jseyfried) positional field hygiene
1153+
None => Ident { name: Symbol::intern(&index.to_string()), ctxt: f.span.ctxt },
1154+
}),
11411155
vis: self.lower_visibility(&f.vis, None),
11421156
ty: self.lower_ty(&f.ty),
11431157
attrs: self.lower_attrs(&f.attrs),
@@ -1146,7 +1160,7 @@ impl<'a> LoweringContext<'a> {
11461160

11471161
fn lower_field(&mut self, f: &Field) -> hir::Field {
11481162
hir::Field {
1149-
name: respan(f.ident.span, f.ident.node.name),
1163+
name: respan(f.ident.span, self.lower_ident(f.ident.node)),
11501164
expr: P(self.lower_expr(&f.expr)),
11511165
span: f.span,
11521166
is_shorthand: f.is_shorthand,
@@ -1371,7 +1385,7 @@ impl<'a> LoweringContext<'a> {
13711385
self.with_parent_def(i.id, |this| {
13721386
hir::TraitItem {
13731387
id: this.lower_node_id(i.id),
1374-
name: i.ident.name,
1388+
name: this.lower_ident(i.ident),
13751389
attrs: this.lower_attrs(&i.attrs),
13761390
node: match i.node {
13771391
TraitItemKind::Const(ref ty, ref default) => {
@@ -1421,7 +1435,7 @@ impl<'a> LoweringContext<'a> {
14211435
};
14221436
hir::TraitItemRef {
14231437
id: hir::TraitItemId { node_id: i.id },
1424-
name: i.ident.name,
1438+
name: self.lower_ident(i.ident),
14251439
span: i.span,
14261440
defaultness: self.lower_defaultness(Defaultness::Default, has_default),
14271441
kind: kind,
@@ -1432,7 +1446,7 @@ impl<'a> LoweringContext<'a> {
14321446
self.with_parent_def(i.id, |this| {
14331447
hir::ImplItem {
14341448
id: this.lower_node_id(i.id),
1435-
name: i.ident.name,
1449+
name: this.lower_ident(i.ident),
14361450
attrs: this.lower_attrs(&i.attrs),
14371451
vis: this.lower_visibility(&i.vis, None),
14381452
defaultness: this.lower_defaultness(i.defaultness, true /* [1] */),
@@ -1461,7 +1475,7 @@ impl<'a> LoweringContext<'a> {
14611475
fn lower_impl_item_ref(&mut self, i: &ImplItem) -> hir::ImplItemRef {
14621476
hir::ImplItemRef {
14631477
id: hir::ImplItemId { node_id: i.id },
1464-
name: i.ident.name,
1478+
name: self.lower_ident(i.ident),
14651479
span: i.span,
14661480
vis: self.lower_visibility(&i.vis, Some(i.id)),
14671481
defaultness: self.lower_defaultness(i.defaultness, true /* [1] */),
@@ -1501,17 +1515,23 @@ impl<'a> LoweringContext<'a> {
15011515

15021516
pub fn lower_item(&mut self, i: &Item) -> Option<hir::Item> {
15031517
let mut name = i.ident.name;
1518+
let mut vis = self.lower_visibility(&i.vis, None);
15041519
let attrs = self.lower_attrs(&i.attrs);
1505-
if let ItemKind::MacroDef(ref tts) = i.node {
1506-
if i.attrs.iter().any(|attr| attr.path == "macro_export") {
1520+
if let ItemKind::MacroDef(ref def) = i.node {
1521+
if !def.legacy || i.attrs.iter().any(|attr| attr.path == "macro_export") {
15071522
self.exported_macros.push(hir::MacroDef {
1508-
name: name, attrs: attrs, id: i.id, span: i.span, body: tts.clone().into(),
1523+
name: name,
1524+
vis: vis,
1525+
attrs: attrs,
1526+
id: i.id,
1527+
span: i.span,
1528+
body: def.stream(),
1529+
legacy: def.legacy,
15091530
});
15101531
}
15111532
return None;
15121533
}
15131534

1514-
let mut vis = self.lower_visibility(&i.vis, None);
15151535
let node = self.with_parent_def(i.id, |this| {
15161536
this.lower_item_kind(i.id, &mut name, &attrs, &mut vis, &i.node)
15171537
});
@@ -1654,7 +1674,7 @@ impl<'a> LoweringContext<'a> {
16541674
Spanned {
16551675
span: f.span,
16561676
node: hir::FieldPat {
1657-
name: f.node.ident.name,
1677+
name: self.lower_ident(f.node.ident),
16581678
pat: self.lower_pat(&f.node.pat),
16591679
is_shorthand: f.node.is_shorthand,
16601680
},
@@ -1824,7 +1844,7 @@ impl<'a> LoweringContext<'a> {
18241844
ExprKind::MethodCall(i, ref tps, ref args) => {
18251845
let tps = tps.iter().map(|x| self.lower_ty(x)).collect();
18261846
let args = args.iter().map(|x| self.lower_expr(x)).collect();
1827-
hir::ExprMethodCall(respan(i.span, i.node.name), tps, args)
1847+
hir::ExprMethodCall(respan(i.span, self.lower_ident(i.node)), tps, args)
18281848
}
18291849
ExprKind::Binary(binop, ref lhs, ref rhs) => {
18301850
let binop = self.lower_binop(binop);
@@ -1923,7 +1943,8 @@ impl<'a> LoweringContext<'a> {
19231943
P(self.lower_expr(er)))
19241944
}
19251945
ExprKind::Field(ref el, ident) => {
1926-
hir::ExprField(P(self.lower_expr(el)), respan(ident.span, ident.node.name))
1946+
hir::ExprField(P(self.lower_expr(el)),
1947+
respan(ident.span, self.lower_ident(ident.node)))
19271948
}
19281949
ExprKind::TupField(ref el, ident) => {
19291950
hir::ExprTupField(P(self.lower_expr(el)), ident)
@@ -2641,11 +2662,9 @@ impl<'a> LoweringContext<'a> {
26412662
let parent_def = self.parent_def.unwrap();
26422663
let def_id = {
26432664
let defs = self.resolver.definitions();
2644-
let def_path_data = DefPathData::Binding(name.as_str());
2645-
let def_index = defs.create_def_with_parent(parent_def,
2646-
id,
2647-
def_path_data,
2648-
REGULAR_SPACE);
2665+
let def_path_data = DefPathData::Binding(Ident::with_empty_ctxt(name));
2666+
let def_index = defs
2667+
.create_def_with_parent(parent_def, id, def_path_data, REGULAR_SPACE, Mark::root());
26492668
DefId::local(def_index)
26502669
};
26512670

src/librustc/hir/map/def_collector.rs

+25-24
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ use hir::def_id::{CRATE_DEF_INDEX, DefIndex, DefIndexAddressSpace};
1414
use syntax::ast::*;
1515
use syntax::ext::hygiene::Mark;
1616
use syntax::visit;
17-
use syntax::symbol::{Symbol, keywords};
17+
use syntax::symbol::keywords;
1818

1919
use hir::map::{ITEM_LIKE_SPACE, REGULAR_SPACE};
2020

2121
/// Creates def ids for nodes in the AST.
2222
pub struct DefCollector<'a> {
2323
definitions: &'a mut Definitions,
2424
parent_def: Option<DefIndex>,
25+
expansion: Mark,
2526
pub visit_macro_invoc: Option<&'a mut FnMut(MacroInvocationData)>,
2627
}
2728

@@ -32,9 +33,10 @@ pub struct MacroInvocationData {
3233
}
3334

3435
impl<'a> DefCollector<'a> {
35-
pub fn new(definitions: &'a mut Definitions) -> Self {
36+
pub fn new(definitions: &'a mut Definitions, expansion: Mark) -> Self {
3637
DefCollector {
3738
definitions: definitions,
39+
expansion: expansion,
3840
parent_def: None,
3941
visit_macro_invoc: None,
4042
}
@@ -54,7 +56,8 @@ impl<'a> DefCollector<'a> {
5456
-> DefIndex {
5557
let parent_def = self.parent_def.unwrap();
5658
debug!("create_def(node_id={:?}, data={:?}, parent_def={:?})", node_id, data, parent_def);
57-
self.definitions.create_def_with_parent(parent_def, node_id, data, address_space)
59+
self.definitions
60+
.create_def_with_parent(parent_def, node_id, data, address_space, self.expansion)
5861
}
5962

6063
pub fn with_parent<F: FnOnce(&mut Self)>(&mut self, parent_def: DefIndex, f: F) {
@@ -100,14 +103,14 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
100103
DefPathData::Impl,
101104
ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) | ItemKind::Trait(..) |
102105
ItemKind::ExternCrate(..) | ItemKind::ForeignMod(..) | ItemKind::Ty(..) =>
103-
DefPathData::TypeNs(i.ident.name.as_str()),
106+
DefPathData::TypeNs(i.ident.modern()),
104107
ItemKind::Mod(..) if i.ident == keywords::Invalid.ident() => {
105108
return visit::walk_item(self, i);
106109
}
107-
ItemKind::Mod(..) => DefPathData::Module(i.ident.name.as_str()),
110+
ItemKind::Mod(..) => DefPathData::Module(i.ident.modern()),
108111
ItemKind::Static(..) | ItemKind::Const(..) | ItemKind::Fn(..) =>
109-
DefPathData::ValueNs(i.ident.name.as_str()),
110-
ItemKind::MacroDef(..) => DefPathData::MacroDef(i.ident.name.as_str()),
112+
DefPathData::ValueNs(i.ident.modern()),
113+
ItemKind::MacroDef(..) => DefPathData::MacroDef(i.ident.modern()),
111114
ItemKind::Mac(..) => return self.visit_macro_invoc(i.id, false),
112115
ItemKind::GlobalAsm(..) => DefPathData::Misc,
113116
ItemKind::Use(ref view_path) => {
@@ -135,15 +138,13 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
135138
for v in &enum_definition.variants {
136139
let variant_def_index =
137140
this.create_def(v.node.data.id(),
138-
DefPathData::EnumVariant(v.node.name.name.as_str()),
141+
DefPathData::EnumVariant(v.node.name.modern()),
139142
REGULAR_SPACE);
140143
this.with_parent(variant_def_index, |this| {
141144
for (index, field) in v.node.data.fields().iter().enumerate() {
142-
let name = field.ident.map(|ident| ident.name)
143-
.unwrap_or_else(|| Symbol::intern(&index.to_string()));
144-
this.create_def(field.id,
145-
DefPathData::Field(name.as_str()),
146-
REGULAR_SPACE);
145+
let ident = field.ident.map(Ident::modern)
146+
.unwrap_or_else(|| Ident::from_str(&index.to_string()));
147+
this.create_def(field.id, DefPathData::Field(ident), REGULAR_SPACE);
147148
}
148149

149150
if let Some(ref expr) = v.node.disr_expr {
@@ -161,9 +162,9 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
161162
}
162163

163164
for (index, field) in struct_def.fields().iter().enumerate() {
164-
let name = field.ident.map(|ident| ident.name.as_str())
165-
.unwrap_or(Symbol::intern(&index.to_string()).as_str());
166-
this.create_def(field.id, DefPathData::Field(name), REGULAR_SPACE);
165+
let ident = field.ident.map(Ident::modern)
166+
.unwrap_or_else(|| Ident::from_str(&index.to_string()));
167+
this.create_def(field.id, DefPathData::Field(ident), REGULAR_SPACE);
167168
}
168169
}
169170
_ => {}
@@ -174,7 +175,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
174175

175176
fn visit_foreign_item(&mut self, foreign_item: &'a ForeignItem) {
176177
let def = self.create_def(foreign_item.id,
177-
DefPathData::ValueNs(foreign_item.ident.name.as_str()),
178+
DefPathData::ValueNs(foreign_item.ident.modern()),
178179
REGULAR_SPACE);
179180

180181
self.with_parent(def, |this| {
@@ -185,7 +186,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
185186
fn visit_generics(&mut self, generics: &'a Generics) {
186187
for ty_param in generics.ty_params.iter() {
187188
self.create_def(ty_param.id,
188-
DefPathData::TypeParam(ty_param.ident.name.as_str()),
189+
DefPathData::TypeParam(ty_param.ident.modern()),
189190
REGULAR_SPACE);
190191
}
191192

@@ -195,8 +196,8 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
195196
fn visit_trait_item(&mut self, ti: &'a TraitItem) {
196197
let def_data = match ti.node {
197198
TraitItemKind::Method(..) | TraitItemKind::Const(..) =>
198-
DefPathData::ValueNs(ti.ident.name.as_str()),
199-
TraitItemKind::Type(..) => DefPathData::TypeNs(ti.ident.name.as_str()),
199+
DefPathData::ValueNs(ti.ident.modern()),
200+
TraitItemKind::Type(..) => DefPathData::TypeNs(ti.ident.modern()),
200201
TraitItemKind::Macro(..) => return self.visit_macro_invoc(ti.id, false),
201202
};
202203

@@ -213,8 +214,8 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
213214
fn visit_impl_item(&mut self, ii: &'a ImplItem) {
214215
let def_data = match ii.node {
215216
ImplItemKind::Method(..) | ImplItemKind::Const(..) =>
216-
DefPathData::ValueNs(ii.ident.name.as_str()),
217-
ImplItemKind::Type(..) => DefPathData::TypeNs(ii.ident.name.as_str()),
217+
DefPathData::ValueNs(ii.ident.modern()),
218+
ImplItemKind::Type(..) => DefPathData::TypeNs(ii.ident.modern()),
218219
ImplItemKind::Macro(..) => return self.visit_macro_invoc(ii.id, false),
219220
};
220221

@@ -235,7 +236,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
235236
PatKind::Mac(..) => return self.visit_macro_invoc(pat.id, false),
236237
PatKind::Ident(_, id, _) => {
237238
let def = self.create_def(pat.id,
238-
DefPathData::Binding(id.node.name.as_str()),
239+
DefPathData::Binding(id.node.modern()),
239240
REGULAR_SPACE);
240241
self.parent_def = Some(def);
241242
}
@@ -280,7 +281,7 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
280281

281282
fn visit_lifetime_def(&mut self, def: &'a LifetimeDef) {
282283
self.create_def(def.lifetime.id,
283-
DefPathData::LifetimeDef(def.lifetime.name.as_str()),
284+
DefPathData::LifetimeDef(def.lifetime.ident.modern()),
284285
REGULAR_SPACE);
285286
}
286287

0 commit comments

Comments
 (0)