about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>2018-06-24 19:54:23 +0300
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>2018-06-30 01:53:32 +0300
commit99ecdb3f5fc49efb3eccdd10fbe12dc98623a938 (patch)
tree7c82e4221bf6f94e44ca58399c873b2b9eb29a25
parent09856c85b73feff1db93990cd3d80f2c585b40c4 (diff)
downloadrust-99ecdb3f5fc49efb3eccdd10fbe12dc98623a938.tar.gz
rust-99ecdb3f5fc49efb3eccdd10fbe12dc98623a938.zip
hygiene: Implement transparent marks
-rw-r--r--src/librustc_resolve/lib.rs13
-rw-r--r--src/librustc_resolve/macros.rs4
-rw-r--r--src/libsyntax/ext/base.rs8
-rw-r--r--src/libsyntax/ext/expand.rs2
-rw-r--r--src/libsyntax/ext/tt/macro_rules.rs3
-rw-r--r--src/libsyntax_pos/hygiene.rs97
-rw-r--r--src/libsyntax_pos/lib.rs6
-rw-r--r--src/libsyntax_pos/symbol.rs9
-rw-r--r--src/test/ui/hygiene/auxiliary/intercrate.rs6
-rw-r--r--src/test/ui/hygiene/auxiliary/transparent-basic.rs16
-rw-r--r--src/test/ui/hygiene/dollar-crate-modern.rs22
-rw-r--r--src/test/ui/hygiene/generate-mod.rs24
-rw-r--r--src/test/ui/hygiene/generate-mod.stderr17
-rw-r--r--src/test/ui/hygiene/transparent-basic.rs53
14 files changed, 253 insertions, 27 deletions
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 7771bc9b1cb..640e650207e 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -1850,6 +1850,8 @@ impl<'a> Resolver<'a> {
             } else {
                 ident.span.modern()
             }
+        } else {
+            ident = ident.modern_and_legacy();
         }
 
         // Walk backwards up the ribs in scope.
@@ -1987,7 +1989,7 @@ impl<'a> Resolver<'a> {
             // When resolving `$crate` from a `macro_rules!` invoked in a `macro`,
             // we don't want to pretend that the `macro_rules!` definition is in the `macro`
             // as described in `SyntaxContext::apply_mark`, so we ignore prepended modern marks.
-            ctxt.marks().into_iter().find(|&mark| mark.transparency() != Transparency::Opaque)
+            ctxt.marks().into_iter().rev().find(|m| m.transparency() != Transparency::Transparent)
         } else {
             ctxt = ctxt.modern();
             ctxt.adjust(Mark::root())
@@ -2628,6 +2630,7 @@ impl<'a> Resolver<'a> {
         // must not add it if it's in the bindings map
         // because that breaks the assumptions later
         // passes make about or-patterns.)
+        let ident = ident.modern_and_legacy();
         let mut def = Def::Local(pat_id);
         match bindings.get(&ident).cloned() {
             Some(id) if id == outer_pat_id => {
@@ -3782,7 +3785,8 @@ impl<'a> Resolver<'a> {
             self.unused_labels.insert(id, label.ident.span);
             let def = Def::Label(id);
             self.with_label_rib(|this| {
-                this.label_ribs.last_mut().unwrap().bindings.insert(label.ident, def);
+                let ident = label.ident.modern_and_legacy();
+                this.label_ribs.last_mut().unwrap().bindings.insert(ident, def);
                 f(this);
             });
         } else {
@@ -3813,7 +3817,10 @@ impl<'a> Resolver<'a> {
             }
 
             ExprKind::Break(Some(label), _) | ExprKind::Continue(Some(label)) => {
-                match self.search_label(label.ident, |rib, id| rib.bindings.get(&id).cloned()) {
+                let def = self.search_label(label.ident, |rib, ident| {
+                    rib.bindings.get(&ident.modern_and_legacy()).cloned()
+                });
+                match def {
                     None => {
                         // Search again for close matches...
                         // Picks the first label that is "close enough", which is not necessarily
diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs
index c4a20bea685..0523765ea18 100644
--- a/src/librustc_resolve/macros.rs
+++ b/src/librustc_resolve/macros.rs
@@ -332,7 +332,9 @@ impl<'a> base::Resolver for Resolver<'a> {
         self.unused_macros.remove(&def_id);
         let ext = self.get_macro(def);
         if ext.is_modern() {
-            invoc.expansion_data.mark.set_transparency(Transparency::Opaque);
+            let transparency =
+                if ext.is_transparent() { Transparency::Transparent } else { Transparency::Opaque };
+            invoc.expansion_data.mark.set_transparency(transparency);
         } else if def_id.krate == BUILTIN_MACROS_CRATE {
             invoc.expansion_data.mark.set_is_builtin(true);
         }
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index 78fa3f326d6..e2424de4d14 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -649,6 +649,7 @@ pub enum SyntaxExtension {
     DeclMacro {
         expander: Box<TTMacroExpander + sync::Sync + sync::Send>,
         def_info: Option<(ast::NodeId, Span)>,
+        is_transparent: bool,
         edition: Edition,
     }
 }
@@ -682,6 +683,13 @@ impl SyntaxExtension {
         }
     }
 
+    pub fn is_transparent(&self) -> bool {
+        match *self {
+            SyntaxExtension::DeclMacro { is_transparent, .. } => is_transparent,
+            _ => false,
+        }
+    }
+
     pub fn edition(&self) -> Edition {
         match *self {
             SyntaxExtension::NormalTT { edition, .. } |
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 38fa92f2c93..e364e145593 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -738,7 +738,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         };
 
         let opt_expanded = match *ext {
-            DeclMacro { ref expander, def_info, edition } => {
+            DeclMacro { ref expander, def_info, edition, .. } => {
                 if let Err(dummy_span) = validate_and_set_expn_info(self, def_info.map(|(_, s)| s),
                                                                     false, false, false, None,
                                                                     edition) {
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index 0c81a68e999..70fc9dada42 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -312,9 +312,12 @@ pub fn compile(sess: &ParseSess, features: &Features, def: &ast::Item, edition:
             edition,
         }
     } else {
+        let is_transparent = attr::contains_name(&def.attrs, "rustc_transparent_macro");
+
         SyntaxExtension::DeclMacro {
             expander,
             def_info: Some((def.id, def.span)),
+            is_transparent,
             edition,
         }
     }
diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs
index 99d8b1b172d..e7f1f31084a 100644
--- a/src/libsyntax_pos/hygiene.rs
+++ b/src/libsyntax_pos/hygiene.rs
@@ -33,14 +33,17 @@ pub struct SyntaxContext(pub(super) u32);
 pub struct SyntaxContextData {
     pub outer_mark: Mark,
     pub prev_ctxt: SyntaxContext,
-    pub modern: SyntaxContext,
+    // This context, but with all transparent and semi-transparent marks filtered away.
+    pub opaque: SyntaxContext,
+    // This context, but with all transparent marks filtered away.
+    pub opaque_and_semitransparent: SyntaxContext,
 }
 
 /// A mark is a unique id associated with a macro expansion.
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
 pub struct Mark(u32);
 
-#[derive(Debug)]
+#[derive(Clone, Debug)]
 struct MarkData {
     parent: Mark,
     transparency: Transparency,
@@ -50,7 +53,7 @@ struct MarkData {
 
 /// A property of a macro expansion that determines how identifiers
 /// produced by that expansion are resolved.
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)]
 pub enum Transparency {
     /// Identifier produced by a transparent expansion is always resolved at call-site.
     /// Call-site spans in procedural macros, hygiene opt-out in `macro` should use this.
@@ -69,16 +72,26 @@ pub enum Transparency {
 }
 
 impl Mark {
+    fn fresh_with_data(mark_data: MarkData, data: &mut HygieneData) -> Self {
+        data.marks.push(mark_data);
+        Mark(data.marks.len() as u32 - 1)
+    }
+
     pub fn fresh(parent: Mark) -> Self {
         HygieneData::with(|data| {
-            data.marks.push(MarkData {
+            Mark::fresh_with_data(MarkData {
                 parent,
                 // By default expansions behave like `macro_rules`.
                 transparency: Transparency::SemiTransparent,
                 is_builtin: false,
                 expn_info: None,
-            });
-            Mark(data.marks.len() as u32 - 1)
+            }, data)
+        })
+    }
+
+    pub fn fresh_cloned(clone_from: Mark) -> Self {
+        HygieneData::with(|data| {
+            Mark::fresh_with_data(data.marks[clone_from.0 as usize].clone(), data)
         })
     }
 
@@ -207,7 +220,8 @@ impl HygieneData {
             syntax_contexts: vec![SyntaxContextData {
                 outer_mark: Mark::root(),
                 prev_ctxt: SyntaxContext(0),
-                modern: SyntaxContext(0),
+                opaque: SyntaxContext(0),
+                opaque_and_semitransparent: SyntaxContext(0),
             }],
             markings: HashMap::new(),
             default_edition: Edition::Edition2015,
@@ -239,7 +253,7 @@ impl SyntaxContext {
     // Allocate a new SyntaxContext with the given ExpnInfo. This is used when
     // deserializing Spans from the incr. comp. cache.
     // FIXME(mw): This method does not restore MarkData::parent or
-    // SyntaxContextData::prev_ctxt or SyntaxContextData::modern. These things
+    // SyntaxContextData::prev_ctxt or SyntaxContextData::opaque. These things
     // don't seem to be used after HIR lowering, so everything should be fine
     // as long as incremental compilation does not kick in before that.
     pub fn allocate_directly(expansion_info: ExpnInfo) -> Self {
@@ -256,7 +270,8 @@ impl SyntaxContext {
             data.syntax_contexts.push(SyntaxContextData {
                 outer_mark: mark,
                 prev_ctxt: SyntaxContext::empty(),
-                modern: SyntaxContext::empty(),
+                opaque: SyntaxContext::empty(),
+                opaque_and_semitransparent: SyntaxContext::empty(),
             });
             SyntaxContext(data.syntax_contexts.len() as u32 - 1)
         })
@@ -269,7 +284,13 @@ impl SyntaxContext {
         }
 
         let call_site_ctxt =
-            mark.expn_info().map_or(SyntaxContext::empty(), |info| info.call_site.ctxt()).modern();
+            mark.expn_info().map_or(SyntaxContext::empty(), |info| info.call_site.ctxt());
+        let call_site_ctxt = if mark.transparency() == Transparency::SemiTransparent {
+            call_site_ctxt.modern()
+        } else {
+            call_site_ctxt.modern_and_legacy()
+        };
+
         if call_site_ctxt == SyntaxContext::empty() {
             return self.apply_mark_internal(mark);
         }
@@ -293,26 +314,53 @@ impl SyntaxContext {
     fn apply_mark_internal(self, mark: Mark) -> SyntaxContext {
         HygieneData::with(|data| {
             let syntax_contexts = &mut data.syntax_contexts;
-            let mut modern = syntax_contexts[self.0 as usize].modern;
-            if data.marks[mark.0 as usize].transparency == Transparency::Opaque {
-                modern = *data.markings.entry((modern, mark)).or_insert_with(|| {
-                    let len = syntax_contexts.len() as u32;
+            let transparency = data.marks[mark.0 as usize].transparency;
+
+            let mut opaque = syntax_contexts[self.0 as usize].opaque;
+            let mut opaque_and_semitransparent =
+                syntax_contexts[self.0 as usize].opaque_and_semitransparent;
+
+            if transparency >= Transparency::Opaque {
+                let prev_ctxt = opaque;
+                opaque = *data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
+                    let new_opaque = SyntaxContext(syntax_contexts.len() as u32);
+                    syntax_contexts.push(SyntaxContextData {
+                        outer_mark: mark,
+                        prev_ctxt,
+                        opaque: new_opaque,
+                        opaque_and_semitransparent: new_opaque,
+                    });
+                    new_opaque
+                });
+            }
+
+            if transparency >= Transparency::SemiTransparent {
+                let prev_ctxt = opaque_and_semitransparent;
+                opaque_and_semitransparent =
+                        *data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
+                    let new_opaque_and_semitransparent =
+                        SyntaxContext(syntax_contexts.len() as u32);
                     syntax_contexts.push(SyntaxContextData {
                         outer_mark: mark,
-                        prev_ctxt: modern,
-                        modern: SyntaxContext(len),
+                        prev_ctxt,
+                        opaque,
+                        opaque_and_semitransparent: new_opaque_and_semitransparent,
                     });
-                    SyntaxContext(len)
+                    new_opaque_and_semitransparent
                 });
             }
 
-            *data.markings.entry((self, mark)).or_insert_with(|| {
+            let prev_ctxt = self;
+            *data.markings.entry((prev_ctxt, mark)).or_insert_with(|| {
+                let new_opaque_and_semitransparent_and_transparent =
+                    SyntaxContext(syntax_contexts.len() as u32);
                 syntax_contexts.push(SyntaxContextData {
                     outer_mark: mark,
-                    prev_ctxt: self,
-                    modern,
+                    prev_ctxt,
+                    opaque,
+                    opaque_and_semitransparent,
                 });
-                SyntaxContext(syntax_contexts.len() as u32 - 1)
+                new_opaque_and_semitransparent_and_transparent
             })
         })
     }
@@ -452,7 +500,12 @@ impl SyntaxContext {
 
     #[inline]
     pub fn modern(self) -> SyntaxContext {
-        HygieneData::with(|data| data.syntax_contexts[self.0 as usize].modern)
+        HygieneData::with(|data| data.syntax_contexts[self.0 as usize].opaque)
+    }
+
+    #[inline]
+    pub fn modern_and_legacy(self) -> SyntaxContext {
+        HygieneData::with(|data| data.syntax_contexts[self.0 as usize].opaque_and_semitransparent)
     }
 
     #[inline]
diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs
index 55dec31511c..308fb118f07 100644
--- a/src/libsyntax_pos/lib.rs
+++ b/src/libsyntax_pos/lib.rs
@@ -491,6 +491,12 @@ impl Span {
         let span = self.data();
         span.with_ctxt(span.ctxt.modern())
     }
+
+    #[inline]
+    pub fn modern_and_legacy(self) -> Span {
+        let span = self.data();
+        span.with_ctxt(span.ctxt.modern_and_legacy())
+    }
 }
 
 #[derive(Clone, Debug)]
diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs
index bb64dad1208..fe0b479d161 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -68,6 +68,15 @@ impl Ident {
         Ident::new(self.name, self.span.modern())
     }
 
+    // "Normalize" ident for use in comparisons using "local variable hygiene".
+    // Identifiers with same string value become same if they came from the same non-transparent
+    // macro (e.g. `macro` or `macro_rules!` items) and stay different if they came from different
+    // non-transparent macros.
+    // Technically, this operation strips all transparent marks from ident's syntactic context.
+    pub fn modern_and_legacy(self) -> Ident {
+        Ident::new(self.name, self.span.modern_and_legacy())
+    }
+
     pub fn gensym(self) -> Ident {
         Ident::new(self.name.gensymed(), self.span)
     }
diff --git a/src/test/ui/hygiene/auxiliary/intercrate.rs b/src/test/ui/hygiene/auxiliary/intercrate.rs
index aa67e5c5f4d..244a9903e31 100644
--- a/src/test/ui/hygiene/auxiliary/intercrate.rs
+++ b/src/test/ui/hygiene/auxiliary/intercrate.rs
@@ -19,3 +19,9 @@ pub mod foo {
         }
     }
 }
+
+pub struct SomeType;
+
+pub macro uses_dollar_crate() {
+    type Alias = $crate::SomeType;
+}
diff --git a/src/test/ui/hygiene/auxiliary/transparent-basic.rs b/src/test/ui/hygiene/auxiliary/transparent-basic.rs
new file mode 100644
index 00000000000..ba65c5f4da8
--- /dev/null
+++ b/src/test/ui/hygiene/auxiliary/transparent-basic.rs
@@ -0,0 +1,16 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(decl_macro, rustc_attrs)]
+
+#[rustc_transparent_macro]
+pub macro dollar_crate() {
+    let s = $crate::S;
+}
diff --git a/src/test/ui/hygiene/dollar-crate-modern.rs b/src/test/ui/hygiene/dollar-crate-modern.rs
new file mode 100644
index 00000000000..f4b24d0c5b4
--- /dev/null
+++ b/src/test/ui/hygiene/dollar-crate-modern.rs
@@ -0,0 +1,22 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Make sure `$crate` works in `macro` macros.
+
+// compile-pass
+// aux-build:intercrate.rs
+
+#![feature(use_extern_macros)]
+
+extern crate intercrate;
+
+intercrate::uses_dollar_crate!();
+
+fn main() {}
diff --git a/src/test/ui/hygiene/generate-mod.rs b/src/test/ui/hygiene/generate-mod.rs
new file mode 100644
index 00000000000..90409857dea
--- /dev/null
+++ b/src/test/ui/hygiene/generate-mod.rs
@@ -0,0 +1,24 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// This is an equivalent of issue #50504, but for declarative macros.
+
+#![feature(decl_macro, rustc_attrs)]
+
+#[rustc_transparent_macro]
+macro genmod() {
+    mod m {
+        type A = S; //~ ERROR cannot find type `S` in this scope
+    }
+}
+
+struct S;
+
+genmod!();
diff --git a/src/test/ui/hygiene/generate-mod.stderr b/src/test/ui/hygiene/generate-mod.stderr
new file mode 100644
index 00000000000..e79f8528c2c
--- /dev/null
+++ b/src/test/ui/hygiene/generate-mod.stderr
@@ -0,0 +1,17 @@
+error[E0412]: cannot find type `S` in this scope
+  --> $DIR/generate-mod.rs:18:18
+   |
+LL |         type A = S; //~ ERROR cannot find type `S` in this scope
+   |                  ^ did you mean `A`?
+...
+LL | genmod!();
+   | ---------- in this macro invocation
+
+error[E0601]: `main` function not found in crate `generate_mod`
+   |
+   = note: consider adding a `main` function to `$DIR/generate-mod.rs`
+
+error: aborting due to 2 previous errors
+
+Some errors occurred: E0412, E0601.
+For more information about an error, try `rustc --explain E0412`.
diff --git a/src/test/ui/hygiene/transparent-basic.rs b/src/test/ui/hygiene/transparent-basic.rs
new file mode 100644
index 00000000000..81ece1f11bc
--- /dev/null
+++ b/src/test/ui/hygiene/transparent-basic.rs
@@ -0,0 +1,53 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-pass
+// aux-build:transparent-basic.rs
+
+#![feature(decl_macro, rustc_attrs)]
+
+extern crate transparent_basic;
+
+#[rustc_transparent_macro]
+macro binding() {
+    let x = 10;
+}
+
+#[rustc_transparent_macro]
+macro label() {
+    break 'label
+}
+
+macro_rules! legacy {
+    () => {
+        binding!();
+        let y = x;
+    }
+}
+
+fn legacy_interaction1() {
+    legacy!();
+}
+
+struct S;
+
+fn check_dollar_crate() {
+    // `$crate::S` inside the macro resolves to `S` from this crate.
+    transparent_basic::dollar_crate!();
+}
+
+fn main() {
+    binding!();
+    let y = x;
+
+    'label: loop {
+        label!();
+    }
+}