diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 7224b482ed780..7d5e235c88526 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -222,6 +222,15 @@ pub enum AngleBracketedArg { Constraint(AssocTyConstraint), } +impl AngleBracketedArg { + pub fn span(&self) -> Span { + match self { + AngleBracketedArg::Arg(arg) => arg.span(), + AngleBracketedArg::Constraint(constraint) => constraint.span, + } + } +} + impl Into>> for AngleBracketedArgs { fn into(self) -> Option> { Some(P(GenericArgs::AngleBracketed(self))) diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index d991027cb4573..2bba7e618c050 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -303,6 +303,13 @@ impl TokenKind { _ => None, } } + + pub fn should_end_const_arg(&self) -> bool { + match self { + Gt | Ge | BinOp(Shr) | BinOpEq(Shr) => true, + _ => false, + } + } } impl Token { diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index c4df0bba726cb..ef5034e218da4 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -490,7 +490,7 @@ pub fn super_relate_consts>( let eagerly_eval = |x: &'tcx ty::Const<'tcx>| x.eval(tcx, relation.param_env()).val; // FIXME(eddyb) doesn't look like everything below checks that `a.ty == b.ty`. - // We could probably always assert it early, as `const` generic parameters + // We could probably always assert it early, as const generic parameters // are not allowed to depend on other generic parameters, i.e. are concrete. // (although there could be normalization differences) diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 39e1256a57835..012940a2ebb53 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -1,13 +1,14 @@ use super::ty::AllowPlus; -use super::{BlockMode, Parser, PathStyle, SemiColonMode, SeqSep, TokenExpectType, TokenType}; +use super::TokenType; +use super::{BlockMode, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep, TokenExpectType}; use rustc_ast::ptr::P; use rustc_ast::token::{self, Lit, LitKind, TokenKind}; use rustc_ast::util::parser::AssocOp; use rustc_ast::{ - self as ast, AngleBracketedArgs, AttrVec, BinOpKind, BindingMode, Block, BlockCheckMode, Expr, - ExprKind, Item, ItemKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QSelf, Ty, - TyKind, + self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, + Block, BlockCheckMode, Expr, ExprKind, GenericArg, Item, ItemKind, Mutability, Param, Pat, + PatKind, Path, PathSegment, QSelf, Ty, TyKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; @@ -1780,4 +1781,142 @@ impl<'a> Parser<'a> { } } } + + /// Handle encountering a symbol in a generic argument list that is not a `,` or `>`. In this + /// case, we emit an error and try to suggest enclosing a const argument in braces if it looks + /// like the user has forgotten them. + pub fn handle_ambiguous_unbraced_const_arg( + &mut self, + args: &mut Vec, + ) -> PResult<'a, bool> { + // If we haven't encountered a closing `>`, then the argument is malformed. + // It's likely that the user has written a const expression without enclosing it + // in braces, so we try to recover here. + let arg = args.pop().unwrap(); + // FIXME: for some reason using `unexpected` or `expected_one_of_not_found` has + // adverse side-effects to subsequent errors and seems to advance the parser. + // We are causing this error here exclusively in case that a `const` expression + // could be recovered from the current parser state, even if followed by more + // arguments after a comma. + let mut err = self.struct_span_err( + self.token.span, + &format!("expected one of `,` or `>`, found {}", super::token_descr(&self.token)), + ); + err.span_label(self.token.span, "expected one of `,` or `>`"); + match self.recover_const_arg(arg.span(), err) { + Ok(arg) => { + args.push(AngleBracketedArg::Arg(arg)); + if self.eat(&token::Comma) { + return Ok(true); // Continue + } + } + Err(mut err) => { + args.push(arg); + // We will emit a more generic error later. + err.delay_as_bug(); + } + } + return Ok(false); // Don't continue. + } + + /// Handle a generic const argument that had not been enclosed in braces, and suggest enclosing + /// it braces. In this situation, unlike in `handle_ambiguous_unbraced_const_arg`, this is + /// almost certainly a const argument, so we always offer a suggestion. + pub fn handle_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, P> { + let start = self.token.span; + let expr = self.parse_expr_res(Restrictions::CONST_EXPR, None).map_err(|mut err| { + err.span_label( + start.shrink_to_lo(), + "while parsing a const generic argument starting here", + ); + err + })?; + if !self.expr_is_valid_const_arg(&expr) { + self.struct_span_err( + expr.span, + "expressions must be enclosed in braces to be used as const generic \ + arguments", + ) + .multipart_suggestion( + "enclose the `const` expression in braces", + vec![ + (expr.span.shrink_to_lo(), "{ ".to_string()), + (expr.span.shrink_to_hi(), " }".to_string()), + ], + Applicability::MachineApplicable, + ) + .emit(); + } + Ok(expr) + } + + /// Try to recover from possible generic const argument without `{` and `}`. + /// + /// When encountering code like `foo::< bar + 3 >` or `foo::< bar - baz >` we suggest + /// `foo::<{ bar + 3 }>` and `foo::<{ bar - baz }>`, respectively. We only provide a suggestion + /// if we think that that the resulting expression would be well formed. + pub fn recover_const_arg( + &mut self, + start: Span, + mut err: DiagnosticBuilder<'a>, + ) -> PResult<'a, GenericArg> { + let is_op = AssocOp::from_token(&self.token) + .and_then(|op| { + if let AssocOp::Greater + | AssocOp::Less + | AssocOp::ShiftRight + | AssocOp::GreaterEqual + // Don't recover from `foo::`, because this could be an attempt to + // assign a value to a defaulted generic parameter. + | AssocOp::Assign + | AssocOp::AssignOp(_) = op + { + None + } else { + Some(op) + } + }) + .is_some(); + // This will be true when a trait object type `Foo +` or a path which was a `const fn` with + // type params has been parsed. + let was_op = + matches!(self.prev_token.kind, token::BinOp(token::Plus | token::Shr) | token::Gt); + if !is_op && !was_op { + // We perform these checks and early return to avoid taking a snapshot unnecessarily. + return Err(err); + } + let snapshot = self.clone(); + if is_op { + self.bump(); + } + match self.parse_expr_res(Restrictions::CONST_EXPR, None) { + Ok(expr) => { + if token::Comma == self.token.kind || self.token.kind.should_end_const_arg() { + // Avoid the following output by checking that we consumed a full const arg: + // help: expressions must be enclosed in braces to be used as const generic + // arguments + // | + // LL | let sr: Vec<{ (u32, _, _) = vec![] }; + // | ^ ^ + err.multipart_suggestion( + "expressions must be enclosed in braces to be used as const generic \ + arguments", + vec![ + (start.shrink_to_lo(), "{ ".to_string()), + (expr.span.shrink_to_hi(), " }".to_string()), + ], + Applicability::MaybeIncorrect, + ); + let value = self.mk_expr_err(start.to(expr.span)); + err.emit(); + return Ok(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })); + } + } + Err(mut err) => { + err.cancel(); + } + } + *self = snapshot; + Err(err) + } } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index c44e00f861de6..5994eda966fb3 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -359,6 +359,18 @@ impl<'a> Parser<'a> { /// Also performs recovery for `and` / `or` which are mistaken for `&&` and `||` respectively. fn check_assoc_op(&self) -> Option> { let (op, span) = match (AssocOp::from_token(&self.token), self.token.ident()) { + // When parsing const expressions, stop parsing when encountering `>`. + ( + Some( + AssocOp::ShiftRight + | AssocOp::Greater + | AssocOp::GreaterEqual + | AssocOp::AssignOp(token::BinOpToken::Shr), + ), + _, + ) if self.restrictions.contains(Restrictions::CONST_EXPR) => { + return None; + } (Some(op), _) => (op, self.token.span), (None, Some((Ident { name: sym::and, span }, false))) => { self.error_bad_logical_op("and", "&&", "conjunction"); @@ -1715,7 +1727,7 @@ impl<'a> Parser<'a> { let lo = self.prev_token.span; let pat = self.parse_top_pat(GateOr::No)?; self.expect(&token::Eq)?; - let expr = self.with_res(Restrictions::NO_STRUCT_LITERAL, |this| { + let expr = self.with_res(self.restrictions | Restrictions::NO_STRUCT_LITERAL, |this| { this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into()) })?; let span = lo.to(expr.span); diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 175dd3fa53a6e..d99fcb0c4a10f 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -36,6 +36,7 @@ bitflags::bitflags! { struct Restrictions: u8 { const STMT_EXPR = 1 << 0; const NO_STRUCT_LITERAL = 1 << 1; + const CONST_EXPR = 1 << 2; } } diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 66ce015d02e4c..06760547eba0c 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -397,6 +397,13 @@ impl<'a> Parser<'a> { while let Some(arg) = self.parse_angle_arg()? { args.push(arg); if !self.eat(&token::Comma) { + if !self.token.kind.should_end_const_arg() { + if self.handle_ambiguous_unbraced_const_arg(&mut args)? { + // We've managed to (partially) recover, so continue trying to parse + // arguments. + continue; + } + } break; } } @@ -476,41 +483,50 @@ impl<'a> Parser<'a> { Ok(self.mk_ty(span, ast::TyKind::Err)) } + /// We do not permit arbitrary expressions as const arguments. They must be one of: + /// - An expression surrounded in `{}`. + /// - A literal. + /// - A numeric literal prefixed by `-`. + pub(super) fn expr_is_valid_const_arg(&self, expr: &P) -> bool { + match &expr.kind { + ast::ExprKind::Block(_, _) | ast::ExprKind::Lit(_) => true, + ast::ExprKind::Unary(ast::UnOp::Neg, expr) => match &expr.kind { + ast::ExprKind::Lit(_) => true, + _ => false, + }, + _ => false, + } + } + /// Parse a generic argument in a path segment. /// This does not include constraints, e.g., `Item = u8`, which is handled in `parse_angle_arg`. fn parse_generic_arg(&mut self) -> PResult<'a, Option> { + let start = self.token.span; let arg = if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { // Parse lifetime argument. GenericArg::Lifetime(self.expect_lifetime()) } else if self.check_const_arg() { // Parse const argument. - let expr = if let token::OpenDelim(token::Brace) = self.token.kind { + let value = if let token::OpenDelim(token::Brace) = self.token.kind { self.parse_block_expr( None, self.token.span, BlockCheckMode::Default, ast::AttrVec::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 { - let span = self.token.span; - let msg = "identifiers may currently not be used for const generics"; - self.struct_span_err(span, msg).emit(); - let block = self.mk_block_err(span); - self.mk_expr(span, ast::ExprKind::Block(block, None), ast::AttrVec::new()) - } } else { - self.parse_literal_maybe_minus()? + self.handle_unambiguous_unbraced_const_arg()? }; - GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value: expr }) + GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }) } else if self.check_type() { // Parse type argument. - GenericArg::Type(self.parse_ty()?) + match self.parse_ty() { + Ok(ty) => GenericArg::Type(ty), + Err(err) => { + // Try to recover from possible `const` arg without braces. + return self.recover_const_arg(start, err).map(Some); + } + } } else { return Ok(None); }; diff --git a/src/test/ui/const-generics/closing-args-token.full.stderr b/src/test/ui/const-generics/closing-args-token.full.stderr new file mode 100644 index 0000000000000..1c3ddd345a533 --- /dev/null +++ b/src/test/ui/const-generics/closing-args-token.full.stderr @@ -0,0 +1,52 @@ +error: expressions must be enclosed in braces to be used as const generic arguments + --> $DIR/closing-args-token.rs:11:9 + | +LL | S::<5 + 2 >> 7>; + | ^^^^^ + | +help: enclose the `const` expression in braces + | +LL | S::<{ 5 + 2 } >> 7>; + | ^ ^ + +error: comparison operators cannot be chained + --> $DIR/closing-args-token.rs:11:16 + | +LL | S::<5 + 2 >> 7>; + | ^ ^ + | +help: split the comparison into two + | +LL | S::<5 + 2 >> 7 && 7>; + | ^^^^ + +error: comparison operators cannot be chained + --> $DIR/closing-args-token.rs:17:20 + | +LL | S::<{ 5 + 2 } >> 7>; + | ^ ^ + | +help: split the comparison into two + | +LL | S::<{ 5 + 2 } >> 7 && 7>; + | ^^^^ + +error: expected expression, found `;` + --> $DIR/closing-args-token.rs:22:16 + | +LL | T::<0 >= 3>; + | ^ expected expression + +error: comparison operators cannot be chained + --> $DIR/closing-args-token.rs:28:12 + | +LL | T::>= 2 > 0>; + | ^^ ^ + | +help: split the comparison into two + | +LL | T::>= 2 && 2 > 0>; + | ^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/const-generics/closing-args-token.min.stderr b/src/test/ui/const-generics/closing-args-token.min.stderr new file mode 100644 index 0000000000000..1c3ddd345a533 --- /dev/null +++ b/src/test/ui/const-generics/closing-args-token.min.stderr @@ -0,0 +1,52 @@ +error: expressions must be enclosed in braces to be used as const generic arguments + --> $DIR/closing-args-token.rs:11:9 + | +LL | S::<5 + 2 >> 7>; + | ^^^^^ + | +help: enclose the `const` expression in braces + | +LL | S::<{ 5 + 2 } >> 7>; + | ^ ^ + +error: comparison operators cannot be chained + --> $DIR/closing-args-token.rs:11:16 + | +LL | S::<5 + 2 >> 7>; + | ^ ^ + | +help: split the comparison into two + | +LL | S::<5 + 2 >> 7 && 7>; + | ^^^^ + +error: comparison operators cannot be chained + --> $DIR/closing-args-token.rs:17:20 + | +LL | S::<{ 5 + 2 } >> 7>; + | ^ ^ + | +help: split the comparison into two + | +LL | S::<{ 5 + 2 } >> 7 && 7>; + | ^^^^ + +error: expected expression, found `;` + --> $DIR/closing-args-token.rs:22:16 + | +LL | T::<0 >= 3>; + | ^ expected expression + +error: comparison operators cannot be chained + --> $DIR/closing-args-token.rs:28:12 + | +LL | T::>= 2 > 0>; + | ^^ ^ + | +help: split the comparison into two + | +LL | T::>= 2 && 2 > 0>; + | ^^^^ + +error: aborting due to 5 previous errors + diff --git a/src/test/ui/const-generics/closing-args-token.rs b/src/test/ui/const-generics/closing-args-token.rs new file mode 100644 index 0000000000000..8699637c54e94 --- /dev/null +++ b/src/test/ui/const-generics/closing-args-token.rs @@ -0,0 +1,32 @@ +// revisions: full min + +#![cfg_attr(full, feature(const_generics))] +#![cfg_attr(full, allow(incomplete_features))] +#![cfg_attr(min, feature(min_const_generics))] + +struct S; +struct T; + +fn bad_args_1() { + S::<5 + 2 >> 7>; + //~^ ERROR expressions must be enclosed in braces to be used as const generic arguments + //~| ERROR comparison operators cannot be chained +} + +fn bad_args_2() { + S::<{ 5 + 2 } >> 7>; + //~^ ERROR comparison operators cannot be chained +} + +fn bad_args_3() { + T::<0 >= 3>; + //~^ ERROR expected expression, found `;` +} + +fn bad_args_4() { + let mut x = 0; + T::>= 2 > 0>; + //~^ ERROR comparison operators cannot be chained +} + +fn main() {} diff --git a/src/test/ui/const-generics/const-expression-parameter.full.stderr b/src/test/ui/const-generics/const-expression-parameter.full.stderr index 496af9c6e02e0..0615a4c206d80 100644 --- a/src/test/ui/const-generics/const-expression-parameter.full.stderr +++ b/src/test/ui/const-generics/const-expression-parameter.full.stderr @@ -1,8 +1,13 @@ -error: expected one of `,` or `>`, found `+` - --> $DIR/const-expression-parameter.rs:16:22 +error: expressions must be enclosed in braces to be used as const generic arguments + --> $DIR/const-expression-parameter.rs:16:20 | LL | i32_identity::<1 + 2>(); - | ^ expected one of `,` or `>` + | ^^^^^ + | +help: enclose the `const` expression in braces + | +LL | i32_identity::<{ 1 + 2 }>(); + | ^ ^ error: aborting due to previous error diff --git a/src/test/ui/const-generics/const-expression-parameter.min.stderr b/src/test/ui/const-generics/const-expression-parameter.min.stderr index 496af9c6e02e0..0615a4c206d80 100644 --- a/src/test/ui/const-generics/const-expression-parameter.min.stderr +++ b/src/test/ui/const-generics/const-expression-parameter.min.stderr @@ -1,8 +1,13 @@ -error: expected one of `,` or `>`, found `+` - --> $DIR/const-expression-parameter.rs:16:22 +error: expressions must be enclosed in braces to be used as const generic arguments + --> $DIR/const-expression-parameter.rs:16:20 | LL | i32_identity::<1 + 2>(); - | ^ expected one of `,` or `>` + | ^^^^^ + | +help: enclose the `const` expression in braces + | +LL | i32_identity::<{ 1 + 2 }>(); + | ^ ^ error: aborting due to previous error diff --git a/src/test/ui/const-generics/const-expression-parameter.rs b/src/test/ui/const-generics/const-expression-parameter.rs index 7a1eaf9f93908..3ef7c8b32e035 100644 --- a/src/test/ui/const-generics/const-expression-parameter.rs +++ b/src/test/ui/const-generics/const-expression-parameter.rs @@ -13,7 +13,7 @@ fn foo_a() { } fn foo_b() { - i32_identity::<1 + 2>(); //~ ERROR expected one of `,` or `>`, found `+` + i32_identity::<1 + 2>(); //~ ERROR expressions must be enclosed in braces } fn foo_c() { diff --git a/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.rs b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.rs new file mode 100644 index 0000000000000..b9afd2264307e --- /dev/null +++ b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.rs @@ -0,0 +1,49 @@ +#![feature(min_const_generics)] + +fn foo() {} + +const BAR: usize = 42; + +fn a() { + foo(); //~ ERROR comparison operators cannot be chained +} +fn b() { + foo(); //~ ERROR comparison operators cannot be chained +} +fn c() { + foo<3 + 3>(); //~ ERROR comparison operators cannot be chained +} +fn d() { + foo(); //~ ERROR comparison operators cannot be chained +} +fn e() { + foo(); //~ ERROR comparison operators cannot be chained +} +fn f() { + foo<100 - BAR>(); //~ ERROR comparison operators cannot be chained +} +fn g() { + foo()>(); //~ ERROR comparison operators cannot be chained + //~^ ERROR expected one of `;` or `}`, found `>` +} +fn h() { + foo()>(); //~ ERROR comparison operators cannot be chained +} +fn i() { + foo() + BAR>(); //~ ERROR comparison operators cannot be chained +} +fn j() { + foo() - BAR>(); //~ ERROR comparison operators cannot be chained +} +fn k() { + foo()>(); //~ ERROR comparison operators cannot be chained +} +fn l() { + foo()>(); //~ ERROR comparison operators cannot be chained +} + +const fn bar() -> usize { + C +} + +fn main() {} diff --git a/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.stderr b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.stderr new file mode 100644 index 0000000000000..103a295fced51 --- /dev/null +++ b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces-without-turbofish.stderr @@ -0,0 +1,140 @@ +error: comparison operators cannot be chained + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:8:8 + | +LL | foo(); + | ^ ^ + | +help: use `::<...>` instead of `<...>` to specify type arguments + | +LL | foo::(); + | ^^ + +error: comparison operators cannot be chained + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:11:8 + | +LL | foo(); + | ^ ^ + | +help: use `::<...>` instead of `<...>` to specify type arguments + | +LL | foo::(); + | ^^ + +error: comparison operators cannot be chained + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:14:8 + | +LL | foo<3 + 3>(); + | ^ ^ + | +help: use `::<...>` instead of `<...>` to specify type arguments + | +LL | foo::<3 + 3>(); + | ^^ + +error: comparison operators cannot be chained + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:17:8 + | +LL | foo(); + | ^ ^ + | +help: use `::<...>` instead of `<...>` to specify type arguments + | +LL | foo::(); + | ^^ + +error: comparison operators cannot be chained + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:20:8 + | +LL | foo(); + | ^ ^ + | +help: use `::<...>` instead of `<...>` to specify type arguments + | +LL | foo::(); + | ^^ + +error: comparison operators cannot be chained + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:23:8 + | +LL | foo<100 - BAR>(); + | ^ ^ + | +help: use `::<...>` instead of `<...>` to specify type arguments + | +LL | foo::<100 - BAR>(); + | ^^ + +error: comparison operators cannot be chained + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:26:8 + | +LL | foo()>(); + | ^ ^ + | +help: use `::<...>` instead of `<...>` to specify type arguments + | +LL | foo::()>(); + | ^^ + +error: expected one of `;` or `}`, found `>` + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:26:19 + | +LL | foo()>(); + | ^ expected one of `;` or `}` + +error: comparison operators cannot be chained + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:30:8 + | +LL | foo()>(); + | ^ ^ + | +help: use `::<...>` instead of `<...>` to specify type arguments + | +LL | foo::()>(); + | ^^ + +error: comparison operators cannot be chained + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:33:8 + | +LL | foo() + BAR>(); + | ^ ^ + | +help: use `::<...>` instead of `<...>` to specify type arguments + | +LL | foo::() + BAR>(); + | ^^ + +error: comparison operators cannot be chained + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:36:8 + | +LL | foo() - BAR>(); + | ^ ^ + | +help: use `::<...>` instead of `<...>` to specify type arguments + | +LL | foo::() - BAR>(); + | ^^ + +error: comparison operators cannot be chained + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:39:8 + | +LL | foo()>(); + | ^ ^ + | +help: use `::<...>` instead of `<...>` to specify type arguments + | +LL | foo::()>(); + | ^^ + +error: comparison operators cannot be chained + --> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:42:8 + | +LL | foo()>(); + | ^ ^ + | +help: use `::<...>` instead of `<...>` to specify type arguments + | +LL | foo::()>(); + | ^^ + +error: aborting due to 13 previous errors + diff --git a/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces.rs b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces.rs new file mode 100644 index 0000000000000..aea3def5aeb23 --- /dev/null +++ b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces.rs @@ -0,0 +1,55 @@ +#![feature(min_const_generics)] + +fn foo() {} + +const BAR: usize = 42; + +fn a() { + foo::(); //~ ERROR expected one of +} +fn b() { + // FIXME(const_generics): these diagnostics are awful, because trait objects without `dyn` were + // a terrible mistake. + foo::(); + //~^ ERROR expected trait, found constant `BAR` + //~| ERROR expected trait, found constant `BAR` + //~| ERROR wrong number of const arguments: expected 1, found 0 + //~| ERROR wrong number of type arguments: expected 0, found 1 + //~| WARN trait objects without an explicit `dyn` are deprecated +} +fn c() { + foo::<3 + 3>(); //~ ERROR expressions must be enclosed in braces +} +fn d() { + foo::(); //~ ERROR expected one of +} +fn e() { + foo::(); //~ ERROR expected one of +} +fn f() { + foo::<100 - BAR>(); //~ ERROR expressions must be enclosed in braces +} +fn g() { + foo::()>(); //~ ERROR expected one of +} +fn h() { + foo::()>(); //~ ERROR expected one of +} +fn i() { + foo::() + BAR>(); //~ ERROR expected one of +} +fn j() { + foo::() - BAR>(); //~ ERROR expected one of +} +fn k() { + foo::()>(); //~ ERROR expected one of +} +fn l() { + foo::()>(); //~ ERROR expected one of +} + +const fn bar() -> usize { + C +} + +fn main() {} diff --git a/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces.stderr b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces.stderr new file mode 100644 index 0000000000000..47e9dc034efff --- /dev/null +++ b/src/test/ui/const-generics/min_const_generics/const-expression-suggest-missing-braces.stderr @@ -0,0 +1,157 @@ +error: expected one of `,` or `>`, found `3` + --> $DIR/const-expression-suggest-missing-braces.rs:8:17 + | +LL | foo::(); + | ^ expected one of `,` or `>` + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | foo::<{ BAR + 3 }>(); + | ^ ^ + +error: expressions must be enclosed in braces to be used as const generic arguments + --> $DIR/const-expression-suggest-missing-braces.rs:21:11 + | +LL | foo::<3 + 3>(); + | ^^^^^ + | +help: enclose the `const` expression in braces + | +LL | foo::<{ 3 + 3 }>(); + | ^ ^ + +error: expected one of `,` or `>`, found `-` + --> $DIR/const-expression-suggest-missing-braces.rs:24:15 + | +LL | foo::(); + | ^ expected one of `,` or `>` + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | foo::<{ BAR - 3 }>(); + | ^ ^ + +error: expected one of `,` or `>`, found `-` + --> $DIR/const-expression-suggest-missing-braces.rs:27:15 + | +LL | foo::(); + | ^ expected one of `,` or `>` + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | foo::<{ BAR - BAR }>(); + | ^ ^ + +error: expressions must be enclosed in braces to be used as const generic arguments + --> $DIR/const-expression-suggest-missing-braces.rs:30:11 + | +LL | foo::<100 - BAR>(); + | ^^^^^^^^^ + | +help: enclose the `const` expression in braces + | +LL | foo::<{ 100 - BAR }>(); + | ^ ^ + +error: expected one of `,` or `>`, found `(` + --> $DIR/const-expression-suggest-missing-braces.rs:33:19 + | +LL | foo::()>(); + | ^ expected one of `,` or `>` + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | foo::<{ bar() }>(); + | ^ ^ + +error: expected one of `,` or `>`, found `(` + --> $DIR/const-expression-suggest-missing-braces.rs:36:21 + | +LL | foo::()>(); + | ^ expected one of `,` or `>` + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | foo::<{ bar::() }>(); + | ^ ^ + +error: expected one of `,` or `>`, found `(` + --> $DIR/const-expression-suggest-missing-braces.rs:39:21 + | +LL | foo::() + BAR>(); + | ^ expected one of `,` or `>` + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | foo::<{ bar::() + BAR }>(); + | ^ ^ + +error: expected one of `,` or `>`, found `(` + --> $DIR/const-expression-suggest-missing-braces.rs:42:21 + | +LL | foo::() - BAR>(); + | ^ expected one of `,` or `>` + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | foo::<{ bar::() - BAR }>(); + | ^ ^ + +error: expected one of `,` or `>`, found `-` + --> $DIR/const-expression-suggest-missing-braces.rs:45:15 + | +LL | foo::()>(); + | ^ expected one of `,` or `>` + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | foo::<{ BAR - bar::() }>(); + | ^ ^ + +error: expected one of `,` or `>`, found `-` + --> $DIR/const-expression-suggest-missing-braces.rs:48:15 + | +LL | foo::()>(); + | ^ expected one of `,` or `>` + | +help: expressions must be enclosed in braces to be used as const generic arguments + | +LL | foo::<{ BAR - bar::() }>(); + | ^ ^ + +error[E0404]: expected trait, found constant `BAR` + --> $DIR/const-expression-suggest-missing-braces.rs:13:11 + | +LL | foo::(); + | ^^^ not a trait + +error[E0404]: expected trait, found constant `BAR` + --> $DIR/const-expression-suggest-missing-braces.rs:13:17 + | +LL | foo::(); + | ^^^ not a trait + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/const-expression-suggest-missing-braces.rs:13:11 + | +LL | foo::(); + | ^^^^^^^^^ help: use `dyn`: `dyn BAR + BAR` + | + = note: `#[warn(bare_trait_objects)]` on by default + +error[E0107]: wrong number of const arguments: expected 1, found 0 + --> $DIR/const-expression-suggest-missing-braces.rs:13:5 + | +LL | foo::(); + | ^^^^^^^^^^^^^^^^ expected 1 const argument + +error[E0107]: wrong number of type arguments: expected 0, found 1 + --> $DIR/const-expression-suggest-missing-braces.rs:13:11 + | +LL | foo::(); + | ^^^^^^^^^ unexpected type argument + +error: aborting due to 15 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0107, E0404. +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 25c7fe760d339..aeee27a151e82 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 @@ -233,6 +233,8 @@ 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 `&&` + true && let 1 = 1 + //~^ ERROR `let` expressions are not supported here + //~| ERROR expressions must be enclosed in braces >::O == 5 {} } 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 bd39650702c7c..11155038a9139 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,13 @@ -error: expected one of `,` or `>`, found `&&` - --> $DIR/disallowed-positions.rs:236:14 +error: expressions must be enclosed in braces to be used as const generic arguments + --> $DIR/disallowed-positions.rs:236:9 | LL | true && let 1 = 1 - | ^^ expected one of `,` or `>` + | ^^^^^^^^^^^^^^^^^ + | +help: enclose the `const` expression in braces + | +LL | { true && let 1 = 1 } + | ^ ^ error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:32:9 @@ -499,6 +504,15 @@ LL | true && let 1 = 1 = note: only supported directly in conditions of `if`- and `while`-expressions = note: as well as when nested within `&&` and parenthesis in those conditions +error: `let` expressions are not supported here + --> $DIR/disallowed-positions.rs:236:17 + | +LL | true && let 1 = 1 + | ^^^^^^^^^ + | + = note: only supported directly in conditions of `if`- and `while`-expressions + = note: as well as when nested within `&&` and parenthesis in those conditions + warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes --> $DIR/disallowed-positions.rs:20:12 | @@ -961,7 +975,7 @@ LL | let 0 = 0?; = help: the trait `Try` is not implemented for `{integer}` = note: required by `into_result` -error: aborting due to 103 previous errors; 2 warnings emitted +error: aborting due to 104 previous errors; 2 warnings emitted Some errors have detailed explanations: E0277, E0308, E0600, E0614. For more information about an error, try `rustc --explain E0277`.