Skip to content

Commit 7206642

Browse files
committed
Make the algorithm descend into delimited TTs.
1 parent a27606c commit 7206642

File tree

5 files changed

+63
-28
lines changed

5 files changed

+63
-28
lines changed

src/libcollections/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#![feature(pattern)]
4444
#![feature(placement_in)]
4545
#![feature(placement_new_protocol)]
46+
#![feature(rustc_attrs)]
4647
#![feature(shared)]
4748
#![feature(slice_patterns)]
4849
#![feature(specialization)]

src/libcollections/macros.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#[macro_export]
4343
#[stable(feature = "rust1", since = "1.0.0")]
4444
#[allow_internal_unstable]
45+
#[rustc_unsafe_macro]
4546
macro_rules! vec {
4647
($elem:expr; $n:expr) => (
4748
$crate::vec::from_elem($elem, $n)
@@ -57,6 +58,7 @@ macro_rules! vec {
5758
// `slice::into_vec` function which is only available with cfg(test)
5859
// NB see the slice::hack module in slice.rs for more information
5960
#[cfg(test)]
61+
#[rustc_unsafe_macro]
6062
macro_rules! vec {
6163
($elem:expr; $n:expr) => (
6264
$crate::vec::from_elem($elem, $n)

src/libsyntax/ext/expand.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ fn expand_mac_invoc<T>(mac: ast::Mac, ident: Option<Ident>, attrs: Vec<ast::Attr
279279
}
280280
});
281281

282+
let check = !attr::contains_name(&attrs, "rustc_unsafe_macro");
282283
// DON'T mark before expansion.
283284
fld.cx.insert_macro(ast::MacroDef {
284285
ident: ident,
@@ -290,7 +291,7 @@ fn expand_mac_invoc<T>(mac: ast::Mac, ident: Option<Ident>, attrs: Vec<ast::Attr
290291
export: attr::contains_name(&attrs, "macro_export"),
291292
allow_internal_unstable: attr::contains_name(&attrs, "allow_internal_unstable"),
292293
attrs: attrs,
293-
}, false);
294+
}, check);
294295

295296
// macro_rules! has a side effect but expands to nothing.
296297
fld.cx.bt_pop();
@@ -911,7 +912,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
911912
// We need to error on `#[macro_use] extern crate` when it isn't at the
912913
// crate root, because `$crate` won't work properly.
913914
for def in self.cx.loader.load_crate(item, self.at_crate_root) {
914-
self.cx.insert_macro(def, true);
915+
self.cx.insert_macro(def, false);
915916
}
916917
} else {
917918
let at_crate_root = ::std::mem::replace(&mut self.at_crate_root, false);

src/libsyntax/ext/tt/macro_rules.rs

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ impl<'a> MacResult for ParserAnyMacro<'a> {
116116

117117

118118
fn make_stmts(self: Box<ParserAnyMacro<'a>>)
119-
-> Option<SmallVector<ast::Stmt>> {
119+
-> Option<SmallVector<ast::Stmt>> {
120120
let mut ret = SmallVector::zero();
121121
loop {
122122
let mut parser = self.parser.borrow_mut();
@@ -250,7 +250,7 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt,
250250
/// Converts a `macro_rules!` invocation into a syntax extension.
251251
pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
252252
def: &ast::MacroDef,
253-
imported: bool) -> SyntaxExtension {
253+
check_macro: bool) -> SyntaxExtension {
254254

255255
let lhs_nm = gensym_ident("lhs");
256256
let rhs_nm = gensym_ident("rhs");
@@ -306,7 +306,7 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
306306
MatchedSeq(ref s, _) => {
307307
s.iter().map(|m| match **m {
308308
MatchedNonterminal(NtTT(ref tt)) => {
309-
if !imported {
309+
if check_macro {
310310
valid &= check_lhs_nt_follows(cx, tt);
311311
}
312312
(**tt).clone()
@@ -317,17 +317,20 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
317317
_ => cx.span_bug(def.span, "wrong-structured lhs")
318318
};
319319

320-
if !imported {
320+
if check_macro {
321321
'a: for (i, lhs) in lhses.iter().enumerate() {
322322
for lhs_ in lhses[i + 1 ..].iter() {
323-
if !check_lhs_firsts(cx, lhs, lhs_) {
324-
cx.struct_span_warn(def.span, "macro is not future-proof")
325-
.span_help(lhs.get_span(), "parsing of this arm is ambiguous...")
326-
.span_help(lhs_.get_span(), "with the parsing of this arm.")
327-
.help("the behaviour of this macro might change in the future")
328-
.emit();
329-
//valid = false;
330-
break 'a;
323+
match check_lhs_firsts(cx, lhs, lhs_) {
324+
AnalysisResult::Error => {
325+
cx.struct_span_err(def.span, "macro is not future-proof")
326+
.span_help(lhs.get_span(), "parsing of this arm is ambiguous...")
327+
.span_help(lhs_.get_span(), "with the parsing of this arm.")
328+
.help("the behaviour of this macro might change in the future")
329+
.emit();
330+
//valid = false;
331+
break 'a;
332+
}
333+
_ => ()
331334
}
332335
}
333336
}
@@ -358,7 +361,8 @@ pub fn compile<'cx>(cx: &'cx mut ExtCtxt,
358361
NormalTT(exp, Some(def.span), def.allow_internal_unstable)
359362
}
360363

361-
fn check_lhs_firsts(cx: &ExtCtxt, lhs: &TokenTree, lhs_: &TokenTree) -> bool {
364+
fn check_lhs_firsts(cx: &ExtCtxt, lhs: &TokenTree, lhs_: &TokenTree)
365+
-> AnalysisResult {
362366
match (lhs, lhs_) {
363367
(&TokenTree::Delimited(_, ref tta),
364368
&TokenTree::Delimited(_, ref ttb)) =>
@@ -578,7 +582,20 @@ fn first_sets_disjoints(ma: &TokenTree, mb: &TokenTree,
578582
}
579583
}
580584

581-
fn check_matcher_firsts(cx: &ExtCtxt, ma: &[TokenTree], mb: &[TokenTree]) -> bool {
585+
// the result of the FIRST set analysis.
586+
// * Ok -> an obvious disambiguation has been found
587+
// * Unsure -> no problem between those matchers but analysis should continue
588+
// * Error -> maybe a problem. should be accepted only if an obvious
589+
// disambiguation is found later
590+
enum AnalysisResult {
591+
Ok,
592+
Unsure,
593+
Error
594+
}
595+
596+
fn check_matcher_firsts(cx: &ExtCtxt, ma: &[TokenTree], mb: &[TokenTree])
597+
-> AnalysisResult {
598+
use self::AnalysisResult::*;
582599
let mut need_disambiguation = false;
583600

584601
// first compute the FIRST sets. FIRST sets for tokens, delimited TTs and NT
@@ -601,7 +618,7 @@ fn check_matcher_firsts(cx: &ExtCtxt, ma: &[TokenTree], mb: &[TokenTree]) -> boo
601618

602619
if first_sets_disjoints(&ta, &tb, &firsts_a, &firsts_b) {
603620
// accept the macro
604-
return true
621+
return Ok
605622
}
606623

607624
// i.e. A or B is either a repeated sequence or a NT matcher that is
@@ -611,22 +628,26 @@ fn check_matcher_firsts(cx: &ExtCtxt, ma: &[TokenTree], mb: &[TokenTree]) -> boo
611628
(&TokenTree::Token(_, MatchNt(_, nta)),
612629
&TokenTree::Token(_, MatchNt(_, ntb))) =>
613630
if !(nt_is_single_tt(nta) && nt_is_single_tt(ntb)) {
614-
return false
631+
return Error
615632
},
616633

617634
(&TokenTree::Token(_, MatchNt(_, nt)), _) if !nt_is_single_tt(nt) =>
618-
return false,
635+
return Error,
619636

620637
// super specific corner case: if one arm is always one token,
621638
// followed by the end of the macro invocation, then we can accept
622639
// it.
623640

624641
(&TokenTree::Sequence(_, _), _) |
625642
(_, &TokenTree::Sequence(_, _)) =>
626-
return only_simple_tokens(&ma[idx_a..]) && !need_disambiguation,
643+
return if only_simple_tokens(&ma[idx_a..]) && !need_disambiguation {
644+
Unsure
645+
} else { Error },
627646

628647
(_ ,&TokenTree::Token(_, MatchNt(_, nt))) if !nt_is_single_tt(nt) =>
629-
return only_simple_tokens(&ma[idx_a..]) && !need_disambiguation,
648+
return if only_simple_tokens(&ma[idx_a..]) && !need_disambiguation {
649+
Unsure
650+
} else { Error },
630651

631652
_ => ()
632653
}
@@ -690,12 +711,17 @@ fn check_matcher_firsts(cx: &ExtCtxt, ma: &[TokenTree], mb: &[TokenTree]) -> boo
690711
continue
691712
}
692713

693-
(&TokenTree::Delimited(..), &TokenTree::Delimited(..)) => {
714+
(&TokenTree::Delimited(_, ref d1),
715+
&TokenTree::Delimited(_, ref d2)) => {
694716
// they have the same delim. as above.
695-
// FIXME: we could search for disambiguation *inside* the
696-
// delimited TTs
697-
need_disambiguation = true;
698-
continue
717+
match check_matcher_firsts(cx, &d1.tts, &d2.tts) {
718+
Ok => return Ok,
719+
Unsure => continue,
720+
Error => {
721+
need_disambiguation = true;
722+
continue
723+
}
724+
}
699725
}
700726

701727
// cannot happen. either they're the same token or their FIRST sets
@@ -713,12 +739,12 @@ fn check_matcher_firsts(cx: &ExtCtxt, ma: &[TokenTree], mb: &[TokenTree]) -> boo
713739
// reject conservatively.
714740
// FIXME: if we are not at the end of the other arm, and that the other
715741
// arm cannot derive empty, I think we could accept...?
716-
false
742+
Error
717743
} else {
718744
// either A is strictly included in B and the other inputs that match B
719745
// will never match A, or B is included in or equal to A, which means
720746
// it's unreachable. this is not our problem. accept.
721-
true
747+
Unsure
722748
}
723749
}
724750

src/libsyntax/feature_gate.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,11 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
510510
across crates and will never be stable",
511511
cfg_fn!(rustc_attrs))),
512512

513+
("unsafe_macro", Normal, Gated("rustc_attrs",
514+
"unsafe_macro allows to write \
515+
non-future proof macros",
516+
cfg_fn!(rustc_attrs))),
517+
513518
("allow_internal_unstable", Normal, Gated("allow_internal_unstable",
514519
EXPLAIN_ALLOW_INTERNAL_UNSTABLE,
515520
cfg_fn!(allow_internal_unstable))),

0 commit comments

Comments
 (0)