diff --git a/src/grammar/RustLexer.g4 b/src/grammar/RustLexer.g4 index 5c13295451c7a..a63fc59e50b07 100644 --- a/src/grammar/RustLexer.g4 +++ b/src/grammar/RustLexer.g4 @@ -10,7 +10,7 @@ lexer grammar RustLexer; tokens { EQ, LT, LE, EQEQ, NE, GE, GT, ANDAND, OROR, NOT, TILDE, PLUS, MINUS, STAR, SLASH, PERCENT, CARET, AND, OR, SHL, SHR, BINOP, - BINOPEQ, AT, DOT, DOTDOT, DOTDOTDOT, COMMA, SEMI, COLON, + BINOPEQ, LARROW, AT, DOT, DOTDOT, DOTDOTDOT, COMMA, SEMI, COLON, MOD_SEP, RARROW, FAT_ARROW, LPAREN, RPAREN, LBRACKET, RBRACKET, LBRACE, RBRACE, POUND, DOLLAR, UNDERSCORE, LIT_CHAR, LIT_BYTE, LIT_INTEGER, LIT_FLOAT, LIT_STR, LIT_STR_RAW, LIT_BYTE_STR, @@ -44,6 +44,7 @@ AND : '&' ; OR : '|' ; SHL : '<<' ; SHR : '>>' ; +LARROW : '<-' ; BINOP : PLUS @@ -56,6 +57,7 @@ BINOP | OR | SHL | SHR + | LARROW ; BINOPEQ : BINOP EQ ; diff --git a/src/grammar/lexer.l b/src/grammar/lexer.l index dd7b1c74de749..77737c99496f3 100644 --- a/src/grammar/lexer.l +++ b/src/grammar/lexer.l @@ -317,6 +317,7 @@ r/# { \\[^n\nrt\\\x27\x220] { return -1; } (.|\n) { yymore(); } +\<- { return LARROW; } -\> { return RARROW; } - { return '-'; } -= { return MINUSEQ; } diff --git a/src/grammar/parser-lalr.y b/src/grammar/parser-lalr.y index 75d9d28242f4e..b310b2b3351c5 100644 --- a/src/grammar/parser-lalr.y +++ b/src/grammar/parser-lalr.y @@ -45,6 +45,7 @@ extern char *yytext; %token DOTDOTDOT %token MOD_SEP %token RARROW +%token LARROW %token FAT_ARROW %token LIT_BYTE %token LIT_CHAR @@ -167,7 +168,8 @@ extern char *yytext; // prefix_exprs %precedence RETURN -%left '=' SHLEQ SHREQ MINUSEQ ANDEQ OREQ PLUSEQ STAREQ SLASHEQ CARETEQ PERCENTEQ +%right '=' SHLEQ SHREQ MINUSEQ ANDEQ OREQ PLUSEQ STAREQ SLASHEQ CARETEQ PERCENTEQ +%right LARROW %left OROR %left ANDAND %left EQEQ NE @@ -1316,6 +1318,7 @@ nonblock_expr | RETURN expr { $$ = mk_node("ExprRet", 1, $2); } | BREAK { $$ = mk_node("ExprBreak", 0); } | BREAK lifetime { $$ = mk_node("ExprBreak", 1, $2); } +| nonblock_expr LARROW expr { $$ = mk_node("ExprInPlace", 2, $1, $3); } | nonblock_expr '=' expr { $$ = mk_node("ExprAssign", 2, $1, $3); } | nonblock_expr SHLEQ expr { $$ = mk_node("ExprAssignShl", 2, $1, $3); } | nonblock_expr SHREQ expr { $$ = mk_node("ExprAssignShr", 2, $1, $3); } @@ -1375,6 +1378,7 @@ expr | RETURN expr { $$ = mk_node("ExprRet", 1, $2); } | BREAK { $$ = mk_node("ExprBreak", 0); } | BREAK ident { $$ = mk_node("ExprBreak", 1, $2); } +| expr LARROW expr { $$ = mk_node("ExprInPlace", 2, $1, $3); } | expr '=' expr { $$ = mk_node("ExprAssign", 2, $1, $3); } | expr SHLEQ expr { $$ = mk_node("ExprAssignShl", 2, $1, $3); } | expr SHREQ expr { $$ = mk_node("ExprAssignShr", 2, $1, $3); } @@ -1435,6 +1439,7 @@ nonparen_expr | RETURN expr { $$ = mk_node("ExprRet", 1, $2); } | BREAK { $$ = mk_node("ExprBreak", 0); } | BREAK ident { $$ = mk_node("ExprBreak", 1, $2); } +| nonparen_expr LARROW nonparen_expr { $$ = mk_node("ExprInPlace", 2, $1, $3); } | nonparen_expr '=' nonparen_expr { $$ = mk_node("ExprAssign", 2, $1, $3); } | nonparen_expr SHLEQ nonparen_expr { $$ = mk_node("ExprAssignShl", 2, $1, $3); } | nonparen_expr SHREQ nonparen_expr { $$ = mk_node("ExprAssignShr", 2, $1, $3); } @@ -1495,6 +1500,7 @@ expr_nostruct | RETURN expr { $$ = mk_node("ExprRet", 1, $2); } | BREAK { $$ = mk_node("ExprBreak", 0); } | BREAK ident { $$ = mk_node("ExprBreak", 1, $2); } +| expr_nostruct LARROW expr_nostruct { $$ = mk_node("ExprInPlace", 2, $1, $3); } | expr_nostruct '=' expr_nostruct { $$ = mk_node("ExprAssign", 2, $1, $3); } | expr_nostruct SHLEQ expr_nostruct { $$ = mk_node("ExprAssignShl", 2, $1, $3); } | expr_nostruct SHREQ expr_nostruct { $$ = mk_node("ExprAssignShr", 2, $1, $3); } @@ -1794,6 +1800,7 @@ unpaired_token | GE { $$ = mk_atom(yytext); } | ANDAND { $$ = mk_atom(yytext); } | OROR { $$ = mk_atom(yytext); } +| LARROW { $$ = mk_atom(yytext); } | SHLEQ { $$ = mk_atom(yytext); } | SHREQ { $$ = mk_atom(yytext); } | MINUSEQ { $$ = mk_atom(yytext); } diff --git a/src/grammar/verify.rs b/src/grammar/verify.rs index b7714a7e3c5dd..fe9c5aaee4ce9 100644 --- a/src/grammar/verify.rs +++ b/src/grammar/verify.rs @@ -35,7 +35,7 @@ use syntax::parse::lexer::TokenAndSpan; fn parse_token_list(file: &str) -> HashMap { fn id() -> token::Token { - token::Ident(ast::Ident::with_empty_ctxt(Name(0))), token::Plain) + token::Ident(ast::Ident::with_empty_ctxt(Name(0)), token::Plain) } let mut res = HashMap::new(); @@ -208,7 +208,7 @@ fn parse_antlr_token(s: &str, tokens: &HashMap, surrogate_ token::Literal(token::ByteStr(..), n) => token::Literal(token::ByteStr(nm), n), token::Literal(token::ByteStrRaw(..), n) => token::Literal(token::ByteStrRaw(fix(content), count(content)), n), - token::Ident(..) => token::Ident(ast::Ident::with_empty_ctxt(nm)), + token::Ident(..) => token::Ident(ast::Ident::with_empty_ctxt(nm), token::ModName), token::Lifetime(..) => token::Lifetime(ast::Ident::with_empty_ctxt(nm)), ref t => t.clone() diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index 8ed4706b1ce9f..7f80763fb6dda 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -369,6 +369,7 @@ impl EarlyLintPass for UnusedParens { ast::ExprRet(Some(ref value)) => (value, "`return` value", false), ast::ExprAssign(_, ref value) => (value, "assigned value", false), ast::ExprAssignOp(_, _, ref value) => (value, "assigned value", false), + ast::ExprInPlace(_, ref value) => (value, "emplacement value", false), _ => return }; self.check_unused_parens_core(cx, &**value, msg, struct_lit_needs_parens); diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 8c3360512d512..f1c88232fc45a 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -242,26 +242,6 @@ pub fn struct_field_visibility(field: ast::StructField) -> Visibility { } } -/// Maps a binary operator to its precedence -pub fn operator_prec(op: ast::BinOp_) -> usize { - match op { - // 'as' sits here with 12 - BiMul | BiDiv | BiRem => 11, - BiAdd | BiSub => 10, - BiShl | BiShr => 9, - BiBitAnd => 8, - BiBitXor => 7, - BiBitOr => 6, - BiLt | BiLe | BiGe | BiGt | BiEq | BiNe => 3, - BiAnd => 2, - BiOr => 1 - } -} - -/// Precedence of the `as` operator, which is a binary operator -/// not appearing in the prior table. -pub const AS_PREC: usize = 12; - pub fn empty_generics() -> Generics { Generics { lifetimes: Vec::new(), diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 9adef08771d06..406b763ca4654 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -66,6 +66,7 @@ pub mod util { #[cfg(test)] pub mod parser_testing; pub mod small_vector; + pub mod parser; } pub mod diagnostics { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index e153f1665e3e6..6cee7b86a6106 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -15,7 +15,7 @@ use ast::BareFnTy; use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier}; use ast::{Public, Unsafety}; use ast::{Mod, BiAdd, Arg, Arm, Attribute, BindByRef, BindByValue}; -use ast::{BiBitAnd, BiBitOr, BiBitXor, BiRem, BiLt, BiGt, Block}; +use ast::{BiBitAnd, BiBitOr, BiBitXor, BiRem, BiLt, Block}; use ast::{BlockCheckMode, CaptureByRef, CaptureByValue, CaptureClause}; use ast::{Constness, ConstImplItem, ConstTraitItem, Crate, CrateConfig}; use ast::{Decl, DeclItem, DeclLocal, DefaultBlock, DefaultReturn}; @@ -50,8 +50,7 @@ use ast::{BiSub, StrStyle}; use ast::{SelfExplicit, SelfRegion, SelfStatic, SelfValue}; use ast::{Delimited, SequenceRepetition, TokenTree, TraitItem, TraitRef}; use ast::{TtDelimited, TtSequence, TtToken}; -use ast::{Ty, Ty_, TypeBinding}; -use ast::{TyMac}; +use ast::{Ty, Ty_, TypeBinding, TyMac}; use ast::{TyFixedLengthVec, TyBareFn, TyTypeof, TyInfer}; use ast::{TyParam, TyParamBound, TyParen, TyPath, TyPolyTraitRef, TyPtr}; use ast::{TyRptr, TyTup, TyU32, TyVec}; @@ -60,7 +59,7 @@ use ast::{UnnamedField, UnsafeBlock}; use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple}; use ast::{Visibility, WhereClause}; use ast; -use ast_util::{self, AS_PREC, ident_to_path, operator_prec}; +use ast_util::{self, ident_to_path}; use codemap::{self, Span, BytePos, Spanned, spanned, mk_sp, CodeMap}; use diagnostic; use ext::tt::macro_parser; @@ -73,6 +72,7 @@ use parse::obsolete::{ParserObsoleteMethods, ObsoleteSyntax}; use parse::token::{self, MatchNt, SubstNt, SpecialVarNt, InternedString}; use parse::token::{keywords, special_idents, SpecialMacroVar}; use parse::{new_sub_parser_from_file, ParseSess}; +use util::parser::{AssocOp, Fixity}; use print::pprust; use ptr::P; use owned_slice::OwnedSlice; @@ -2597,117 +2597,189 @@ impl<'a> Parser<'a> { Ok(tts) } - /// Parse a prefix-operator expr + /// Parse a prefix-unary-operator expr pub fn parse_prefix_expr(&mut self) -> PResult> { let lo = self.span.lo; let hi; - // Note: when adding new unary operators, don't forget to adjust Token::can_begin_expr() - let ex; - match self.token { - token::Not => { - try!(self.bump()); - let e = try!(self.parse_prefix_expr()); - hi = e.span.hi; - ex = self.mk_unary(UnNot, e); - } - token::BinOp(token::Minus) => { - try!(self.bump()); - let e = try!(self.parse_prefix_expr()); - hi = e.span.hi; - ex = self.mk_unary(UnNeg, e); - } - token::BinOp(token::Star) => { - try!(self.bump()); - let e = try!(self.parse_prefix_expr()); - hi = e.span.hi; - ex = self.mk_unary(UnDeref, e); - } - token::BinOp(token::And) | token::AndAnd => { - try!(self.expect_and()); - let m = try!(self.parse_mutability()); - let e = try!(self.parse_prefix_expr()); - hi = e.span.hi; - ex = ExprAddrOf(m, e); - } - token::Ident(..) if self.token.is_keyword(keywords::In) => { - try!(self.bump()); - let place = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL)); - let blk = try!(self.parse_block()); - hi = blk.span.hi; - let blk_expr = self.mk_expr(blk.span.lo, blk.span.hi, ExprBlock(blk)); - ex = ExprInPlace(place, blk_expr); - } - token::Ident(..) if self.token.is_keyword(keywords::Box) => { - try!(self.bump()); - let subexpression = try!(self.parse_prefix_expr()); - hi = subexpression.span.hi; - ex = ExprBox(subexpression); - } - _ => return self.parse_dot_or_call_expr() - } + let ex = match self.token { + token::Not => { + try!(self.bump()); + let e = try!(self.parse_prefix_expr()); + hi = e.span.hi; + self.mk_unary(UnNot, e) + } + token::BinOp(token::Minus) => { + try!(self.bump()); + let e = try!(self.parse_prefix_expr()); + hi = e.span.hi; + self.mk_unary(UnNeg, e) + } + token::BinOp(token::Star) => { + try!(self.bump()); + let e = try!(self.parse_prefix_expr()); + hi = e.span.hi; + self.mk_unary(UnDeref, e) + } + token::BinOp(token::And) | token::AndAnd => { + try!(self.expect_and()); + let m = try!(self.parse_mutability()); + let e = try!(self.parse_prefix_expr()); + hi = e.span.hi; + ExprAddrOf(m, e) + } + token::Ident(..) if self.token.is_keyword(keywords::In) => { + try!(self.bump()); + let place = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL)); + let blk = try!(self.parse_block()); + let span = blk.span; + hi = span.hi; + let blk_expr = self.mk_expr(span.lo, span.hi, ExprBlock(blk)); + ExprInPlace(place, blk_expr) + } + token::Ident(..) if self.token.is_keyword(keywords::Box) => { + try!(self.bump()); + let subexpression = try!(self.parse_prefix_expr()); + hi = subexpression.span.hi; + ExprBox(subexpression) + } + _ => return self.parse_dot_or_call_expr() + }; return Ok(self.mk_expr(lo, hi, ex)); } - /// Parse an expression of binops - pub fn parse_binops(&mut self) -> PResult> { - let prefix_expr = try!(self.parse_prefix_expr()); - self.parse_more_binops(prefix_expr, 0) - } + /// Parse an associative expression + /// + /// This parses an expression accounting for associativity and precedence of the operators in + /// the expression. + pub fn parse_assoc_expr(&mut self) -> PResult> { + self.parse_assoc_expr_with(0, None) + } + + /// Parse an associative expression with operators of at least `min_prec` precedence + pub fn parse_assoc_expr_with(&mut self, + min_prec: usize, + lhs: Option>) + -> PResult> { + let mut lhs = if lhs.is_some() { + lhs.unwrap() + } else if self.token == token::DotDot { + return self.parse_prefix_range_expr(); + } else { + try!(self.parse_prefix_expr()) + }; + if self.expr_is_complete(&*lhs) { + // Semi-statement forms are odd. See https://github.com/rust-lang/rust/issues/29071 + return Ok(lhs); + } + let cur_op_span = self.span; + self.expected_tokens.push(TokenType::Operator); + while let Some(op) = AssocOp::from_token(&self.token) { + let restrictions = if op.is_assign_like() { + self.restrictions & Restrictions::RESTRICTION_NO_STRUCT_LITERAL + } else { + self.restrictions + }; + if op.precedence() < min_prec { + break; + } + try!(self.bump()); + if op.is_comparison() { + self.check_no_chained_comparison(&*lhs, &op); + } + // Special cases: + if op == AssocOp::As { + let rhs = try!(self.parse_ty_nopanic()); + lhs = self.mk_expr(lhs.span.lo, rhs.span.hi, ExprCast(lhs, rhs)); + continue + } else if op == AssocOp::DotDot { + // If we didn’t have to handle `x..`, it would be pretty easy to generalise + // it to the Fixity::None code. + // + // We have 2 alternatives here: `x..y` and `x..` The other two variants are + // handled with `parse_prefix_range_expr` call above. + let rhs = if self.is_at_start_of_range_notation_rhs() { + self.parse_assoc_expr_with(op.precedence() + 1, None).ok() + } else { + None + }; + let (lhs_span, rhs_span) = (lhs.span, if let Some(ref x) = rhs { + x.span + } else { + cur_op_span + }); + let r = self.mk_range(Some(lhs), rhs); + lhs = self.mk_expr(lhs_span.lo, rhs_span.hi, r); + break + } - /// Parse an expression of binops of at least min_prec precedence - pub fn parse_more_binops(&mut self, lhs: P, min_prec: usize) -> PResult> { - if self.expr_is_complete(&*lhs) { return Ok(lhs); } - self.expected_tokens.push(TokenType::Operator); + let rhs = try!(match op.fixity() { + Fixity::Right => self.with_res(restrictions, |this|{ + this.parse_assoc_expr_with(op.precedence(), None) + }), + Fixity::Left => self.with_res(restrictions, |this|{ + this.parse_assoc_expr_with(op.precedence() + 1, None) + }), + // We currently have no non-associative operators that are not handled above by + // the special cases. The code is here only for future convenience. + Fixity::None => self.with_res(restrictions, |this|{ + this.parse_assoc_expr_with(op.precedence() + 1, None) + }), + }); - let cur_op_span = self.span; - let cur_opt = self.token.to_binop(); - match cur_opt { - Some(cur_op) => { - if ast_util::is_comparison_binop(cur_op) { - self.check_no_chained_comparison(&*lhs, cur_op) - } - let cur_prec = operator_prec(cur_op); - if cur_prec >= min_prec { - try!(self.bump()); - let expr = try!(self.parse_prefix_expr()); - let rhs = try!(self.parse_more_binops(expr, cur_prec + 1)); - let lhs_span = lhs.span; - let rhs_span = rhs.span; - let binary = self.mk_binary(codemap::respan(cur_op_span, cur_op), lhs, rhs); - let bin = self.mk_expr(lhs_span.lo, rhs_span.hi, binary); - self.parse_more_binops(bin, min_prec) - } else { - Ok(lhs) + lhs = match op { + AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | + AssocOp::Modulus | AssocOp::LAnd | AssocOp::LOr | AssocOp::BitXor | + AssocOp::BitAnd | AssocOp::BitOr | AssocOp::ShiftLeft | AssocOp::ShiftRight | + AssocOp::Equal | AssocOp::Less | AssocOp::LessEqual | AssocOp::NotEqual | + AssocOp::Greater | AssocOp::GreaterEqual => { + let ast_op = op.to_ast_binop().unwrap(); + let (lhs_span, rhs_span) = (lhs.span, rhs.span); + let binary = self.mk_binary(codemap::respan(cur_op_span, ast_op), lhs, rhs); + self.mk_expr(lhs_span.lo, rhs_span.hi, binary) } - } - None => { - if AS_PREC >= min_prec && try!(self.eat_keyword_noexpect(keywords::As) ){ - let rhs = try!(self.parse_ty_nopanic()); - let _as = self.mk_expr(lhs.span.lo, - rhs.span.hi, - ExprCast(lhs, rhs)); - self.parse_more_binops(_as, min_prec) - } else { - Ok(lhs) + AssocOp::Assign => + self.mk_expr(lhs.span.lo, rhs.span.hi, ExprAssign(lhs, rhs)), + AssocOp::Inplace => + self.mk_expr(lhs.span.lo, rhs.span.hi, ExprInPlace(lhs, rhs)), + AssocOp::AssignOp(k) => { + let aop = match k { + token::Plus => BiAdd, + token::Minus => BiSub, + token::Star => BiMul, + token::Slash => BiDiv, + token::Percent => BiRem, + token::Caret => BiBitXor, + token::And => BiBitAnd, + token::Or => BiBitOr, + token::Shl => BiShl, + token::Shr => BiShr + }; + let (lhs_span, rhs_span) = (lhs.span, rhs.span); + let aopexpr = self.mk_assign_op(codemap::respan(cur_op_span, aop), lhs, rhs); + self.mk_expr(lhs_span.lo, rhs_span.hi, aopexpr) } - } + AssocOp::As | AssocOp::DotDot => self.bug("As or DotDot branch reached") + }; + + if op.fixity() == Fixity::None { break } } + Ok(lhs) } /// Produce an error if comparison operators are chained (RFC #558). /// We only need to check lhs, not rhs, because all comparison ops /// have same precedence and are left-associative - fn check_no_chained_comparison(&mut self, lhs: &Expr, outer_op: ast::BinOp_) { - debug_assert!(ast_util::is_comparison_binop(outer_op)); + fn check_no_chained_comparison(&mut self, lhs: &Expr, outer_op: &AssocOp) { + debug_assert!(outer_op.is_comparison()); match lhs.node { ExprBinary(op, _, _) if ast_util::is_comparison_binop(op.node) => { // respan to include both operators let op_span = mk_sp(op.span.lo, self.span.hi); self.span_err(op_span, "chained comparison operators require parentheses"); - if op.node == BiLt && outer_op == BiGt { + if op.node == BiLt && *outer_op == AssocOp::Greater { self.fileline_help(op_span, "use `::<...>` instead of `<...>` if you meant to specify type arguments"); } @@ -2716,86 +2788,24 @@ impl<'a> Parser<'a> { } } - /// Parse an assignment expression.... - /// actually, this seems to be the main entry point for - /// parsing an arbitrary expression. - pub fn parse_assign_expr(&mut self) -> PResult> { - match self.token { - token::DotDot => { - // prefix-form of range notation '..expr' - // This has the same precedence as assignment expressions - // (much lower than other prefix expressions) to be consistent - // with the postfix-form 'expr..' - let lo = self.span.lo; - let mut hi = self.span.hi; - try!(self.bump()); - let opt_end = if self.is_at_start_of_range_notation_rhs() { - let end = try!(self.parse_binops()); - hi = end.span.hi; - Some(end) - } else { - None - }; - let ex = self.mk_range(None, opt_end); - Ok(self.mk_expr(lo, hi, ex)) - } - _ => { - let lhs = try!(self.parse_binops()); - self.parse_assign_expr_with(lhs) - } - } - } - - pub fn parse_assign_expr_with(&mut self, lhs: P) -> PResult> { - let restrictions = self.restrictions & Restrictions::RESTRICTION_NO_STRUCT_LITERAL; - let op_span = self.span; - match self.token { - token::Eq => { - try!(self.bump()); - let rhs = try!(self.parse_expr_res(restrictions)); - Ok(self.mk_expr(lhs.span.lo, rhs.span.hi, ExprAssign(lhs, rhs))) - } - token::BinOpEq(op) => { - try!(self.bump()); - let rhs = try!(self.parse_expr_res(restrictions)); - let aop = match op { - token::Plus => BiAdd, - token::Minus => BiSub, - token::Star => BiMul, - token::Slash => BiDiv, - token::Percent => BiRem, - token::Caret => BiBitXor, - token::And => BiBitAnd, - token::Or => BiBitOr, - token::Shl => BiShl, - token::Shr => BiShr - }; - let rhs_span = rhs.span; - let span = lhs.span; - let assign_op = self.mk_assign_op(codemap::respan(op_span, aop), lhs, rhs); - Ok(self.mk_expr(span.lo, rhs_span.hi, assign_op)) - } - // A range expression, either `expr..expr` or `expr..`. - token::DotDot => { - let lo = lhs.span.lo; - let mut hi = self.span.hi; - try!(self.bump()); - - let opt_end = if self.is_at_start_of_range_notation_rhs() { - let end = try!(self.parse_binops()); - hi = end.span.hi; - Some(end) - } else { - None - }; - let range = self.mk_range(Some(lhs), opt_end); - return Ok(self.mk_expr(lo, hi, range)); - } - - _ => { - Ok(lhs) - } - } + /// Parse prefix-forms of range notation: `..expr` and `..` + fn parse_prefix_range_expr(&mut self) -> PResult> { + debug_assert!(self.token == token::DotDot); + let lo = self.span.lo; + let mut hi = self.span.hi; + try!(self.bump()); + let opt_end = if self.is_at_start_of_range_notation_rhs() { + // RHS must be parsed with more associativity than DotDot. + let next_prec = AssocOp::from_token(&token::DotDot).unwrap().precedence() + 1; + Some(try!(self.parse_assoc_expr_with(next_prec, None).map(|x|{ + hi = x.span.hi; + x + }))) + } else { + None + }; + let r = self.mk_range(None, opt_end); + Ok(self.mk_expr(lo, hi, r)) } fn is_at_start_of_range_notation_rhs(&self) -> bool { @@ -2978,13 +2988,22 @@ impl<'a> Parser<'a> { self.parse_expr_res(Restrictions::empty()) } - /// Parse an expression, subject to the given restrictions - pub fn parse_expr_res(&mut self, r: Restrictions) -> PResult> { + /// Evaluate the closure with restrictions in place. + /// + /// After the closure is evaluated, restrictions are reset. + pub fn with_res(&mut self, r: Restrictions, f: F) -> PResult> + where F: FnOnce(&mut Self) -> PResult> { let old = self.restrictions; self.restrictions = r; - let e = try!(self.parse_assign_expr()); + let r = f(self); self.restrictions = old; - return Ok(e); + return r; + + } + + /// Parse an expression, subject to the given restrictions + pub fn parse_expr_res(&mut self, r: Restrictions) -> PResult> { + self.with_res(r, |this| this.parse_assoc_expr()) } /// Parse the RHS of a local variable declaration (e.g. '= 14;') @@ -3624,8 +3643,7 @@ impl<'a> Parser<'a> { let e = self.mk_mac_expr(span.lo, span.hi, mac.and_then(|m| m.node)); let e = try!(self.parse_dot_or_call_expr_with(e)); - let e = try!(self.parse_more_binops(e, 0)); - let e = try!(self.parse_assign_expr_with(e)); + let e = try!(self.parse_assoc_expr_with(0, Some(e))); try!(self.handle_expression_like_statement( e, span, diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 87cecdba28e3d..0bdbf132cb8d2 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -14,6 +14,7 @@ use abi; use ast; use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier}; use ast_util; +use util::parser::AssocOp; use attr; use owned_slice::OwnedSlice; use attr::{AttrMetaMethods, AttributeMethods}; @@ -445,7 +446,8 @@ fn needs_parentheses(expr: &ast::Expr) -> bool { match expr.node { ast::ExprAssign(..) | ast::ExprBinary(..) | ast::ExprClosure(..) | - ast::ExprAssignOp(..) | ast::ExprCast(..) => true, + ast::ExprAssignOp(..) | ast::ExprCast(..) | + ast::ExprInPlace(..) => true, _ => false, } } @@ -1776,8 +1778,8 @@ impl<'a> State<'a> { binop: ast::BinOp) -> bool { match sub_expr.node { ast::ExprBinary(ref sub_op, _, _) => { - if ast_util::operator_prec(sub_op.node) < - ast_util::operator_prec(binop.node) { + if AssocOp::from_ast_binop(sub_op.node).precedence() < + AssocOp::from_ast_binop(binop.node).precedence() { true } else { false @@ -1802,10 +1804,10 @@ impl<'a> State<'a> { fn print_expr_in_place(&mut self, place: &ast::Expr, expr: &ast::Expr) -> io::Result<()> { - try!(self.word_space("in")); - try!(self.print_expr(place)); + try!(self.print_expr_maybe_paren(place)); try!(space(&mut self.s)); - self.print_expr(expr) + try!(self.word_space("<-")); + self.print_expr_maybe_paren(expr) } fn print_expr_vec(&mut self, exprs: &[P]) -> io::Result<()> { diff --git a/src/libsyntax/util/parser.rs b/src/libsyntax/util/parser.rs new file mode 100644 index 0000000000000..bf3a8def39011 --- /dev/null +++ b/src/libsyntax/util/parser.rs @@ -0,0 +1,210 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +use parse::token::{Token, BinOpToken, keywords}; +use ast; + +/// Associative operator with precedence. +/// +/// This is the enum which specifies operator precedence and fixity to the parser. +#[derive(Debug, PartialEq, Eq)] +pub enum AssocOp { + /// `+` + Add, + /// `-` + Subtract, + /// `*` + Multiply, + /// `/` + Divide, + /// `%` + Modulus, + /// `&&` + LAnd, + /// `||` + LOr, + /// `^` + BitXor, + /// `&` + BitAnd, + /// `|` + BitOr, + /// `<<` + ShiftLeft, + /// `>>` + ShiftRight, + /// `==` + Equal, + /// `<` + Less, + /// `<=` + LessEqual, + /// `!=` + NotEqual, + /// `>` + Greater, + /// `>=` + GreaterEqual, + /// `=` + Assign, + /// `<-` + Inplace, + /// `?=` where ? is one of the BinOpToken + AssignOp(BinOpToken), + /// `as` + As, + /// `..` range + DotDot +} + +#[derive(Debug, PartialEq, Eq)] +pub enum Fixity { + /// The operator is left-associative + Left, + /// The operator is right-associative + Right, + /// The operator is not associative + None +} + +impl AssocOp { + /// Create a new AssocOP from a token + pub fn from_token(t: &Token) -> Option { + use self::AssocOp::*; + match *t { + Token::BinOpEq(k) => Some(AssignOp(k)), + Token::LArrow => Some(Inplace), + Token::Eq => Some(Assign), + Token::BinOp(BinOpToken::Star) => Some(Multiply), + Token::BinOp(BinOpToken::Slash) => Some(Divide), + Token::BinOp(BinOpToken::Percent) => Some(Modulus), + Token::BinOp(BinOpToken::Plus) => Some(Add), + Token::BinOp(BinOpToken::Minus) => Some(Subtract), + Token::BinOp(BinOpToken::Shl) => Some(ShiftLeft), + Token::BinOp(BinOpToken::Shr) => Some(ShiftRight), + Token::BinOp(BinOpToken::And) => Some(BitAnd), + Token::BinOp(BinOpToken::Caret) => Some(BitXor), + Token::BinOp(BinOpToken::Or) => Some(BitOr), + Token::Lt => Some(Less), + Token::Le => Some(LessEqual), + Token::Ge => Some(GreaterEqual), + Token::Gt => Some(Greater), + Token::EqEq => Some(Equal), + Token::Ne => Some(NotEqual), + Token::AndAnd => Some(LAnd), + Token::OrOr => Some(LOr), + Token::DotDot => Some(DotDot), + _ if t.is_keyword(keywords::As) => Some(As), + _ => None + } + } + + /// Create a new AssocOp from ast::BinOp_. + pub fn from_ast_binop(op: ast::BinOp_) -> Self { + use self::AssocOp::*; + match op { + ast::BiLt => Less, + ast::BiGt => Greater, + ast::BiLe => LessEqual, + ast::BiGe => GreaterEqual, + ast::BiEq => Equal, + ast::BiNe => NotEqual, + ast::BiMul => Multiply, + ast::BiDiv => Divide, + ast::BiRem => Modulus, + ast::BiAdd => Add, + ast::BiSub => Subtract, + ast::BiShl => ShiftLeft, + ast::BiShr => ShiftRight, + ast::BiBitAnd => BitAnd, + ast::BiBitXor => BitXor, + ast::BiBitOr => BitOr, + ast::BiAnd => LAnd, + ast::BiOr => LOr + } + } + + /// Gets the precedence of this operator + pub fn precedence(&self) -> usize { + use self::AssocOp::*; + match *self { + As => 14, + Multiply | Divide | Modulus => 13, + Add | Subtract => 12, + ShiftLeft | ShiftRight => 11, + BitAnd => 10, + BitXor => 9, + BitOr => 8, + Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7, + LAnd => 6, + LOr => 5, + DotDot => 4, + Inplace => 3, + Assign | AssignOp(_) => 2, + } + } + + /// Gets the fixity of this operator + pub fn fixity(&self) -> Fixity { + use self::AssocOp::*; + // NOTE: it is a bug to have an operators that has same precedence but different fixities! + match *self { + Inplace | Assign | AssignOp(_) => Fixity::Right, + As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | + BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | + LAnd | LOr => Fixity::Left, + DotDot => Fixity::None + } + } + + pub fn is_comparison(&self) -> bool { + use self::AssocOp::*; + match *self { + Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true, + Inplace | Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract | + ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot => false + } + } + + pub fn is_assign_like(&self) -> bool { + use self::AssocOp::*; + match *self { + Assign | AssignOp(_) | Inplace => true, + Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply | Divide | + Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | + LOr | DotDot => false + } + } + + pub fn to_ast_binop(&self) -> Option { + use self::AssocOp::*; + match *self { + Less => Some(ast::BiLt), + Greater => Some(ast::BiGt), + LessEqual => Some(ast::BiLe), + GreaterEqual => Some(ast::BiGe), + Equal => Some(ast::BiEq), + NotEqual => Some(ast::BiNe), + Multiply => Some(ast::BiMul), + Divide => Some(ast::BiDiv), + Modulus => Some(ast::BiRem), + Add => Some(ast::BiAdd), + Subtract => Some(ast::BiSub), + ShiftLeft => Some(ast::BiShl), + ShiftRight => Some(ast::BiShr), + BitAnd => Some(ast::BiBitAnd), + BitXor => Some(ast::BiBitXor), + BitOr => Some(ast::BiBitOr), + LAnd => Some(ast::BiAnd), + LOr => Some(ast::BiOr), + Inplace | Assign | AssignOp(_) | As | DotDot => None + } + } + +} diff --git a/src/test/compile-fail/feature-gate-placement-expr.rs b/src/test/compile-fail/feature-gate-placement-expr.rs index 47a25bf637c5f..080364c08554f 100644 --- a/src/test/compile-fail/feature-gate-placement-expr.rs +++ b/src/test/compile-fail/feature-gate-placement-expr.rs @@ -19,6 +19,6 @@ fn main() { use std::boxed::HEAP; - let x = in HEAP { 'c' }; //~ ERROR placement-in expression syntax is experimental + let x = HEAP <- 'c'; //~ ERROR placement-in expression syntax is experimental println!("x: {}", x); } diff --git a/src/test/compile-fail/issue-14084.rs b/src/test/compile-fail/issue-14084.rs index 6b19cb0b68f10..8cbec549dda96 100644 --- a/src/test/compile-fail/issue-14084.rs +++ b/src/test/compile-fail/issue-14084.rs @@ -12,6 +12,6 @@ #![feature(placement_in_syntax)] fn main() { - in () { 0 }; + () <- 0; //~^ ERROR: the trait `core::ops::Placer<_>` is not implemented } diff --git a/src/test/compile-fail/placement-expr-unsafe.rs b/src/test/compile-fail/placement-expr-unsafe.rs index 50a840e6c9b7f..bf6f4c52f1f9c 100644 --- a/src/test/compile-fail/placement-expr-unsafe.rs +++ b/src/test/compile-fail/placement-expr-unsafe.rs @@ -17,8 +17,8 @@ fn main() { use std::boxed::HEAP; let p: *const i32 = &42; - let _ = in HEAP { *p }; //~ ERROR requires unsafe + let _ = HEAP <- *p; //~ ERROR requires unsafe let p: *const _ = &HEAP; - let _ = in *p { 42 }; //~ ERROR requires unsafe + let _ = *p <- 42; //~ ERROR requires unsafe } diff --git a/src/test/compile-fail/placement-expr-unstable.rs b/src/test/compile-fail/placement-expr-unstable.rs index d981b71a8132d..23fc8e7a07e23 100644 --- a/src/test/compile-fail/placement-expr-unstable.rs +++ b/src/test/compile-fail/placement-expr-unstable.rs @@ -18,7 +18,7 @@ extern crate core; fn main() { use std::boxed::HEAP; //~ ERROR use of unstable library feature - let _ = in HEAP { //~ ERROR use of unstable library feature + let _ = HEAP <- { //~ ERROR use of unstable library feature ::core::raw::Slice { //~ ERROR use of unstable library feature data: &42, //~ ERROR use of unstable library feature len: 1 //~ ERROR use of unstable library feature diff --git a/src/test/parse-fail/assoc-oddities-1.rs b/src/test/parse-fail/assoc-oddities-1.rs new file mode 100644 index 0000000000000..5c0c47de58aed --- /dev/null +++ b/src/test/parse-fail/assoc-oddities-1.rs @@ -0,0 +1,22 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z parse-only + +fn that_odd_parse() { + // following lines below parse and must not fail + x = if c { a } else { b }(); + x <- if c { a } else { b }[n]; + x = if true { 1 } else { 0 } as *mut _; + // however this does not parse and probably should fail to retain compat? + // NB: `..` here is arbitrary, failure happens/should happen ∀ops that aren’t `=` or `<-` + // see assoc-oddities-2 and assoc-oddities-3 + ..if c { a } else { b }[n]; //~ ERROR expected one of +} diff --git a/src/test/parse-fail/removed-syntax-larrow-init.rs b/src/test/parse-fail/assoc-oddities-2.rs similarity index 69% rename from src/test/parse-fail/removed-syntax-larrow-init.rs rename to src/test/parse-fail/assoc-oddities-2.rs index 1388f8745df57..5679328edc998 100644 --- a/src/test/parse-fail/removed-syntax-larrow-init.rs +++ b/src/test/parse-fail/assoc-oddities-2.rs @@ -1,4 +1,4 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -10,8 +10,7 @@ // compile-flags: -Z parse-only -fn removed_moves() { - let mut x = 0; - let y <- x; - //~^ ERROR expected one of `:`, `;`, `=`, or `@`, found `<-` +fn that_odd_parse() { + // see assoc-oddities-1 for explanation + x..if c { a } else { b }[n]; //~ ERROR expected one of } diff --git a/src/test/parse-fail/removed-syntax-larrow-move.rs b/src/test/parse-fail/assoc-oddities-3.rs similarity index 66% rename from src/test/parse-fail/removed-syntax-larrow-move.rs rename to src/test/parse-fail/assoc-oddities-3.rs index 0736e483a4979..0d4f21f0dca26 100644 --- a/src/test/parse-fail/removed-syntax-larrow-move.rs +++ b/src/test/parse-fail/assoc-oddities-3.rs @@ -1,4 +1,4 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -10,9 +10,7 @@ // compile-flags: -Z parse-only -fn removed_moves() { - let mut x = 0; - let y = 0; - y <- x; - //~^ ERROR expected one of `!`, `.`, `::`, `;`, `{`, `}`, or an operator, found `<-` +fn that_odd_parse() { + // see assoc-oddities-1 for explanation + x + if c { a } else { b }[n]; //~ ERROR expected one of } diff --git a/src/test/run-pass/issue-29071-2.rs b/src/test/run-pass/issue-29071-2.rs new file mode 100644 index 0000000000000..8e69c063f99a0 --- /dev/null +++ b/src/test/run-pass/issue-29071-2.rs @@ -0,0 +1,40 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn t1() -> u32 { + let x; + x = if true { [1, 2, 3] } else { [2, 3, 4] }[0]; + x +} + +fn t2() -> [u32; 1] { + if true { [1, 2, 3]; } else { [2, 3, 4]; } + [0] +} + +fn t3() -> u32 { + let x; + x = if true { i1 as F } else { i2 as F }(); + x +} + +fn t4() -> () { + if true { i1 as F; } else { i2 as F; } + () +} + +type F = fn() -> u32; +fn i1() -> u32 { 1 } +fn i2() -> u32 { 2 } + +fn main() { + assert_eq!(t1(), 1); + assert_eq!(t3(), 1); +} diff --git a/src/test/run-pass/issue-29071.rs b/src/test/run-pass/issue-29071.rs new file mode 100644 index 0000000000000..09c17a85ab50b --- /dev/null +++ b/src/test/run-pass/issue-29071.rs @@ -0,0 +1,21 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +fn ret() -> u32 { + static x: u32 = 10; + x & if true { 10u32 } else { 20u32 } & x +} + +fn ret2() -> &'static u32 { + static x: u32 = 10; + if true { 10u32; } else { 20u32; } + &x +} + +fn main() {}