From f3fa836939161f59224c30c96210df1eb01f002e Mon Sep 17 00:00:00 2001 From: John Clements Date: Fri, 4 Jul 2014 10:57:17 -0700 Subject: [PATCH 01/17] added test for method arg hygiene --- src/libsyntax/ext/expand.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 74cede2a125ad..1569b4ac4e95d 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1390,6 +1390,19 @@ mod test { // but *shouldn't* bind because it was inserted by a different macro.... // can't write this test case until we have macro-generating macros. + // method arg hygiene + // method expands to fn get_x(&self_0, x_1:int) {self_0 + self_2 + x_3 + x_1} + #[test] fn method_arg_hygiene(){ + run_renaming_test( + &("macro_rules! inject_x (()=>(x)) + macro_rules! inject_self (()=>(self)) + struct A; + impl A{fn get_x(&self, x: int) {self + inject_self!() + inject_x!() + x;} }", + vec!(vec!(0),vec!(3)), + true), + 0) + } + // item fn hygiene // expands to fn q(x_1:int){fn g(x_2:int){x_2 + x_1};} #[test] fn issue_9383(){ From 9f94f823b0e6a89b4a210cb077f94919bfc7a118 Mon Sep 17 00:00:00 2001 From: John Clements Date: Sun, 6 Jul 2014 14:29:29 -0700 Subject: [PATCH 02/17] change if/else to match --- src/libsyntax/parse/parser.rs | 376 ++++++++++++++++++---------------- 1 file changed, 197 insertions(+), 179 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index a3917e9319798..2f6555dcb69d5 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1877,211 +1877,229 @@ impl<'a> Parser<'a> { let ex: Expr_; - if self.token == token::LPAREN { - self.bump(); - // (e) is parenthesized e - // (e,) is a tuple with only one field, e - let mut trailing_comma = false; - if self.token == token::RPAREN { - hi = self.span.hi; - self.bump(); - let lit = box(GC) spanned(lo, hi, LitNil); - return self.mk_expr(lo, hi, ExprLit(lit)); - } - let mut es = vec!(self.parse_expr()); - self.commit_expr(*es.last().unwrap(), &[], &[token::COMMA, token::RPAREN]); - while self.token == token::COMMA { + match self.token { + token::LPAREN => { self.bump(); - if self.token != token::RPAREN { - es.push(self.parse_expr()); - self.commit_expr(*es.last().unwrap(), &[], &[token::COMMA, token::RPAREN]); + // (e) is parenthesized e + // (e,) is a tuple with only one field, e + let mut trailing_comma = false; + if self.token == token::RPAREN { + hi = self.span.hi; + self.bump(); + let lit = box(GC) spanned(lo, hi, LitNil); + return self.mk_expr(lo, hi, ExprLit(lit)); } - else { - trailing_comma = true; + let mut es = vec!(self.parse_expr()); + self.commit_expr(*es.last().unwrap(), &[], &[token::COMMA, token::RPAREN]); + while self.token == token::COMMA { + self.bump(); + if self.token != token::RPAREN { + es.push(self.parse_expr()); + self.commit_expr(*es.last().unwrap(), &[], &[token::COMMA, token::RPAREN]); + } + else { + trailing_comma = true; + } } - } - hi = self.span.hi; - self.commit_expr_expecting(*es.last().unwrap(), token::RPAREN); - - return if es.len() == 1 && !trailing_comma { - self.mk_expr(lo, hi, ExprParen(*es.get(0))) - } - else { - self.mk_expr(lo, hi, ExprTup(es)) - } - } else if self.token == token::LBRACE { - self.bump(); - let blk = self.parse_block_tail(lo, DefaultBlock); - return self.mk_expr(blk.span.lo, blk.span.hi, - ExprBlock(blk)); - } else if token::is_bar(&self.token) { - return self.parse_lambda_expr(); - } else if self.eat_keyword(keywords::Proc) { - let decl = self.parse_proc_decl(); - let body = self.parse_expr(); - let fakeblock = P(ast::Block { - view_items: Vec::new(), - stmts: Vec::new(), - expr: Some(body), - id: ast::DUMMY_NODE_ID, - rules: DefaultBlock, - span: body.span, - }); + hi = self.span.hi; + self.commit_expr_expecting(*es.last().unwrap(), token::RPAREN); - return self.mk_expr(lo, body.span.hi, ExprProc(decl, fakeblock)); - } else if self.eat_keyword(keywords::Self) { - let path = ast_util::ident_to_path(mk_sp(lo, hi), special_idents::self_); - ex = ExprPath(path); - hi = self.last_span.hi; - } else if self.eat_keyword(keywords::If) { - return self.parse_if_expr(); - } else if self.eat_keyword(keywords::For) { - return self.parse_for_expr(None); - } else if self.eat_keyword(keywords::While) { - return self.parse_while_expr(); - } else if Parser::token_is_lifetime(&self.token) { - let lifetime = self.get_lifetime(); - self.bump(); - self.expect(&token::COLON); - if self.eat_keyword(keywords::For) { - return self.parse_for_expr(Some(lifetime)) - } else if self.eat_keyword(keywords::Loop) { - return self.parse_loop_expr(Some(lifetime)) - } else { - self.fatal("expected `for` or `loop` after a label") + return if es.len() == 1 && !trailing_comma { + self.mk_expr(lo, hi, ExprParen(*es.get(0))) + } + else { + self.mk_expr(lo, hi, ExprTup(es)) + } + }, + token::LBRACE => { + self.bump(); + let blk = self.parse_block_tail(lo, DefaultBlock); + return self.mk_expr(blk.span.lo, blk.span.hi, + ExprBlock(blk)); + }, + _ if token::is_bar(&self.token) => { + return self.parse_lambda_expr(); + }, + _ if self.eat_keyword(keywords::Proc) => { + let decl = self.parse_proc_decl(); + let body = self.parse_expr(); + let fakeblock = P(ast::Block { + view_items: Vec::new(), + stmts: Vec::new(), + expr: Some(body), + id: ast::DUMMY_NODE_ID, + rules: DefaultBlock, + span: body.span, + }); + return self.mk_expr(lo, body.span.hi, ExprProc(decl, fakeblock)); + }, + _ if self.eat_keyword(keywords::Self) => { + let path = ast_util::ident_to_path(mk_sp(lo, hi), special_idents::self_); + ex = ExprPath(path); + hi = self.last_span.hi; } - } else if self.eat_keyword(keywords::Loop) { - return self.parse_loop_expr(None); - } else if self.eat_keyword(keywords::Continue) { - let lo = self.span.lo; - let ex = if Parser::token_is_lifetime(&self.token) { + _ if self.eat_keyword(keywords::If) => { + return self.parse_if_expr(); + }, + _ if self.eat_keyword(keywords::For) => { + return self.parse_for_expr(None); + }, + _ if self.eat_keyword(keywords::While) => { + return self.parse_while_expr(); + }, + _ if Parser::token_is_lifetime(&self.token) => { let lifetime = self.get_lifetime(); self.bump(); - ExprAgain(Some(lifetime)) - } else { - ExprAgain(None) - }; - let hi = self.span.hi; - return self.mk_expr(lo, hi, ex); - } else if self.eat_keyword(keywords::Match) { - return self.parse_match_expr(); - } else if self.eat_keyword(keywords::Unsafe) { - return self.parse_block_expr(lo, UnsafeBlock(ast::UserProvided)); - } else if self.token == token::LBRACKET { - self.bump(); - - if self.token == token::RBRACKET { - // Empty vector. - self.bump(); - ex = ExprVec(Vec::new()); - } else { - // Nonempty vector. - let first_expr = self.parse_expr(); - if self.token == token::COMMA && - self.look_ahead(1, |t| *t == token::DOTDOT) { - // Repeating vector syntax: [ 0, ..512 ] + self.expect(&token::COLON); + if self.eat_keyword(keywords::For) { + return self.parse_for_expr(Some(lifetime)) + } else if self.eat_keyword(keywords::Loop) { + return self.parse_loop_expr(Some(lifetime)) + } else { + self.fatal("expected `for` or `loop` after a label") + } + }, + _ if self.eat_keyword(keywords::Loop) => { + return self.parse_loop_expr(None); + }, + _ if self.eat_keyword(keywords::Continue) => { + let lo = self.span.lo; + let ex = if Parser::token_is_lifetime(&self.token) { + let lifetime = self.get_lifetime(); self.bump(); + ExprAgain(Some(lifetime)) + } else { + ExprAgain(None) + }; + let hi = self.span.hi; + return self.mk_expr(lo, hi, ex); + }, + _ if self.eat_keyword(keywords::Match) => { + return self.parse_match_expr(); + }, + _ if self.eat_keyword(keywords::Unsafe) => { + return self.parse_block_expr(lo, UnsafeBlock(ast::UserProvided)); + }, + token::LBRACKET => { + self.bump(); + + if self.token == token::RBRACKET { + // Empty vector. self.bump(); - let count = self.parse_expr(); - self.expect(&token::RBRACKET); - ex = ExprRepeat(first_expr, count); - } else if self.token == token::COMMA { - // Vector with two or more elements. + ex = ExprVec(Vec::new()); + } else { + // Nonempty vector. + let first_expr = self.parse_expr(); + if self.token == token::COMMA && + self.look_ahead(1, |t| *t == token::DOTDOT) { + // Repeating vector syntax: [ 0, ..512 ] + self.bump(); + self.bump(); + let count = self.parse_expr(); + self.expect(&token::RBRACKET); + ex = ExprRepeat(first_expr, count); + } else if self.token == token::COMMA { + // Vector with two or more elements. + self.bump(); + let remaining_exprs = self.parse_seq_to_end( + &token::RBRACKET, + seq_sep_trailing_allowed(token::COMMA), + |p| p.parse_expr() + ); + let mut exprs = vec!(first_expr); + exprs.push_all_move(remaining_exprs); + ex = ExprVec(exprs); + } else { + // Vector with one element. + self.expect(&token::RBRACKET); + ex = ExprVec(vec!(first_expr)); + } + } + hi = self.last_span.hi; + }, + _ if self.eat_keyword(keywords::Return) => { + // RETURN expression + if can_begin_expr(&self.token) { + let e = self.parse_expr(); + hi = e.span.hi; + ex = ExprRet(Some(e)); + } else { ex = ExprRet(None); } + }, + _ if self.eat_keyword(keywords::Break) => { + // BREAK expression + if Parser::token_is_lifetime(&self.token) { + let lifetime = self.get_lifetime(); self.bump(); - let remaining_exprs = self.parse_seq_to_end( - &token::RBRACKET, - seq_sep_trailing_allowed(token::COMMA), - |p| p.parse_expr() - ); - let mut exprs = vec!(first_expr); - exprs.push_all_move(remaining_exprs); - ex = ExprVec(exprs); + ex = ExprBreak(Some(lifetime)); } else { - // Vector with one element. - self.expect(&token::RBRACKET); - ex = ExprVec(vec!(first_expr)); + ex = ExprBreak(None); } - } - hi = self.last_span.hi; - } else if self.eat_keyword(keywords::Return) { - // RETURN expression - if can_begin_expr(&self.token) { - let e = self.parse_expr(); - hi = e.span.hi; - ex = ExprRet(Some(e)); - } else { ex = ExprRet(None); } - } else if self.eat_keyword(keywords::Break) { - // BREAK expression - if Parser::token_is_lifetime(&self.token) { - let lifetime = self.get_lifetime(); - self.bump(); - ex = ExprBreak(Some(lifetime)); - } else { - ex = ExprBreak(None); - } - hi = self.span.hi; - } else if self.token == token::MOD_SEP || + hi = self.span.hi; + }, + _ if self.token == token::MOD_SEP || is_ident(&self.token) && !self.is_keyword(keywords::True) && - !self.is_keyword(keywords::False) { - let pth = self.parse_path(LifetimeAndTypesWithColons).path; + !self.is_keyword(keywords::False) => { + let pth = self.parse_path(LifetimeAndTypesWithColons).path; - // `!`, as an operator, is prefix, so we know this isn't that - if self.token == token::NOT { - // MACRO INVOCATION expression - self.bump(); + // `!`, as an operator, is prefix, so we know this isn't that + if self.token == token::NOT { + // MACRO INVOCATION expression + self.bump(); - let ket = token::close_delimiter_for(&self.token) - .unwrap_or_else(|| self.fatal("expected open delimiter")); - self.bump(); + let ket = token::close_delimiter_for(&self.token) + .unwrap_or_else(|| self.fatal("expected open delimiter")); + self.bump(); - let tts = self.parse_seq_to_end(&ket, - seq_sep_none(), - |p| p.parse_token_tree()); - let hi = self.span.hi; + let tts = self.parse_seq_to_end(&ket, + seq_sep_none(), + |p| p.parse_token_tree()); + let hi = self.span.hi; - return self.mk_mac_expr(lo, hi, MacInvocTT(pth, tts, EMPTY_CTXT)); - } else if self.token == token::LBRACE { - // This is a struct literal, unless we're prohibited from - // parsing struct literals here. - if self.restriction != RESTRICT_NO_STRUCT_LITERAL { - // It's a struct literal. - self.bump(); - let mut fields = Vec::new(); - let mut base = None; + return self.mk_mac_expr(lo, hi, MacInvocTT(pth, tts, EMPTY_CTXT)); + } else if self.token == token::LBRACE { + // This is a struct literal, unless we're prohibited from + // parsing struct literals here. + if self.restriction != RESTRICT_NO_STRUCT_LITERAL { + // It's a struct literal. + self.bump(); + let mut fields = Vec::new(); + let mut base = None; - while self.token != token::RBRACE { - if self.eat(&token::DOTDOT) { - base = Some(self.parse_expr()); - break; + while self.token != token::RBRACE { + if self.eat(&token::DOTDOT) { + base = Some(self.parse_expr()); + break; + } + + fields.push(self.parse_field()); + self.commit_expr(fields.last().unwrap().expr, + &[token::COMMA], &[token::RBRACE]); } - fields.push(self.parse_field()); - self.commit_expr(fields.last().unwrap().expr, - &[token::COMMA], &[token::RBRACE]); - } + if fields.len() == 0 && base.is_none() { + let last_span = self.last_span; + self.span_err(last_span, + "structure literal must either have at \ + least one field or use functional \ + structure update syntax"); + } - if fields.len() == 0 && base.is_none() { - let last_span = self.last_span; - self.span_err(last_span, - "structure literal must either have at \ - least one field or use functional \ - structure update syntax"); + hi = self.span.hi; + self.expect(&token::RBRACE); + ex = ExprStruct(pth, fields, base); + return self.mk_expr(lo, hi, ex); } - - hi = self.span.hi; - self.expect(&token::RBRACE); - ex = ExprStruct(pth, fields, base); - return self.mk_expr(lo, hi, ex); } - } hi = pth.span.hi; ex = ExprPath(pth); - } else { - // other literal expression - let lit = self.parse_lit(); - hi = lit.span.hi; - ex = ExprLit(box(GC) lit); + }, + _ => { + // other literal expression + let lit = self.parse_lit(); + hi = lit.span.hi; + ex = ExprLit(box(GC) lit); + } } return self.mk_expr(lo, hi, ex); From 29b3f2982007daf1c744254314f38f43145e2cd6 Mon Sep 17 00:00:00 2001 From: John Clements Date: Sun, 6 Jul 2014 15:06:32 -0700 Subject: [PATCH 03/17] test harness cleanup --- src/libsyntax/ext/expand.rs | 47 ++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 1569b4ac4e95d..764e60364e122 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1454,27 +1454,36 @@ mod test { assert!((shouldmatch.len() == 0) || (varrefs.len() > *shouldmatch.iter().max().unwrap())); for (idx,varref) in varrefs.iter().enumerate() { + let print_hygiene_debug_info = || { + // good lord, you can't make a path with 0 segments, can you? + let final_varref_ident = match varref.segments.last() { + Some(pathsegment) => pathsegment.identifier, + None => fail!("varref with 0 path segments?") + }; + let varref_name = mtwt::resolve(final_varref_ident); + let varref_idents : Vec + = varref.segments.iter().map(|s| s.identifier) + .collect(); + println!("varref #{}: {}, resolves to {}",idx, varref_idents, varref_name); + let string = token::get_ident(final_varref_ident); + println!("varref's first segment's string: \"{}\"", string.get()); + println!("binding #{}: {}, resolves to {}", + binding_idx, *bindings.get(binding_idx), binding_name); + mtwt::with_sctable(|x| mtwt::display_sctable(x)); + }; if shouldmatch.contains(&idx) { // it should be a path of length 1, and it should // be free-identifier=? or bound-identifier=? to the given binding assert_eq!(varref.segments.len(),1); - let varref_name = mtwt::resolve(varref.segments - .get(0) - .identifier); + let varref_name = mtwt::resolve(varref.segments.get(0).identifier); let varref_marks = mtwt::marksof(varref.segments .get(0) .identifier .ctxt, invalid_name); if !(varref_name==binding_name) { - let varref_idents : Vec - = varref.segments.iter().map(|s| - s.identifier) - .collect(); println!("uh oh, should match but doesn't:"); - println!("varref #{}: {}",idx, varref_idents); - println!("binding #{}: {}", binding_idx, *bindings.get(binding_idx)); - mtwt::with_sctable(|x| mtwt::display_sctable(x)); + print_hygiene_debug_info(); } assert_eq!(varref_name,binding_name); if bound_ident_check { @@ -1488,27 +1497,11 @@ mod test { && (varref_name == binding_name); // temp debugging: if fail { - let varref_idents : Vec - = varref.segments.iter().map(|s| - s.identifier) - .collect(); println!("failure on test {}",test_idx); println!("text of test case: \"{}\"", teststr); println!(""); println!("uh oh, matches but shouldn't:"); - println!("varref #{}: {}, resolves to {}",idx, varref_idents, - varref_name); - // good lord, you can't make a path with 0 segments, can you? - let string = token::get_ident(varref.segments - .get(0) - .identifier); - println!("varref's first segment's uint: {}, and string: \"{}\"", - varref.segments.get(0).identifier.name, - string.get()); - println!("binding #{}: {}, resolves to {}", - binding_idx, *bindings.get(binding_idx), - binding_name); - mtwt::with_sctable(|x| mtwt::display_sctable(x)); + print_hygiene_debug_info(); } assert!(!fail); } From 728b269199484f50779c44bad495cc5ddecd4a15 Mon Sep 17 00:00:00 2001 From: John Clements Date: Sun, 6 Jul 2014 15:07:31 -0700 Subject: [PATCH 04/17] remove unused fn, make SELF_KEYWORD_NAME public --- src/libsyntax/parse/token.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 84834f54a04eb..84562413ac1ae 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -365,10 +365,6 @@ pub fn is_plain_ident(t: &Token) -> bool { match *t { IDENT(_, false) => true, _ => false } } -pub fn is_bar(t: &Token) -> bool { - match *t { BINOP(OR) | OROR => true, _ => false } -} - // Get the first "argument" macro_rules! first { ( $first:expr, $( $remainder:expr, )* ) => ( $first ) @@ -447,7 +443,7 @@ macro_rules! declare_special_idents_and_keywords {( }} // If the special idents get renumbered, remember to modify these two as appropriate -static SELF_KEYWORD_NAME: Name = 1; +pub static SELF_KEYWORD_NAME: Name = 1; static STATIC_KEYWORD_NAME: Name = 2; // NB: leaving holes in the ident table is bad! a different ident will get From 06b64345d681681adc97e64c0dd74a1c9b0762ea Mon Sep 17 00:00:00 2001 From: John Clements Date: Sun, 6 Jul 2014 15:11:44 -0700 Subject: [PATCH 05/17] preserve context in parsing of `self` varref --- src/libsyntax/parse/parser.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 2f6555dcb69d5..d0e44b20139ea 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -541,12 +541,13 @@ impl<'a> Parser<'a> { // if the next token is the given keyword, eat it and return // true. Otherwise, return false. pub fn eat_keyword(&mut self, kw: keywords::Keyword) -> bool { - let is_kw = match self.token { - token::IDENT(sid, false) => kw.to_ident().name == sid.name, + match self.token { + token::IDENT(sid, false) if kw.to_ident().name == sid.name => { + self.bump(); + true + } _ => false - }; - if is_kw { self.bump() } - is_kw + } } // if the given word is not a keyword, signal an error. @@ -1917,7 +1918,7 @@ impl<'a> Parser<'a> { return self.mk_expr(blk.span.lo, blk.span.hi, ExprBlock(blk)); }, - _ if token::is_bar(&self.token) => { + token::BINOP(token::OR) | token::OROR => { return self.parse_lambda_expr(); }, _ if self.eat_keyword(keywords::Proc) => { @@ -1933,8 +1934,9 @@ impl<'a> Parser<'a> { }); return self.mk_expr(lo, body.span.hi, ExprProc(decl, fakeblock)); }, - _ if self.eat_keyword(keywords::Self) => { - let path = ast_util::ident_to_path(mk_sp(lo, hi), special_idents::self_); + token::IDENT(id @ ast::Ident{name:token::SELF_KEYWORD_NAME,ctxt:_},false) => { + self.bump(); + let path = ast_util::ident_to_path(mk_sp(lo, hi), id); ex = ExprPath(path); hi = self.last_span.hi; } @@ -1982,7 +1984,7 @@ impl<'a> Parser<'a> { }, token::LBRACKET => { self.bump(); - + if self.token == token::RBRACKET { // Empty vector. self.bump(); From c1b8b3c35c1e59c475412864c230491843b52ab1 Mon Sep 17 00:00:00 2001 From: John Clements Date: Sun, 6 Jul 2014 15:19:29 -0700 Subject: [PATCH 06/17] get rid of keyword idents, replace with names should prevent future bugs --- src/libsyntax/parse/parser.rs | 4 ++-- src/libsyntax/parse/token.rs | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index d0e44b20139ea..584670cc65480 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -542,7 +542,7 @@ impl<'a> Parser<'a> { // true. Otherwise, return false. pub fn eat_keyword(&mut self, kw: keywords::Keyword) -> bool { match self.token { - token::IDENT(sid, false) if kw.to_ident().name == sid.name => { + token::IDENT(sid, false) if kw.to_name() == sid.name => { self.bump(); true } @@ -555,7 +555,7 @@ impl<'a> Parser<'a> { // otherwise, eat it. pub fn expect_keyword(&mut self, kw: keywords::Keyword) { if !self.eat_keyword(kw) { - let id_interned_str = token::get_ident(kw.to_ident()); + let id_interned_str = token::get_name(kw.to_name()); let token_str = self.this_token_to_string(); self.fatal(format!("expected `{}`, found `{}`", id_interned_str, token_str).as_slice()) diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 84562413ac1ae..dc0be39c42c99 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -413,7 +413,7 @@ macro_rules! declare_special_idents_and_keywords {( * the language and may not appear as identifiers. */ pub mod keywords { - use ast::Ident; + use ast::Name; pub enum Keyword { $( $sk_variant, )* @@ -421,10 +421,10 @@ macro_rules! declare_special_idents_and_keywords {( } impl Keyword { - pub fn to_ident(&self) -> Ident { + pub fn to_name(&self) -> Name { match *self { - $( $sk_variant => Ident { name: $sk_name, ctxt: 0 }, )* - $( $rk_variant => Ident { name: $rk_name, ctxt: 0 }, )* + $( $sk_variant => $sk_name, )* + $( $rk_variant => $rk_name, )* } } } @@ -432,7 +432,7 @@ macro_rules! declare_special_idents_and_keywords {( fn mk_fresh_ident_interner() -> IdentInterner { // The indices here must correspond to the numbers in - // special_idents, in Keyword to_ident(), and in static + // special_idents, in Keyword to_name(), and in static // constants below. let mut init_vec = Vec::new(); $(init_vec.push($si_str);)* @@ -710,7 +710,7 @@ pub fn fresh_mark() -> Mrk { pub fn is_keyword(kw: keywords::Keyword, tok: &Token) -> bool { match *tok { - token::IDENT(sid, false) => { kw.to_ident().name == sid.name } + token::IDENT(sid, false) => { kw.to_name() == sid.name } _ => { false } } } From 6007797ed6f712576bccac799d0fc79a2eb61ae7 Mon Sep 17 00:00:00 2001 From: John Clements Date: Sun, 6 Jul 2014 16:02:48 -0700 Subject: [PATCH 07/17] replace idents with names --- src/librustc/middle/resolve.rs | 18 ++++++++++-------- src/libsyntax/parse/token.rs | 5 +++++ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 612b29afd3e9c..129a5b7c6bed3 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -25,6 +25,7 @@ use syntax::ast; use syntax::ast_util::{local_def}; use syntax::ast_util::{walk_pat, trait_method_to_ty_method}; use syntax::ext::mtwt; +use syntax::parse::token::special_names; use syntax::parse::token::special_idents; use syntax::parse::token; use syntax::codemap::{Span, DUMMY_SP, Pos}; @@ -830,9 +831,9 @@ struct Resolver<'a> { current_self_type: Option, // The ident for the keyword "self". - self_ident: Ident, + self_name: Name, // The ident for the non-keyword "Self". - type_self_ident: Ident, + type_self_name: Name, // The idents for the primitive types. primitive_type_table: PrimitiveTypeTable, @@ -926,8 +927,8 @@ impl<'a> Resolver<'a> { current_trait_ref: None, current_self_type: None, - self_ident: special_idents::self_, - type_self_ident: special_idents::type_self, + self_name: special_names::self_, + type_self_name: special_names::type_self, primitive_type_table: PrimitiveTypeTable::new(), @@ -3628,8 +3629,8 @@ impl<'a> Resolver<'a> { // Create a new rib for the self type. let self_type_rib = Rib::new(ItemRibKind); - // plain insert (no renaming) - let name = self.type_self_ident.name; + // plain insert (no renaming, types are not currently hygienic....) + let name = self.type_self_name; self_type_rib.bindings.borrow_mut() .insert(name, DlDef(DefSelfTy(item.id))); self.type_ribs.borrow_mut().push(self_type_rib); @@ -5159,8 +5160,8 @@ impl<'a> Resolver<'a> { false // Stop advancing }); - if method_scope && token::get_name(self.self_ident.name).get() - == wrong_name.as_slice() { + if method_scope && token::get_name(self.self_name).get() + == wrong_name.as_slice() { self.resolve_error( expr.span, "`self` is not available \ @@ -5532,6 +5533,7 @@ impl<'a> Resolver<'a> { collect_mod(idents, &*module.upgrade().unwrap()); } BlockParentLink(ref module, _) => { + // danger, shouldn't be ident? idents.push(special_idents::opaque); collect_mod(idents, &*module.upgrade().unwrap()); } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index dc0be39c42c99..55db3482a61a7 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -405,6 +405,11 @@ macro_rules! declare_special_idents_and_keywords {( $( pub static $si_static: Ident = Ident { name: $si_name, ctxt: 0 }; )* } + pub mod special_names { + use ast::Name; + $( pub static $si_static: Name = $si_name; )* + } + /** * All the valid words that have meaning in the Rust language. * From 92c2ff6d697fe7be2d4e3979b4dec9f86b969b69 Mon Sep 17 00:00:00 2001 From: John Clements Date: Mon, 7 Jul 2014 09:53:41 -0700 Subject: [PATCH 08/17] self arg macro test case --- src/libsyntax/ext/expand.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 764e60364e122..69e629bb3c625 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1403,6 +1403,19 @@ mod test { 0) } + // ooh, got another bite? + // expands to struct A; impl A {fn thingy(&self_1) {self_1;}} + #[test] fn method_arg_hygiene_2(){ + run_renaming_test( + &("struct A; + macro_rules! add_method (($T:ty) => + (impl $T { fn thingy(&self) {self;} })) + add_method!(A)", + vec!(vec!(0)), + true), + 0) + } + // item fn hygiene // expands to fn q(x_1:int){fn g(x_2:int){x_2 + x_1};} #[test] fn issue_9383(){ From 9ee9c49cb4823c5bddbd9ec1ece6dfafa9e833ea Mon Sep 17 00:00:00 2001 From: John Clements Date: Mon, 7 Jul 2014 09:54:08 -0700 Subject: [PATCH 09/17] introducing let-syntax The let-syntax expander is different in that it doesn't apply a mark to its token trees before expansion. This is used for macro_rules, and it's because macro_rules is essentially MTWT's let-syntax. You don't want to mark before expand sees let-syntax, because there's no "after" syntax to mark again. In some sense, the cleaner approach might be to introduce a new AST node that macro_rules expands into; this would make it clearer that the expansion of a macro is distinct from the addition of a new macro binding. This should work for now, though... --- src/librustc/plugin/registry.rs | 4 +++- src/libsyntax/ext/base.rs | 11 +++++++++-- src/libsyntax/ext/expand.rs | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/librustc/plugin/registry.rs b/src/librustc/plugin/registry.rs index 587bedd502e15..2581ba51c2e10 100644 --- a/src/librustc/plugin/registry.rs +++ b/src/librustc/plugin/registry.rs @@ -13,7 +13,7 @@ use lint::LintPassObject; use syntax::ext::base::{SyntaxExtension, NamedSyntaxExtension, NormalTT}; -use syntax::ext::base::{IdentTT, ItemDecorator, ItemModifier, BasicMacroExpander}; +use syntax::ext::base::{IdentTT, LetSyntaxTT, ItemDecorator, ItemModifier, BasicMacroExpander}; use syntax::ext::base::{MacroExpanderFn}; use syntax::codemap::Span; use syntax::parse::token; @@ -57,6 +57,8 @@ impl Registry { IdentTT(ext, _) => IdentTT(ext, Some(self.krate_span)), ItemDecorator(ext) => ItemDecorator(ext), ItemModifier(ext) => ItemModifier(ext), + // there's probably a nicer way to signal this: + LetSyntaxTT(_, _) => fail!("can't register a new LetSyntax!"), })); } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index a540b23551b53..a2a442f8b6aa7 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -264,8 +264,15 @@ pub enum SyntaxExtension { /// A function-like syntax extension that has an extra ident before /// the block. /// - /// `macro_rules!` is an `IdentTT`. IdentTT(Box, Option), + + /// An ident macro that has two properties: + /// - it adds a macro definition to the environment, and + /// - the definition it adds doesn't introduce any new + /// identifiers. + /// + /// `macro_rules!` is a LetSyntaxTT + LetSyntaxTT(Box, Option), } pub type NamedSyntaxExtension = (Name, SyntaxExtension); @@ -300,7 +307,7 @@ pub fn syntax_expander_table() -> SyntaxEnv { let mut syntax_expanders = SyntaxEnv::new(); syntax_expanders.insert(intern("macro_rules"), - IdentTT(box BasicIdentMacroExpander { + LetSyntaxTT(box BasicIdentMacroExpander { expander: ext::tt::macro_rules::add_new_extension, span: None, }, diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 69e629bb3c625..709db52262d4b 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -484,6 +484,24 @@ fn expand_item_mac(it: Gc, fld: &mut MacroExpander) let marked_tts = mark_tts(tts.as_slice(), fm); expander.expand(fld.cx, it.span, it.ident, marked_tts) } + Some(&LetSyntaxTT(ref expander, span)) => { + if it.ident.name == parse::token::special_idents::invalid.name { + fld.cx.span_err(pth.span, + format!("macro {}! expects an ident argument", + extnamestr.get()).as_slice()); + return SmallVector::zero(); + } + fld.cx.bt_push(ExpnInfo { + call_site: it.span, + callee: NameAndSpan { + name: extnamestr.get().to_string(), + format: MacroBang, + span: span + } + }); + // DON'T mark before expansion: + expander.expand(fld.cx, it.span, it.ident, tts) + } _ => { fld.cx.span_err(it.span, format!("{}! is not legal in item position", From 8f34b21375017aece4ae08fcf13f0dfcc8aca96e Mon Sep 17 00:00:00 2001 From: John Clements Date: Mon, 7 Jul 2014 15:12:31 -0700 Subject: [PATCH 10/17] test case for expansion of method macro --- src/libsyntax/ext/expand.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 709db52262d4b..47c86f1c9f5c2 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -512,8 +512,10 @@ fn expand_item_mac(it: Gc, fld: &mut MacroExpander) let items = match expanded.make_def() { Some(MacroDef { name, ext }) => { - // yikes... no idea how to apply the mark to this. I'm afraid - // we're going to have to wait-and-see on this one. + // hidden invariant: this should only be possible as the + // result of expanding a LetSyntaxTT, and thus doesn't + // need to be marked. Not that it could be marked anyway. + // create issue to recommend refactoring here? fld.extsbox.insert(intern(name.as_slice()), ext); if attr::contains_name(it.attrs.as_slice(), "macro_export") { SmallVector::one(it) @@ -1466,6 +1468,15 @@ mod test { 0) } + // macro_rules in method position + #[test] fn macro_in_method_posn(){ + expand_crate_str( + "macro_rules! my_method (() => fn thirteen(&self) -> int {13}) + struct A; + impl A{ my_method!()} + fn f(){A.thirteen;}".to_string()); + } + // run one of the renaming tests fn run_renaming_test(t: &RenamingTest, test_idx: uint) { let invalid_name = token::special_idents::invalid.name; From e59dd484c2cbdba5aca039f8895532599914d550 Mon Sep 17 00:00:00 2001 From: John Clements Date: Fri, 4 Jul 2014 11:24:28 -0700 Subject: [PATCH 11/17] implement hygiene for method args --- src/libsyntax/ext/expand.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 47c86f1c9f5c2..f51002f734cd4 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -934,6 +934,27 @@ impl<'a> Folder for PatIdentRenamer<'a> { } } +// expand a method +fn expand_method(m: &ast::Method, fld: &mut MacroExpander) -> Gc { + let id = fld.new_id(m.id); + let (rewritten_fn_decl, rewritten_body) + = expand_and_rename_fn_decl_and_block(m.decl,m.body,fld); + + // all of the other standard stuff: + box(GC) ast::Method { + id: id, + ident: fld.fold_ident(m.ident), + attrs: m.attrs.iter().map(|a| fld.fold_attribute(*a)).collect(), + generics: fold_generics(&m.generics, fld), + explicit_self: fld.fold_explicit_self(&m.explicit_self), + fn_style: m.fn_style, + decl: rewritten_fn_decl, + body: rewritten_body, + span: fld.new_span(m.span), + vis: m.vis + } +} + /// Given a fn_decl and a block and a MacroExpander, expand the fn_decl, then use the /// PatIdents in its arguments to perform renaming in the FnDecl and /// the block, returning both the new FnDecl and the new Block. @@ -988,6 +1009,10 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> { expand_arm(arm, self) } + fn fold_method(&mut self, method: Gc) -> Gc { + expand_method(method, self) + } + fn new_span(&mut self, span: Span) -> Span { new_span(self.cx, span) } From 7f575186f988ecc489af95b874afd4f82ccaccd0 Mon Sep 17 00:00:00 2001 From: John Clements Date: Mon, 7 Jul 2014 14:27:07 -0700 Subject: [PATCH 12/17] remove outdated comment I believe this comment is now irrelevant, as a result of commit 6757053cffb585249105fbd76f --- src/libsyntax/parse/parser.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 584670cc65480..b77b366021cbc 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3233,18 +3233,6 @@ impl<'a> Parser<'a> { } else if is_ident(&self.token) && !token::is_any_keyword(&self.token) && self.look_ahead(1, |t| *t == token::NOT) { - // parse a macro invocation. Looks like there's serious - // overlap here; if this clause doesn't catch it (and it - // won't, for brace-delimited macros) it will fall through - // to the macro clause of parse_item_or_view_item. This - // could use some cleanup, it appears to me. - - // whoops! I now have a guess: I'm guessing the "parens-only" - // rule here is deliberate, to allow macro users to use parens - // for things that should be parsed as stmt_mac, and braces - // for things that should expand into items. Tricky, and - // somewhat awkward... and probably undocumented. Of course, - // I could just be wrong. check_expected_item(self, !item_attrs.is_empty()); From 48c3e5f740df66c192ac9436d99283176aef7649 Mon Sep 17 00:00:00 2001 From: John Clements Date: Mon, 7 Jul 2014 15:10:18 -0700 Subject: [PATCH 13/17] comments --- src/libsyntax/ast.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index d98ae9f0e33a0..141938417dfc1 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -884,6 +884,7 @@ pub enum ExplicitSelf_ { pub type ExplicitSelf = Spanned; +// Represents a method declaration #[deriving(PartialEq, Eq, Encodable, Decodable, Hash)] pub struct Method { pub ident: Ident, From af794a5aae761306ffc894ebd2bedd44f2a9703c Mon Sep 17 00:00:00 2001 From: John Clements Date: Mon, 7 Jul 2014 16:26:30 -0700 Subject: [PATCH 14/17] make macros non-capturing --- src/libcollections/treemap.rs | 12 +-- src/libcore/num/mod.rs | 134 +++++++++++++++++----------------- 2 files changed, 73 insertions(+), 73 deletions(-) diff --git a/src/libcollections/treemap.rs b/src/libcollections/treemap.rs index becceffe6d027..c1d782991b43d 100644 --- a/src/libcollections/treemap.rs +++ b/src/libcollections/treemap.rs @@ -185,7 +185,7 @@ impl TreeMap { macro_rules! bound_setup { // initialiser of the iterator to manipulate - ($iter:expr, + ($iter:expr, $k:expr, // whether we are looking for the lower or upper bound. $is_lower_bound:expr) => { { @@ -193,7 +193,7 @@ macro_rules! bound_setup { loop { if !iter.node.is_null() { let node_k = unsafe {&(*iter.node).key}; - match k.cmp(node_k) { + match $k.cmp(node_k) { Less => iter.traverse_left(), Greater => iter.traverse_right(), Equal => { @@ -230,13 +230,13 @@ impl TreeMap { /// Return a lazy iterator to the first key-value pair whose key is not less than `k` /// If all keys in map are less than `k` an empty iterator is returned. pub fn lower_bound<'a>(&'a self, k: &K) -> Entries<'a, K, V> { - bound_setup!(self.iter_for_traversal(), true) + bound_setup!(self.iter_for_traversal(), k, true) } /// Return a lazy iterator to the first key-value pair whose key is greater than `k` /// If all keys in map are not greater than `k` an empty iterator is returned. pub fn upper_bound<'a>(&'a self, k: &K) -> Entries<'a, K, V> { - bound_setup!(self.iter_for_traversal(), false) + bound_setup!(self.iter_for_traversal(), k, false) } /// Get a lazy iterator that should be initialized using @@ -256,7 +256,7 @@ impl TreeMap { /// If all keys in map are less than `k` an empty iterator is /// returned. pub fn mut_lower_bound<'a>(&'a mut self, k: &K) -> MutEntries<'a, K, V> { - bound_setup!(self.mut_iter_for_traversal(), true) + bound_setup!(self.mut_iter_for_traversal(), k, true) } /// Return a lazy iterator to the first key-value pair (with the @@ -265,7 +265,7 @@ impl TreeMap { /// If all keys in map are not greater than `k` an empty iterator /// is returned. pub fn mut_upper_bound<'a>(&'a mut self, k: &K) -> MutEntries<'a, K, V> { - bound_setup!(self.mut_iter_for_traversal(), false) + bound_setup!(self.mut_iter_for_traversal(), k, false) } } diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 1fae362471d9c..3230873883e19 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -769,16 +769,16 @@ pub trait ToPrimitive { } macro_rules! impl_to_primitive_int_to_int( - ($SrcT:ty, $DstT:ty) => ( + ($SrcT:ty, $DstT:ty, $slf:expr) => ( { if size_of::<$SrcT>() <= size_of::<$DstT>() { - Some(*self as $DstT) + Some($slf as $DstT) } else { - let n = *self as i64; + let n = $slf as i64; let min_value: $DstT = Bounded::min_value(); let max_value: $DstT = Bounded::max_value(); if min_value as i64 <= n && n <= max_value as i64 { - Some(*self as $DstT) + Some($slf as $DstT) } else { None } @@ -788,12 +788,12 @@ macro_rules! impl_to_primitive_int_to_int( ) macro_rules! impl_to_primitive_int_to_uint( - ($SrcT:ty, $DstT:ty) => ( + ($SrcT:ty, $DstT:ty, $slf:expr) => ( { let zero: $SrcT = Zero::zero(); let max_value: $DstT = Bounded::max_value(); - if zero <= *self && *self as u64 <= max_value as u64 { - Some(*self as $DstT) + if zero <= $slf && $slf as u64 <= max_value as u64 { + Some($slf as $DstT) } else { None } @@ -805,26 +805,26 @@ macro_rules! impl_to_primitive_int( ($T:ty) => ( impl ToPrimitive for $T { #[inline] - fn to_int(&self) -> Option { impl_to_primitive_int_to_int!($T, int) } + fn to_int(&self) -> Option { impl_to_primitive_int_to_int!($T, int, *self) } #[inline] - fn to_i8(&self) -> Option { impl_to_primitive_int_to_int!($T, i8) } + fn to_i8(&self) -> Option { impl_to_primitive_int_to_int!($T, i8, *self) } #[inline] - fn to_i16(&self) -> Option { impl_to_primitive_int_to_int!($T, i16) } + fn to_i16(&self) -> Option { impl_to_primitive_int_to_int!($T, i16, *self) } #[inline] - fn to_i32(&self) -> Option { impl_to_primitive_int_to_int!($T, i32) } + fn to_i32(&self) -> Option { impl_to_primitive_int_to_int!($T, i32, *self) } #[inline] - fn to_i64(&self) -> Option { impl_to_primitive_int_to_int!($T, i64) } + fn to_i64(&self) -> Option { impl_to_primitive_int_to_int!($T, i64, *self) } #[inline] - fn to_uint(&self) -> Option { impl_to_primitive_int_to_uint!($T, uint) } + fn to_uint(&self) -> Option { impl_to_primitive_int_to_uint!($T, uint, *self) } #[inline] - fn to_u8(&self) -> Option { impl_to_primitive_int_to_uint!($T, u8) } + fn to_u8(&self) -> Option { impl_to_primitive_int_to_uint!($T, u8, *self) } #[inline] - fn to_u16(&self) -> Option { impl_to_primitive_int_to_uint!($T, u16) } + fn to_u16(&self) -> Option { impl_to_primitive_int_to_uint!($T, u16, *self) } #[inline] - fn to_u32(&self) -> Option { impl_to_primitive_int_to_uint!($T, u32) } + fn to_u32(&self) -> Option { impl_to_primitive_int_to_uint!($T, u32, *self) } #[inline] - fn to_u64(&self) -> Option { impl_to_primitive_int_to_uint!($T, u64) } + fn to_u64(&self) -> Option { impl_to_primitive_int_to_uint!($T, u64, *self) } #[inline] fn to_f32(&self) -> Option { Some(*self as f32) } @@ -841,11 +841,11 @@ impl_to_primitive_int!(i32) impl_to_primitive_int!(i64) macro_rules! impl_to_primitive_uint_to_int( - ($DstT:ty) => ( + ($DstT:ty, $slf:expr) => ( { let max_value: $DstT = Bounded::max_value(); - if *self as u64 <= max_value as u64 { - Some(*self as $DstT) + if $slf as u64 <= max_value as u64 { + Some($slf as $DstT) } else { None } @@ -854,15 +854,15 @@ macro_rules! impl_to_primitive_uint_to_int( ) macro_rules! impl_to_primitive_uint_to_uint( - ($SrcT:ty, $DstT:ty) => ( + ($SrcT:ty, $DstT:ty, $slf:expr) => ( { if size_of::<$SrcT>() <= size_of::<$DstT>() { - Some(*self as $DstT) + Some($slf as $DstT) } else { let zero: $SrcT = Zero::zero(); let max_value: $DstT = Bounded::max_value(); - if zero <= *self && *self as u64 <= max_value as u64 { - Some(*self as $DstT) + if zero <= $slf && $slf as u64 <= max_value as u64 { + Some($slf as $DstT) } else { None } @@ -875,26 +875,26 @@ macro_rules! impl_to_primitive_uint( ($T:ty) => ( impl ToPrimitive for $T { #[inline] - fn to_int(&self) -> Option { impl_to_primitive_uint_to_int!(int) } + fn to_int(&self) -> Option { impl_to_primitive_uint_to_int!(int, *self) } #[inline] - fn to_i8(&self) -> Option { impl_to_primitive_uint_to_int!(i8) } + fn to_i8(&self) -> Option { impl_to_primitive_uint_to_int!(i8, *self) } #[inline] - fn to_i16(&self) -> Option { impl_to_primitive_uint_to_int!(i16) } + fn to_i16(&self) -> Option { impl_to_primitive_uint_to_int!(i16, *self) } #[inline] - fn to_i32(&self) -> Option { impl_to_primitive_uint_to_int!(i32) } + fn to_i32(&self) -> Option { impl_to_primitive_uint_to_int!(i32, *self) } #[inline] - fn to_i64(&self) -> Option { impl_to_primitive_uint_to_int!(i64) } + fn to_i64(&self) -> Option { impl_to_primitive_uint_to_int!(i64, *self) } #[inline] - fn to_uint(&self) -> Option { impl_to_primitive_uint_to_uint!($T, uint) } + fn to_uint(&self) -> Option { impl_to_primitive_uint_to_uint!($T, uint, *self) } #[inline] - fn to_u8(&self) -> Option { impl_to_primitive_uint_to_uint!($T, u8) } + fn to_u8(&self) -> Option { impl_to_primitive_uint_to_uint!($T, u8, *self) } #[inline] - fn to_u16(&self) -> Option { impl_to_primitive_uint_to_uint!($T, u16) } + fn to_u16(&self) -> Option { impl_to_primitive_uint_to_uint!($T, u16, *self) } #[inline] - fn to_u32(&self) -> Option { impl_to_primitive_uint_to_uint!($T, u32) } + fn to_u32(&self) -> Option { impl_to_primitive_uint_to_uint!($T, u32, *self) } #[inline] - fn to_u64(&self) -> Option { impl_to_primitive_uint_to_uint!($T, u64) } + fn to_u64(&self) -> Option { impl_to_primitive_uint_to_uint!($T, u64, *self) } #[inline] fn to_f32(&self) -> Option { Some(*self as f32) } @@ -911,14 +911,14 @@ impl_to_primitive_uint!(u32) impl_to_primitive_uint!(u64) macro_rules! impl_to_primitive_float_to_float( - ($SrcT:ty, $DstT:ty) => ( + ($SrcT:ty, $DstT:ty, $slf:expr) => ( if size_of::<$SrcT>() <= size_of::<$DstT>() { - Some(*self as $DstT) + Some($slf as $DstT) } else { - let n = *self as f64; + let n = $slf as f64; let max_value: $SrcT = Bounded::max_value(); if -max_value as f64 <= n && n <= max_value as f64 { - Some(*self as $DstT) + Some($slf as $DstT) } else { None } @@ -952,9 +952,9 @@ macro_rules! impl_to_primitive_float( fn to_u64(&self) -> Option { Some(*self as u64) } #[inline] - fn to_f32(&self) -> Option { impl_to_primitive_float_to_float!($T, f32) } + fn to_f32(&self) -> Option { impl_to_primitive_float_to_float!($T, f32, *self) } #[inline] - fn to_f64(&self) -> Option { impl_to_primitive_float_to_float!($T, f64) } + fn to_f64(&self) -> Option { impl_to_primitive_float_to_float!($T, f64, *self) } } ) ) @@ -1104,38 +1104,38 @@ pub fn from_f64(n: f64) -> Option { } macro_rules! impl_from_primitive( - ($T:ty, $to_ty:expr) => ( + ($T:ty, $to_ty:ident) => ( impl FromPrimitive for $T { - #[inline] fn from_int(n: int) -> Option<$T> { $to_ty } - #[inline] fn from_i8(n: i8) -> Option<$T> { $to_ty } - #[inline] fn from_i16(n: i16) -> Option<$T> { $to_ty } - #[inline] fn from_i32(n: i32) -> Option<$T> { $to_ty } - #[inline] fn from_i64(n: i64) -> Option<$T> { $to_ty } - - #[inline] fn from_uint(n: uint) -> Option<$T> { $to_ty } - #[inline] fn from_u8(n: u8) -> Option<$T> { $to_ty } - #[inline] fn from_u16(n: u16) -> Option<$T> { $to_ty } - #[inline] fn from_u32(n: u32) -> Option<$T> { $to_ty } - #[inline] fn from_u64(n: u64) -> Option<$T> { $to_ty } - - #[inline] fn from_f32(n: f32) -> Option<$T> { $to_ty } - #[inline] fn from_f64(n: f64) -> Option<$T> { $to_ty } + #[inline] fn from_int(n: int) -> Option<$T> { n.$to_ty() } + #[inline] fn from_i8(n: i8) -> Option<$T> { n.$to_ty() } + #[inline] fn from_i16(n: i16) -> Option<$T> { n.$to_ty() } + #[inline] fn from_i32(n: i32) -> Option<$T> { n.$to_ty() } + #[inline] fn from_i64(n: i64) -> Option<$T> { n.$to_ty() } + + #[inline] fn from_uint(n: uint) -> Option<$T> { n.$to_ty() } + #[inline] fn from_u8(n: u8) -> Option<$T> { n.$to_ty() } + #[inline] fn from_u16(n: u16) -> Option<$T> { n.$to_ty() } + #[inline] fn from_u32(n: u32) -> Option<$T> { n.$to_ty() } + #[inline] fn from_u64(n: u64) -> Option<$T> { n.$to_ty() } + + #[inline] fn from_f32(n: f32) -> Option<$T> { n.$to_ty() } + #[inline] fn from_f64(n: f64) -> Option<$T> { n.$to_ty() } } ) ) -impl_from_primitive!(int, n.to_int()) -impl_from_primitive!(i8, n.to_i8()) -impl_from_primitive!(i16, n.to_i16()) -impl_from_primitive!(i32, n.to_i32()) -impl_from_primitive!(i64, n.to_i64()) -impl_from_primitive!(uint, n.to_uint()) -impl_from_primitive!(u8, n.to_u8()) -impl_from_primitive!(u16, n.to_u16()) -impl_from_primitive!(u32, n.to_u32()) -impl_from_primitive!(u64, n.to_u64()) -impl_from_primitive!(f32, n.to_f32()) -impl_from_primitive!(f64, n.to_f64()) +impl_from_primitive!(int, to_int) +impl_from_primitive!(i8, to_i8) +impl_from_primitive!(i16, to_i16) +impl_from_primitive!(i32, to_i32) +impl_from_primitive!(i64, to_i64) +impl_from_primitive!(uint, to_uint) +impl_from_primitive!(u8, to_u8) +impl_from_primitive!(u16, to_u16) +impl_from_primitive!(u32, to_u32) +impl_from_primitive!(u64, to_u64) +impl_from_primitive!(f32, to_f32) +impl_from_primitive!(f64, to_f64) /// Cast from one machine scalar to another. /// From 69c27546ee37ac2d0384748a21c7dad00642a223 Mon Sep 17 00:00:00 2001 From: John Clements Date: Mon, 7 Jul 2014 17:43:09 -0700 Subject: [PATCH 15/17] macro literals should be compared by name only --- src/libsyntax/ext/expand.rs | 8 ++++++++ src/libsyntax/ext/tt/macro_parser.rs | 3 +-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index f51002f734cd4..d5a9f34dcb922 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1325,6 +1325,14 @@ mod test { "macro_rules! m((a)=>(13)) fn main(){m!(a);}".to_string()); } + // should be able to use a bound identifier as a literal in a macro definition: + #[test] fn self_macro_parsing(){ + expand_crate_str( + "macro_rules! foo ((zz) => (287u;)) + fn f(zz : int) {foo!(zz);}".to_string() + ); + } + // renaming tests expand a crate and then check that the bindings match // the right varrefs. The specification of the test case includes the // text of the crate, and also an array of arrays. Each element in the diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs index 913e0427bda22..b30ede70f0e4b 100644 --- a/src/libsyntax/ext/tt/macro_parser.rs +++ b/src/libsyntax/ext/tt/macro_parser.rs @@ -354,8 +354,7 @@ pub fn parse(sess: &ParseSess, MatchNonterminal(_,_,_) => { bb_eis.push(ei) } MatchTok(ref t) => { let mut ei_t = ei.clone(); - //if (token_name_eq(t,&tok)) { - if token::mtwt_token_eq(t,&tok) { + if token_name_eq(t,&tok) { ei_t.idx += 1; next_eis.push(ei_t); } From 19e718b34def6c3f98372a40352ab9c889ff9f7a Mon Sep 17 00:00:00 2001 From: John Clements Date: Sun, 6 Jul 2014 15:10:57 -0700 Subject: [PATCH 16/17] carry self ident forward through re-parsing formerly, the self identifier was being discarded during parsing, which stymies hygiene. The best fix here seems to be to attach a self identifier to ExplicitSelf_, a change that rippled through the rest of the compiler, but without any obvious damage. --- src/librustc/metadata/decoder.rs | 7 +- src/librustc/metadata/encoder.rs | 8 +-- src/librustc/middle/trans/meth.rs | 15 ++-- src/librustc/middle/typeck/astconv.rs | 6 +- src/librustc/middle/typeck/check/method.rs | 12 ++-- .../middle/typeck/infer/error_reporting.rs | 6 +- src/librustdoc/clean/mod.rs | 6 +- src/libsyntax/ast.rs | 15 ++-- src/libsyntax/ext/deriving/generic/mod.rs | 4 +- src/libsyntax/ext/deriving/generic/ty.rs | 10 +-- src/libsyntax/ext/expand.rs | 17 ++++- src/libsyntax/fold.rs | 6 +- src/libsyntax/parse/parser.rs | 72 +++++++++++-------- src/libsyntax/print/pprust.rs | 6 +- src/libsyntax/visit.rs | 4 +- 15 files changed, 114 insertions(+), 80 deletions(-) diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index 0b9ed37c8384b..8a2b95ae463b4 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -739,10 +739,11 @@ fn get_explicit_self(item: ebml::Doc) -> ast::ExplicitSelf_ { let explicit_self_kind = string.as_bytes()[0]; match explicit_self_kind as char { 's' => ast::SelfStatic, - 'v' => ast::SelfValue, - '~' => ast::SelfUniq, + 'v' => ast::SelfValue(special_idents::self_), + '~' => ast::SelfUniq(special_idents::self_), // FIXME(#4846) expl. region - '&' => ast::SelfRegion(None, get_mutability(string.as_bytes()[1])), + '&' => ast::SelfRegion(None, get_mutability(string.as_bytes()[1]), + special_idents::self_), _ => fail!("unknown self type code: `{}`", explicit_self_kind as char) } } diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index 6c544e3809a07..fbf0288418ab8 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -628,10 +628,10 @@ fn encode_explicit_self(ebml_w: &mut Encoder, explicit_self: ast::ExplicitSelf_) // Encode the base self type. match explicit_self { - SelfStatic => { ebml_w.writer.write(&[ 's' as u8 ]); } - SelfValue => { ebml_w.writer.write(&[ 'v' as u8 ]); } - SelfUniq => { ebml_w.writer.write(&[ '~' as u8 ]); } - SelfRegion(_, m) => { + SelfStatic => { ebml_w.writer.write(&[ 's' as u8 ]); } + SelfValue(_) => { ebml_w.writer.write(&[ 'v' as u8 ]); } + SelfUniq(_) => { ebml_w.writer.write(&[ '~' as u8 ]); } + SelfRegion(_, m, _) => { // FIXME(#4846) encode custom lifetime ebml_w.writer.write(&['&' as u8]); encode_mutability(ebml_w, m); diff --git a/src/librustc/middle/trans/meth.rs b/src/librustc/middle/trans/meth.rs index e1d43c5240059..c79e435707aee 100644 --- a/src/librustc/middle/trans/meth.rs +++ b/src/librustc/middle/trans/meth.rs @@ -502,12 +502,15 @@ fn emit_vtable_methods(bcx: &Block, ExprId(0), substs.clone(), vtables.clone()); - if m.explicit_self == ast::SelfValue { - fn_ref = trans_unboxing_shim(bcx, - fn_ref, - &*m, - m_id, - substs.clone()); + match m.explicit_self { + ast::SelfValue(_) => { + fn_ref = trans_unboxing_shim(bcx, + fn_ref, + &*m, + m_id, + substs.clone()); + }, + _ => {} } fn_ref } diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index d565f144f3656..90331d8f43430 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -938,10 +938,10 @@ fn ty_of_method_or_bare_fn(this: &AC, id: ast::NodeId, let self_ty = opt_self_info.and_then(|self_info| { match self_info.explicit_self.node { ast::SelfStatic => None, - ast::SelfValue => { + ast::SelfValue(_) => { Some(self_info.untransformed_self_ty) } - ast::SelfRegion(ref lifetime, mutability) => { + ast::SelfRegion(ref lifetime, mutability, _) => { let region = opt_ast_region_to_region(this, &rb, self_info.explicit_self.span, @@ -950,7 +950,7 @@ fn ty_of_method_or_bare_fn(this: &AC, id: ast::NodeId, ty::mt {ty: self_info.untransformed_self_ty, mutbl: mutability})) } - ast::SelfUniq => { + ast::SelfUniq(_) => { Some(ty::mk_uniq(this.tcx(), self_info.untransformed_self_ty)) } } diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs index c1a000841a4d1..a184ecac9dea6 100644 --- a/src/librustc/middle/typeck/check/method.rs +++ b/src/librustc/middle/typeck/check/method.rs @@ -270,12 +270,12 @@ fn construct_transformed_self_ty_for_object( ast::SelfStatic => { tcx.sess.span_bug(span, "static method for object type receiver"); } - ast::SelfValue => { + ast::SelfValue(_) => { let tr = ty::mk_trait(tcx, trait_def_id, obj_substs, ty::empty_builtin_bounds()); ty::mk_uniq(tcx, tr) } - ast::SelfRegion(..) | ast::SelfUniq => { + ast::SelfRegion(..) | ast::SelfUniq(..) => { let transformed_self_ty = *method_ty.fty.sig.inputs.get(0); match ty::get(transformed_self_ty).sty { ty::ty_rptr(r, mt) => { // must be SelfRegion @@ -1227,7 +1227,7 @@ impl<'a> LookupContext<'a> { through an object"); } - ast::SelfValue | ast::SelfRegion(..) | ast::SelfUniq => {} + ast::SelfValue(_) | ast::SelfRegion(..) | ast::SelfUniq(_) => {} } // reason (a) above @@ -1296,7 +1296,7 @@ impl<'a> LookupContext<'a> { self.report_statics == ReportStaticMethods } - SelfValue => { + SelfValue(_) => { debug!("(is relevant?) explicit self is by-value"); match ty::get(rcvr_ty).sty { ty::ty_uniq(typ) => { @@ -1319,7 +1319,7 @@ impl<'a> LookupContext<'a> { } } - SelfRegion(_, m) => { + SelfRegion(_, m, _) => { debug!("(is relevant?) explicit self is a region"); match ty::get(rcvr_ty).sty { ty::ty_rptr(_, mt) => { @@ -1339,7 +1339,7 @@ impl<'a> LookupContext<'a> { } } - SelfUniq => { + SelfUniq(_) => { debug!("(is relevant?) explicit self is a unique pointer"); match ty::get(rcvr_ty).sty { ty::ty_uniq(typ) => { diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs index cdd51c4fa7126..b0c9900be909c 100644 --- a/src/librustc/middle/typeck/infer/error_reporting.rs +++ b/src/librustc/middle/typeck/infer/error_reporting.rs @@ -947,16 +947,16 @@ impl<'a> Rebuilder<'a> { -> Option { match expl_self_opt { Some(expl_self) => match expl_self { - ast::SelfRegion(lt_opt, muta) => match lt_opt { + ast::SelfRegion(lt_opt, muta, id) => match lt_opt { Some(lt) => if region_names.contains(<.name) { - return Some(ast::SelfRegion(Some(lifetime), muta)); + return Some(ast::SelfRegion(Some(lifetime), muta, id)); }, None => { let anon = self.cur_anon.get(); self.inc_and_offset_cur_anon(1); if anon_nums.contains(&anon) { self.track_anon(anon); - return Some(ast::SelfRegion(Some(lifetime), muta)); + return Some(ast::SelfRegion(Some(lifetime), muta, id)); } } }, diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 6c40ee21040ad..af0b6a1cb21d1 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -775,9 +775,9 @@ impl Clean for ast::ExplicitSelf_ { fn clean(&self) -> SelfTy { match *self { ast::SelfStatic => SelfStatic, - ast::SelfValue => SelfValue, - ast::SelfUniq => SelfOwned, - ast::SelfRegion(lt, mt) => SelfBorrowed(lt.clean(), mt.clean()), + ast::SelfValue(_) => SelfValue, + ast::SelfUniq(_) => SelfOwned, + ast::SelfRegion(lt, mt, _) => SelfBorrowed(lt.clean(), mt.clean()), } } } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 141938417dfc1..5f3adbdb54df4 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -14,7 +14,7 @@ use codemap::{Span, Spanned, DUMMY_SP}; use abi::Abi; use ast_util; use owned_slice::OwnedSlice; -use parse::token::{InternedString, special_idents, str_to_ident}; +use parse::token::{InternedString, str_to_ident}; use parse::token; use std::fmt; @@ -824,8 +824,8 @@ pub struct Arg { } impl Arg { - pub fn new_self(span: Span, mutability: Mutability) -> Arg { - let path = Spanned{span:span,node:special_idents::self_}; + pub fn new_self(span: Span, mutability: Mutability, self_ident: Ident) -> Arg { + let path = Spanned{span:span,node:self_ident}; Arg { // HACK(eddyb) fake type for the self argument. ty: P(Ty { @@ -874,12 +874,13 @@ pub enum RetStyle { Return, // everything else } +/// Represents the kind of 'self' associated with a method #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)] pub enum ExplicitSelf_ { - SelfStatic, // no self - SelfValue, // `self` - SelfRegion(Option, Mutability), // `&'lt self`, `&'lt mut self` - SelfUniq // `~self` + SelfStatic, // no self + SelfValue(Ident), // `self` + SelfRegion(Option, Mutability, Ident), // `&'lt self`, `&'lt mut self` + SelfUniq(Ident), // `~self` } pub type ExplicitSelf = Spanned; diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs index 7ad11b186f500..764c88cc954ed 100644 --- a/src/libsyntax/ext/deriving/generic/mod.rs +++ b/src/libsyntax/ext/deriving/generic/mod.rs @@ -191,6 +191,7 @@ use codemap; use codemap::Span; use owned_slice::OwnedSlice; use parse::token::InternedString; +use parse::token::special_idents; use self::ty::*; @@ -617,7 +618,8 @@ impl<'a> MethodDef<'a> { let self_arg = match explicit_self.node { ast::SelfStatic => None, - _ => Some(ast::Arg::new_self(trait_.span, ast::MutImmutable)) + // creating fresh self id + _ => Some(ast::Arg::new_self(trait_.span, ast::MutImmutable, special_idents::self_)) }; let args = { let args = arg_types.move_iter().map(|(name, ty)| { diff --git a/src/libsyntax/ext/deriving/generic/ty.rs b/src/libsyntax/ext/deriving/generic/ty.rs index 28f39a4cb8c0f..b53281f99633f 100644 --- a/src/libsyntax/ext/deriving/generic/ty.rs +++ b/src/libsyntax/ext/deriving/generic/ty.rs @@ -19,6 +19,7 @@ use ext::base::ExtCtxt; use ext::build::AstBuilder; use codemap::{Span,respan}; use owned_slice::OwnedSlice; +use parse::token::special_idents; use std::gc::Gc; @@ -244,22 +245,23 @@ impl<'a> LifetimeBounds<'a> { } } - pub fn get_explicit_self(cx: &ExtCtxt, span: Span, self_ptr: &Option) -> (Gc, ast::ExplicitSelf) { + // this constructs a fresh `self` path, which will match the fresh `self` binding + // created below. let self_path = cx.expr_self(span); match *self_ptr { None => { - (self_path, respan(span, ast::SelfValue)) + (self_path, respan(span, ast::SelfValue(special_idents::self_))) } Some(ref ptr) => { let self_ty = respan( span, match *ptr { - Send => ast::SelfUniq, + Send => ast::SelfUniq(special_idents::self_), Borrowed(ref lt, mutbl) => { let lt = lt.map(|s| cx.lifetime(span, cx.ident_of(s).name)); - ast::SelfRegion(lt, mutbl) + ast::SelfRegion(lt, mutbl, special_idents::self_) } }); let self_expr = cx.expr_deref(span, self_path); diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index d5a9f34dcb922..9fe431cfb6c75 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -1501,8 +1501,8 @@ mod test { 0) } - // macro_rules in method position - #[test] fn macro_in_method_posn(){ + // macro_rules in method position. Sadly, unimplemented. + #[ignore] #[test] fn macro_in_method_posn(){ expand_crate_str( "macro_rules! my_method (() => fn thirteen(&self) -> int {13}) struct A; @@ -1510,6 +1510,19 @@ mod test { fn f(){A.thirteen;}".to_string()); } + // another nested macro + // expands to impl Entries {fn size_hint(&self_1) {self_1;} + #[test] fn item_macro_workaround(){ + run_renaming_test( + &("macro_rules! item { ($i:item) => {$i}} + struct Entries; + macro_rules! iterator_impl { + () => { item!( impl Entries { fn size_hint(&self) { self;}})}} + iterator_impl! { }", + vec!(vec!(0)), true), + 0) + } + // run one of the renaming tests fn run_renaming_test(t: &RenamingTest, test_idx: uint) { let invalid_name = token::special_idents::invalid.name; diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index ee4810b4b5430..bcdf920e5dd41 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -334,9 +334,9 @@ pub trait Folder { fn fold_explicit_self_(&mut self, es: &ExplicitSelf_) -> ExplicitSelf_ { match *es { - SelfStatic | SelfValue | SelfUniq => *es, - SelfRegion(ref lifetime, m) => { - SelfRegion(fold_opt_lifetime(lifetime, self), m) + SelfStatic | SelfValue(_) | SelfUniq(_) => *es, + SelfRegion(ref lifetime, m, id) => { + SelfRegion(fold_opt_lifetime(lifetime, self), m, id) } } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index b77b366021cbc..ac4cbf3aa8e55 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3748,13 +3748,18 @@ impl<'a> Parser<'a> { } } - fn expect_self_ident(&mut self) { - if !self.is_self_ident() { - let token_str = self.this_token_to_string(); - self.fatal(format!("expected `self` but found `{}`", - token_str).as_slice()) + fn expect_self_ident(&mut self) -> ast::Ident { + match self.token { + token::IDENT(id, false) if id.name == special_idents::self_.name => { + self.bump(); + id + }, + _ => { + let token_str = self.this_token_to_string(); + self.fatal(format!("expected `self` but found `{}`", + token_str).as_slice()) + } } - self.bump(); } // parse the argument list and result type of a function @@ -3774,24 +3779,21 @@ impl<'a> Parser<'a> { if this.look_ahead(1, |t| token::is_keyword(keywords::Self, t)) { this.bump(); - this.expect_self_ident(); - SelfRegion(None, MutImmutable) + SelfRegion(None, MutImmutable, this.expect_self_ident()) } else if this.look_ahead(1, |t| Parser::token_is_mutability(t)) && this.look_ahead(2, |t| token::is_keyword(keywords::Self, t)) { this.bump(); let mutability = this.parse_mutability(); - this.expect_self_ident(); - SelfRegion(None, mutability) + SelfRegion(None, mutability, this.expect_self_ident()) } else if this.look_ahead(1, |t| Parser::token_is_lifetime(t)) && this.look_ahead(2, |t| token::is_keyword(keywords::Self, t)) { this.bump(); let lifetime = this.parse_lifetime(); - this.expect_self_ident(); - SelfRegion(Some(lifetime), MutImmutable) + SelfRegion(Some(lifetime), MutImmutable, this.expect_self_ident()) } else if this.look_ahead(1, |t| Parser::token_is_lifetime(t)) && this.look_ahead(2, |t| { Parser::token_is_mutability(t) @@ -3801,8 +3803,7 @@ impl<'a> Parser<'a> { this.bump(); let lifetime = this.parse_lifetime(); let mutability = this.parse_mutability(); - this.expect_self_ident(); - SelfRegion(Some(lifetime), mutability) + SelfRegion(Some(lifetime), mutability, this.expect_self_ident()) } else { SelfStatic } @@ -3822,15 +3823,13 @@ impl<'a> Parser<'a> { // We need to make sure it isn't a type if self.look_ahead(1, |t| token::is_keyword(keywords::Self, t)) { self.bump(); - self.expect_self_ident(); - SelfUniq + SelfUniq(self.expect_self_ident()) } else { SelfStatic } } token::IDENT(..) if self.is_self_ident() => { - self.bump(); - SelfValue + SelfValue(self.expect_self_ident()) } token::BINOP(token::STAR) => { // Possibly "*self" or "*mut self" -- not supported. Try to avoid @@ -3844,29 +3843,32 @@ impl<'a> Parser<'a> { self.span_err(span, "cannot pass self by unsafe pointer"); self.bump(); } - SelfValue + // error case, making bogus self ident: + SelfValue(special_idents::self_) } _ if Parser::token_is_mutability(&self.token) && self.look_ahead(1, |t| token::is_keyword(keywords::Self, t)) => { mutbl_self = self.parse_mutability(); - self.expect_self_ident(); - SelfValue + SelfValue(self.expect_self_ident()) } _ if Parser::token_is_mutability(&self.token) && self.look_ahead(1, |t| *t == token::TILDE) && self.look_ahead(2, |t| token::is_keyword(keywords::Self, t)) => { mutbl_self = self.parse_mutability(); self.bump(); - self.expect_self_ident(); - SelfUniq + SelfUniq(self.expect_self_ident()) } _ => SelfStatic }; let explicit_self_sp = mk_sp(lo, self.span.hi); - // If we parsed a self type, expect a comma before the argument list. - let fn_inputs = if explicit_self != SelfStatic { + // shared fall-through for the three cases below. borrowing prevents simply + // writing this as a closure + macro_rules! parse_remaining_arguments { + ($self_id:ident) => + { + // If we parsed a self type, expect a comma before the argument list. match self.token { token::COMMA => { self.bump(); @@ -3876,11 +3878,11 @@ impl<'a> Parser<'a> { sep, parse_arg_fn ); - fn_inputs.unshift(Arg::new_self(explicit_self_sp, mutbl_self)); + fn_inputs.unshift(Arg::new_self(explicit_self_sp, mutbl_self, $self_id)); fn_inputs } token::RPAREN => { - vec!(Arg::new_self(explicit_self_sp, mutbl_self)) + vec!(Arg::new_self(explicit_self_sp, mutbl_self, $self_id)) } _ => { let token_str = self.this_token_to_string(); @@ -3888,11 +3890,21 @@ impl<'a> Parser<'a> { token_str).as_slice()) } } - } else { - let sep = seq_sep_trailing_disallowed(token::COMMA); - self.parse_seq_to_before_end(&token::RPAREN, sep, parse_arg_fn) + } + } + + let fn_inputs = match explicit_self { + SelfStatic => { + let sep = seq_sep_trailing_disallowed(token::COMMA); + self.parse_seq_to_before_end(&token::RPAREN, sep, parse_arg_fn) + } + SelfValue(id) => parse_remaining_arguments!(id), + SelfRegion(_,_,id) => parse_remaining_arguments!(id), + SelfUniq(id) => parse_remaining_arguments!(id) + }; + self.expect(&token::RPAREN); let hi = self.span.hi; diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index e695241472b09..a5d70a9333dde 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1967,13 +1967,13 @@ impl<'a> State<'a> { try!(self.print_mutability(mutbl)); match explicit_self { ast::SelfStatic => { return Ok(false); } - ast::SelfValue => { + ast::SelfValue(_) => { try!(word(&mut self.s, "self")); } - ast::SelfUniq => { + ast::SelfUniq(_) => { try!(word(&mut self.s, "~self")); } - ast::SelfRegion(ref lt, m) => { + ast::SelfRegion(ref lt, m, _) => { try!(word(&mut self.s, "&")); try!(self.print_opt_lifetime(lt)); try!(self.print_mutability(m)); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 4ab064a88b795..df34ff30db67f 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -202,8 +202,8 @@ pub fn walk_explicit_self>(visitor: &mut V, explicit_self: &ExplicitSelf, env: E) { match explicit_self.node { - SelfStatic | SelfValue | SelfUniq => {} - SelfRegion(ref lifetime, _) => { + SelfStatic | SelfValue(_) | SelfUniq(_) => {}, + SelfRegion(ref lifetime, _, _) => { visitor.visit_opt_lifetime_ref(explicit_self.span, lifetime, env) } } From 4c312b6d0ddc4de7e00176d5099dad299473aabd Mon Sep 17 00:00:00 2001 From: John Clements Date: Tue, 8 Jul 2014 15:02:33 -0700 Subject: [PATCH 17/17] fix hygiene for test case --- src/test/run-pass/issue-7911.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/run-pass/issue-7911.rs b/src/test/run-pass/issue-7911.rs index 9e43e3ef1aa75..75494c47dcef1 100644 --- a/src/test/run-pass/issue-7911.rs +++ b/src/test/run-pass/issue-7911.rs @@ -27,19 +27,19 @@ trait Test { fn get_mut<'r>(&'r mut self) -> &'r mut FooBar; } -macro_rules! generate_test(($type_:path, $field:expr) => ( +macro_rules! generate_test(($type_:path, $slf:ident, $field:expr) => ( impl Test for $type_ { - fn get_immut<'r>(&'r self) -> &'r FooBar { + fn get_immut<'r>(&'r $slf) -> &'r FooBar { &$field as &FooBar } - fn get_mut<'r>(&'r mut self) -> &'r mut FooBar { + fn get_mut<'r>(&'r mut $slf) -> &'r mut FooBar { &mut $field as &mut FooBar } } )) -generate_test!(Foo, self.bar) +generate_test!(Foo, self, self.bar) pub fn main() { let mut foo: Foo = Foo { bar: Bar(42) };