diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs index 33a85c6c77026..217cd22d20c4c 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -1494,14 +1494,15 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> { ); } - fn smart_resolve_path_fragment(&mut self, - id: NodeId, - qself: Option<&QSelf>, - path: &[Segment], - span: Span, - source: PathSource<'_>, - crate_lint: CrateLint) - -> PartialRes { + fn smart_resolve_path_fragment( + &mut self, + id: NodeId, + qself: Option<&QSelf>, + path: &[Segment], + span: Span, + source: PathSource<'_>, + crate_lint: CrateLint, + ) -> PartialRes { let ns = source.namespace(); let is_expected = &|res| source.is_expected(res); @@ -1620,9 +1621,10 @@ impl<'a, 'b> LateResolutionVisitor<'a, '_> { match self.resolve_qpath(id, qself, path, ns, span, crate_lint) { // If defer_to_typeck, then resolution > no resolution, // otherwise full resolution > partial resolution > no resolution. - Some(partial_res) if partial_res.unresolved_segments() == 0 || - defer_to_typeck => - return Some(partial_res), + Some(partial_res) + if partial_res.unresolved_segments() == 0 || defer_to_typeck => { + return Some(partial_res) + } partial_res => if fin_res.is_none() { fin_res = partial_res }, } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 2fb6f197dad7c..e831333cfae1b 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -38,8 +38,9 @@ use std::path::PathBuf; bitflags::bitflags! { struct Restrictions: u8 { - const STMT_EXPR = 1 << 0; - const NO_STRUCT_LITERAL = 1 << 1; + const STMT_EXPR = 1 << 0; + const NO_STRUCT_LITERAL = 1 << 1; + const CLOSING_ANGLE_BRACKET = 1 << 2; } } diff --git a/src/libsyntax/parse/parser/expr.rs b/src/libsyntax/parse/parser/expr.rs index 23674ad589dc5..44503dda7b96a 100644 --- a/src/libsyntax/parse/parser/expr.rs +++ b/src/libsyntax/parse/parser/expr.rs @@ -186,6 +186,15 @@ impl<'a> Parser<'a> { self.last_type_ascription = None; return Ok(lhs); } + (true, Some(AssocOp::Greater)) | + (true, Some(AssocOp::Less)) | + (true, Some(AssocOp::ShiftLeft)) | + (true, Some(AssocOp::ShiftRight)) + if self.restrictions.contains(Restrictions::CLOSING_ANGLE_BRACKET) => { + // Bare `const` argument expression. + self.last_type_ascription = None; + return Ok(lhs); + } (true, Some(_)) => { // We've found an expression that would be parsed as a statement, but the next // token implies this should be parsed as an expression. @@ -205,6 +214,15 @@ impl<'a> Parser<'a> { } self.expected_tokens.push(TokenType::Operator); while let Some(op) = AssocOp::from_token(&self.token) { + if let (true, AssocOp::Greater) | + (true, AssocOp::Less) | + (true, AssocOp::ShiftLeft) | + (true, AssocOp::ShiftRight) = ( + self.restrictions.contains(Restrictions::CLOSING_ANGLE_BRACKET), &op + ) { + // Bare `const` argument expression fully parsed. + return Ok(lhs); + } // Adjust the span for interpolated LHS to point to the `$lhs` token and not to what // it refers to. Interpolated identifiers are unwrapped early and never show up here @@ -341,8 +359,20 @@ impl<'a> Parser<'a> { /// Checks if this expression is a successfully parsed statement. fn expr_is_complete(&self, e: &Expr) -> bool { - self.restrictions.contains(Restrictions::STMT_EXPR) && + ( + self.restrictions.contains(Restrictions::STMT_EXPR) && !classify::expr_requires_semi_to_be_stmt(e) + ) || ( + self.restrictions.contains(Restrictions::CLOSING_ANGLE_BRACKET) && + // Comparison and bitshift operators are not allowed in bare const expressions. + [ + token::Lt, + token::Gt, + token::Comma, + token::BinOp(token::Shl), + token::BinOp(token::Shr), + ].contains(&self.token.kind) + ) } fn is_at_start_of_range_notation_rhs(&self) -> bool { diff --git a/src/libsyntax/parse/parser/path.rs b/src/libsyntax/parse/parser/path.rs index 463ae9124ca23..018e4d11db373 100644 --- a/src/libsyntax/parse/parser/path.rs +++ b/src/libsyntax/parse/parser/path.rs @@ -1,8 +1,10 @@ -use super::{Parser, PResult, TokenType}; +use super::{Parser, PResult, Restrictions, TokenType}; use crate::{maybe_whole, ThinVec}; -use crate::ast::{self, QSelf, Path, PathSegment, Ident, ParenthesizedArgs, AngleBracketedArgs}; -use crate::ast::{AnonConst, GenericArg, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode}; +use crate::ast::{ + self, AngleBracketedArgs, AnonConst, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode, + GenericArg, Ident, ParenthesizedArgs, Path, PathSegment, QSelf, +}; use crate::parse::token::{self, Token}; use crate::source_map::{Span, BytePos}; use crate::symbol::kw; @@ -321,7 +323,7 @@ impl<'a> Parser<'a> { }; debug!("parse_generic_args_with_leading_angle_bracket_recovery: (snapshotting)"); - match self.parse_generic_args() { + match self.parse_generic_args(style) { Ok(value) => Ok(value), Err(ref mut e) if is_first_invocation && self.unmatched_angle_bracket_count > 0 => { // Cancel error from being unable to find `>`. We know the error @@ -368,7 +370,7 @@ impl<'a> Parser<'a> { .emit(); // Try again without unmatched angle bracket characters. - self.parse_generic_args() + self.parse_generic_args(style) }, Err(e) => Err(e), } @@ -376,7 +378,10 @@ impl<'a> Parser<'a> { /// Parses (possibly empty) list of lifetime and type arguments and associated type bindings, /// possibly including trailing comma. - fn parse_generic_args(&mut self) -> PResult<'a, (Vec, Vec)> { + fn parse_generic_args( + &mut self, + style: PathStyle, + ) -> PResult<'a, (Vec, Vec)> { let mut args = Vec::new(); let mut constraints = Vec::new(); let mut misplaced_assoc_ty_constraints: Vec = Vec::new(); @@ -413,34 +418,13 @@ impl<'a> Parser<'a> { span, }); assoc_ty_constraints.push(span); + } else if self.check_possible_const_needing_braces(style) { + args.push(self.parse_const_or_type_arg()?); + misplaced_assoc_ty_constraints.append(&mut assoc_ty_constraints); } else if self.check_const_arg() { - // Parse const argument. - let expr = if let token::OpenDelim(token::Brace) = self.token.kind { - self.parse_block_expr( - None, self.token.span, BlockCheckMode::Default, ThinVec::new() - )? - } else if self.token.is_ident() { - // FIXME(const_generics): to distinguish between idents for types and consts, - // we should introduce a GenericArg::Ident in the AST and distinguish when - // lowering to the HIR. For now, idents for const args are not permitted. - if self.token.is_bool_lit() { - self.parse_literal_maybe_minus()? - } else { - return Err( - self.fatal("identifiers may currently not be used for const generics") - ); - } - } else { - self.parse_literal_maybe_minus()? - }; - let value = AnonConst { - id: ast::DUMMY_NODE_ID, - value: expr, - }; - args.push(GenericArg::Const(value)); + args.push(self.parse_const_arg()?); misplaced_assoc_ty_constraints.append(&mut assoc_ty_constraints); } else if self.check_type() { - // Parse type argument. args.push(GenericArg::Type(self.parse_ty()?)); misplaced_assoc_ty_constraints.append(&mut assoc_ty_constraints); } else { @@ -471,4 +455,87 @@ impl<'a> Parser<'a> { Ok((args, constraints)) } + + fn parse_const_arg(&mut self) -> PResult<'a, GenericArg> { + let value = if let token::OpenDelim(token::Brace) = self.token.kind { + // Parse `const` argument surrounded by braces. + self.parse_block_expr(None, self.token.span, BlockCheckMode::Default, ThinVec::new())? + } else { + self.parse_expr_res(Restrictions::CLOSING_ANGLE_BRACKET, None)? + }; + let value = AnonConst { id: ast::DUMMY_NODE_ID, value }; + Ok(GenericArg::Const(value)) + } + + /// Check some ambiguous cases between type and non-block const arguments. + fn check_possible_const_needing_braces(&mut self, style: PathStyle) -> bool { + style == PathStyle::Expr && ( + self.token.kind == token::Not || // `foo::()` + self.token.kind == token::OpenDelim(token::Paren) // `foo::<(1, 2, 3)>()` + ) + } + + /// There's intent ambiguity for this argument, it could be a const expression missing braces, + /// or it could be a type argument. We try the later as the grammar expects, and if it fails we + /// attempt the former and emit a targetted suggestion if valid. + fn parse_const_or_type_arg(&mut self) -> PResult<'a, GenericArg> { + let mut snapshot = self.clone(); + match self.parse_ty() { + // Nothing to do, this was indeed a type argument. + Ok(ty) if [ + token::Comma, + token::Gt, + token::BinOp(token::Shr), + ].contains(&self.token.kind) => Ok(GenericArg::Type(ty)), + Ok(ty) => { // Could have found `foo::()`, needs `foo::<{ !false }>()`. + mem::swap(self, &mut snapshot); + match self.parse_bad_const_arg() { + Ok(arg) => Ok(arg), + Err(mut err) => { + mem::swap(self, &mut snapshot); + err.cancel(); + Ok(GenericArg::Type(ty)) + } + } + } + Err(mut ty_err) => { + mem::swap(self, &mut snapshot); + match self.parse_bad_const_arg() { + Ok(arg) => { + ty_err.cancel(); + Ok(arg) + } + Err(mut err) => { + mem::swap(self, &mut snapshot); + err.cancel(); + Err(ty_err) + } + } + } + } + } + + fn parse_bad_const_arg(&mut self) -> PResult<'a, GenericArg> { + let msg = if self.token.kind == token::OpenDelim(token::Paren) { + // `foo::<(1, 2, 3)>()` + "tuples in const arguments must be surrounded by braces" + } else { + "complex const arguments must be surrounded by braces" + }; + match self.parse_const_arg() { + Ok(arg) => { + self.span_fatal(arg.span(), msg) + .multipart_suggestion( + "surround this const argument in braces", + vec![ + (arg.span().shrink_to_lo(), "{ ".to_string()), + (arg.span().shrink_to_hi(), " }".to_string()), + ], + Applicability::MachineApplicable, + ).emit(); + Ok(arg) + } + Err(err) => Err(err), + } + } } diff --git a/src/libsyntax/parse/parser/ty.rs b/src/libsyntax/parse/parser/ty.rs index c52d3733b5e0a..137763091891d 100644 --- a/src/libsyntax/parse/parser/ty.rs +++ b/src/libsyntax/parse/parser/ty.rs @@ -48,8 +48,12 @@ impl<'a> Parser<'a> { } } - pub(super) fn parse_ty_common(&mut self, allow_plus: bool, allow_qpath_recovery: bool, - allow_c_variadic: bool) -> PResult<'a, P> { + pub(super) fn parse_ty_common( + &mut self, + allow_plus: bool, + allow_qpath_recovery: bool, + allow_c_variadic: bool, + ) -> PResult<'a, P> { maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); maybe_whole!(self, NtTy, |x| x); diff --git a/src/test/ui/const-generics/const-expression-parameter.rs b/src/test/ui/const-generics/const-expression-parameter.rs index 22c6c35162281..843db56c076e1 100644 --- a/src/test/ui/const-generics/const-expression-parameter.rs +++ b/src/test/ui/const-generics/const-expression-parameter.rs @@ -1,22 +1,70 @@ #![feature(const_generics)] //~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash -fn i32_identity() -> i32 { - 5 +const fn const_i32() -> i32 { + 42 } -fn foo_a() { - i32_identity::<-1>(); // ok +const fn const_bool() -> bool { + true } +const X: i32 = 1; -fn foo_b() { - i32_identity::<1 + 2>(); //~ ERROR expected one of `,` or `>`, found `+` +fn foo() -> i32 { 5 } + +fn baz() { } + +fn bar() {} + +fn bat() {} + +fn main() { + foo::<-1>(); // ok + foo::<1 + 2>(); // ok + foo::< -1 >(); // ok + foo::<1 + 2, 3 + 4>(); //~ ERROR wrong number of const arguments: expected 1, found 2 + foo::<5>(); // ok + foo::< const_i32() >(); //~ ERROR expected type, found function `const_i32` + //~^ ERROR wrong number of const arguments: expected 1, found 0 + //~| ERROR wrong number of type arguments: expected 0, found 1 + foo::< X >(); //~ ERROR expected type, found constant `X` + //~^ ERROR wrong number of const arguments: expected 1, found 0 + //~| ERROR wrong number of type arguments: expected 0, found 1 + foo::<{ X }>(); // ok + foo::< 42 + X >(); // ok + foo::<{ const_i32() }>(); // ok + + baz::<-1, -2>(); // ok + baz::<1 + 2, 3 + 4>(); // ok + baz::< -1 , 2 >(); // ok + baz::< -1 , "2" >(); //~ ERROR mismatched types + + bat::<(1, 2, 3)>(); //~ ERROR tuples in const arguments must be surrounded by braces + bat::<(1, 2)>(); + //~^ ERROR tuples in const arguments must be surrounded by braces + //~| ERROR mismatched types + + bar::(); // ok + bar::(); //~ ERROR complex const arguments must be surrounded by braces + bar::<{ const_bool() }>(); // ok + bar::< const_bool() >(); //~ ERROR expected type, found function `const_bool` + //~^ ERROR wrong number of const arguments: expected 1, found 0 + //~| ERROR wrong number of type arguments: expected 0, found 1 + bar::<{ !const_bool() }>(); // ok + bar::< !const_bool() >(); //~ ERROR complex const arguments must be surrounded by braces + + foo::()>(); //~ ERROR expected one of `!`, `+`, `,`, `::`, or `>`, found `(` } -fn foo_c() { - i32_identity::< -1 >(); // ok +fn parse_err_1() { + bar::< 3 < 4 >(); //~ ERROR expected one of `,`, `.`, `>`, or `?`, found `<` } -fn main() { - i32_identity::<5>(); // ok +fn parse_err_2() { + foo::< const_i32() + 42 >(); + //~^ ERROR expected one of `!`, `(`, `,`, `>`, `?`, `for`, lifetime, or path, found `42` +} +fn parse_err_3() { + foo::< X + 42 >(); + //~^ ERROR expected one of `!`, `(`, `,`, `>`, `?`, `for`, lifetime, or path, found `42` } diff --git a/src/test/ui/const-generics/const-expression-parameter.stderr b/src/test/ui/const-generics/const-expression-parameter.stderr index 7311e27c289f7..abc52fcdf1ea6 100644 --- a/src/test/ui/const-generics/const-expression-parameter.stderr +++ b/src/test/ui/const-generics/const-expression-parameter.stderr @@ -1,8 +1,84 @@ -error: expected one of `,` or `>`, found `+` - --> $DIR/const-expression-parameter.rs:13:22 +error: tuples in const arguments must be surrounded by braces + --> $DIR/const-expression-parameter.rs:42:11 | -LL | i32_identity::<1 + 2>(); - | ^ expected one of `,` or `>` here +LL | bat::<(1, 2, 3)>(); + | ^^^^^^^^^ +help: surround this const argument in braces + | +LL | bat::<{ (1, 2, 3) }>(); + | ^ ^ + +error: tuples in const arguments must be surrounded by braces + --> $DIR/const-expression-parameter.rs:43:11 + | +LL | bat::<(1, 2)>(); + | ^^^^^^ +help: surround this const argument in braces + | +LL | bat::<{ (1, 2) }>(); + | ^ ^ + +error: complex const arguments must be surrounded by braces + --> $DIR/const-expression-parameter.rs:48:11 + | +LL | bar::(); + | ^^^^^^ +help: surround this const argument in braces + | +LL | bar::<{ !false }>(); + | ^ ^ + +error: complex const arguments must be surrounded by braces + --> $DIR/const-expression-parameter.rs:54:12 + | +LL | bar::< !const_bool() >(); + | ^^^^^^^^^^^^^ +help: surround this const argument in braces + | +LL | bar::< { !const_bool() } >(); + | ^ ^ + +error: expected one of `!`, `+`, `,`, `::`, or `>`, found `(` + --> $DIR/const-expression-parameter.rs:56:20 + | +LL | foo::()>(); + | ^ expected one of `!`, `+`, `,`, `::`, or `>` here + +error: expected one of `,`, `.`, `>`, or `?`, found `<` + --> $DIR/const-expression-parameter.rs:60:14 + | +LL | bar::< 3 < 4 >(); + | ^ expected one of `,`, `.`, `>`, or `?` here + +error: expected one of `!`, `(`, `,`, `>`, `?`, `for`, lifetime, or path, found `42` + --> $DIR/const-expression-parameter.rs:64:26 + | +LL | foo::< const_i32() + 42 >(); + | ^^ expected one of 8 possible tokens here + +error: expected one of `!`, `(`, `,`, `>`, `?`, `for`, lifetime, or path, found `42` + --> $DIR/const-expression-parameter.rs:68:16 + | +LL | foo::< X + 42 >(); + | ^^ expected one of 8 possible tokens here + +error[E0573]: expected type, found function `const_i32` + --> $DIR/const-expression-parameter.rs:27:12 + | +LL | foo::< const_i32() >(); + | ^^^^^^^^^^^ not a type + +error[E0573]: expected type, found constant `X` + --> $DIR/const-expression-parameter.rs:30:12 + | +LL | foo::< X >(); + | ^ not a type + +error[E0573]: expected type, found function `const_bool` + --> $DIR/const-expression-parameter.rs:50:12 + | +LL | bar::< const_bool() >(); + | ^^^^^^^^^^^^ not a type warning: the feature `const_generics` is incomplete and may cause the compiler to crash --> $DIR/const-expression-parameter.rs:1:12 @@ -12,5 +88,67 @@ LL | #![feature(const_generics)] | = note: `#[warn(incomplete_features)]` on by default -error: aborting due to previous error +error[E0107]: wrong number of const arguments: expected 1, found 2 + --> $DIR/const-expression-parameter.rs:25:18 + | +LL | foo::<1 + 2, 3 + 4>(); + | ^^^^^ unexpected const argument + +error[E0107]: wrong number of const arguments: expected 1, found 0 + --> $DIR/const-expression-parameter.rs:27:5 + | +LL | foo::< const_i32() >(); + | ^^^^^^^^^^^^^^^^^^^^ expected 1 const argument + +error[E0107]: wrong number of type arguments: expected 0, found 1 + --> $DIR/const-expression-parameter.rs:27:12 + | +LL | foo::< const_i32() >(); + | ^^^^^^^^^^^ unexpected type argument + +error[E0107]: wrong number of const arguments: expected 1, found 0 + --> $DIR/const-expression-parameter.rs:30:5 + | +LL | foo::< X >(); + | ^^^^^^^^^^ expected 1 const argument + +error[E0107]: wrong number of type arguments: expected 0, found 1 + --> $DIR/const-expression-parameter.rs:30:12 + | +LL | foo::< X >(); + | ^ unexpected type argument + +error[E0308]: mismatched types + --> $DIR/const-expression-parameter.rs:40:17 + | +LL | baz::< -1 , "2" >(); + | ^^^ expected i32, found reference + | + = note: expected type `i32` + found type `&'static str` + +error[E0308]: mismatched types + --> $DIR/const-expression-parameter.rs:43:11 + | +LL | bat::<(1, 2)>(); + | ^^^^^^ expected a tuple with 3 elements, found one with 2 elements + | + = note: expected type `(i32, i32, i32)` + found type `(i32, i32)` + +error[E0107]: wrong number of const arguments: expected 1, found 0 + --> $DIR/const-expression-parameter.rs:50:5 + | +LL | bar::< const_bool() >(); + | ^^^^^^^^^^^^^^^^^^^^^ expected 1 const argument + +error[E0107]: wrong number of type arguments: expected 0, found 1 + --> $DIR/const-expression-parameter.rs:50:12 + | +LL | bar::< const_bool() >(); + | ^^^^^^^^^^^^ unexpected type argument + +error: aborting due to 20 previous errors +Some errors have detailed explanations: E0107, E0308. +For more information about an error, try `rustc --explain E0107`. diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs index 7d1e5c3d64df3..091becfb63e51 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs @@ -239,6 +239,7 @@ fn inside_const_generic_arguments() { // admit non-IDENT expressions in const generic arguments. if A::< - true && let 1 = 1 //~ ERROR expected one of `,` or `>`, found `&&` - >::O == 5 {} + true && let 1 = 1 + >::O == 5 {} //~ ERROR chained comparison operators require parentheses + //~^ ERROR expected one of `,`, `.`, `>`, `?`, or an operator, found `{` } diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr index 4edc00efc7e72..c05cf1aa84be2 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr @@ -1,8 +1,14 @@ -error: expected one of `,` or `>`, found `&&` - --> $DIR/disallowed-positions.rs:242:14 +error: chained comparison operators require parentheses + --> $DIR/disallowed-positions.rs:243:5 | -LL | true && let 1 = 1 - | ^^ expected one of `,` or `>` here +LL | >::O == 5 {} + | ^^^^^^^^^ + +error: expected one of `,`, `.`, `>`, `?`, or an operator, found `{` + --> $DIR/disallowed-positions.rs:243:15 + | +LL | >::O == 5 {} + | ^ expected one of `,`, `.`, `>`, `?`, or an operator here error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:32:9 @@ -983,7 +989,7 @@ error[E0019]: constant contains unimplemented expression type LL | true && let 1 = 1 | ^ -error: aborting due to 109 previous errors +error: aborting due to 110 previous errors Some errors have detailed explanations: E0019, E0277, E0308, E0600, E0614. For more information about an error, try `rustc --explain E0019`.