Auto merge of #77502 - varkor:const-generics-suggest-enclosing-braces, r=petrochenkov
Suggest that expressions that look like const generic arguments should be enclosed in brackets I pulled out the changes for const expressions from https://github.com/rust-lang/rust/pull/71592 (without the trait object diagnostic changes) and made some small changes; the implementation is `@estebank's.` We're also going to want to make some changes separately to account for trait objects (they result in poor diagnostics, as is evident from one of the test cases here), such as an adaption of https://github.com/rust-lang/rust/pull/72273. Fixes https://github.com/rust-lang/rust/issues/70753. r? `@petrochenkov`
This commit is contained in:
commit
20b1e05a8d
@ -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<Option<P<GenericArgs>>> for AngleBracketedArgs {
|
||||
fn into(self) -> Option<P<GenericArgs>> {
|
||||
Some(P(GenericArgs::AngleBracketed(self)))
|
||||
|
@ -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 {
|
||||
|
@ -490,7 +490,7 @@ pub fn super_relate_consts<R: TypeRelation<'tcx>>(
|
||||
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)
|
||||
|
||||
|
@ -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<AngleBracketedArg>,
|
||||
) -> 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<Expr>> {
|
||||
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::<bar = baz>`, 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)
|
||||
}
|
||||
}
|
||||
|
@ -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<Spanned<AssocOp>> {
|
||||
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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<rustc_ast::Expr>) -> 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<GenericArg>> {
|
||||
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);
|
||||
};
|
||||
|
52
src/test/ui/const-generics/closing-args-token.full.stderr
Normal file
52
src/test/ui/const-generics/closing-args-token.full.stderr
Normal file
@ -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::<x >>= 2 > 0>;
|
||||
| ^^ ^
|
||||
|
|
||||
help: split the comparison into two
|
||||
|
|
||||
LL | T::<x >>= 2 && 2 > 0>;
|
||||
| ^^^^
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
52
src/test/ui/const-generics/closing-args-token.min.stderr
Normal file
52
src/test/ui/const-generics/closing-args-token.min.stderr
Normal file
@ -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::<x >>= 2 > 0>;
|
||||
| ^^ ^
|
||||
|
|
||||
help: split the comparison into two
|
||||
|
|
||||
LL | T::<x >>= 2 && 2 > 0>;
|
||||
| ^^^^
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
32
src/test/ui/const-generics/closing-args-token.rs
Normal file
32
src/test/ui/const-generics/closing-args-token.rs
Normal file
@ -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<const X: u32>;
|
||||
struct T<const X: bool>;
|
||||
|
||||
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::<x >>= 2 > 0>;
|
||||
//~^ ERROR comparison operators cannot be chained
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -0,0 +1,49 @@
|
||||
#![feature(min_const_generics)]
|
||||
|
||||
fn foo<const C: usize>() {}
|
||||
|
||||
const BAR: usize = 42;
|
||||
|
||||
fn a() {
|
||||
foo<BAR + 3>(); //~ ERROR comparison operators cannot be chained
|
||||
}
|
||||
fn b() {
|
||||
foo<BAR + BAR>(); //~ ERROR comparison operators cannot be chained
|
||||
}
|
||||
fn c() {
|
||||
foo<3 + 3>(); //~ ERROR comparison operators cannot be chained
|
||||
}
|
||||
fn d() {
|
||||
foo<BAR - 3>(); //~ ERROR comparison operators cannot be chained
|
||||
}
|
||||
fn e() {
|
||||
foo<BAR - BAR>(); //~ ERROR comparison operators cannot be chained
|
||||
}
|
||||
fn f() {
|
||||
foo<100 - BAR>(); //~ ERROR comparison operators cannot be chained
|
||||
}
|
||||
fn g() {
|
||||
foo<bar<i32>()>(); //~ ERROR comparison operators cannot be chained
|
||||
//~^ ERROR expected one of `;` or `}`, found `>`
|
||||
}
|
||||
fn h() {
|
||||
foo<bar::<i32>()>(); //~ ERROR comparison operators cannot be chained
|
||||
}
|
||||
fn i() {
|
||||
foo<bar::<i32>() + BAR>(); //~ ERROR comparison operators cannot be chained
|
||||
}
|
||||
fn j() {
|
||||
foo<bar::<i32>() - BAR>(); //~ ERROR comparison operators cannot be chained
|
||||
}
|
||||
fn k() {
|
||||
foo<BAR - bar::<i32>()>(); //~ ERROR comparison operators cannot be chained
|
||||
}
|
||||
fn l() {
|
||||
foo<BAR - bar::<i32>()>(); //~ ERROR comparison operators cannot be chained
|
||||
}
|
||||
|
||||
const fn bar<const C: usize>() -> usize {
|
||||
C
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,140 @@
|
||||
error: comparison operators cannot be chained
|
||||
--> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:8:8
|
||||
|
|
||||
LL | foo<BAR + 3>();
|
||||
| ^ ^
|
||||
|
|
||||
help: use `::<...>` instead of `<...>` to specify type arguments
|
||||
|
|
||||
LL | foo::<BAR + 3>();
|
||||
| ^^
|
||||
|
||||
error: comparison operators cannot be chained
|
||||
--> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:11:8
|
||||
|
|
||||
LL | foo<BAR + BAR>();
|
||||
| ^ ^
|
||||
|
|
||||
help: use `::<...>` instead of `<...>` to specify type arguments
|
||||
|
|
||||
LL | foo::<BAR + BAR>();
|
||||
| ^^
|
||||
|
||||
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<BAR - 3>();
|
||||
| ^ ^
|
||||
|
|
||||
help: use `::<...>` instead of `<...>` to specify type arguments
|
||||
|
|
||||
LL | foo::<BAR - 3>();
|
||||
| ^^
|
||||
|
||||
error: comparison operators cannot be chained
|
||||
--> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:20:8
|
||||
|
|
||||
LL | foo<BAR - BAR>();
|
||||
| ^ ^
|
||||
|
|
||||
help: use `::<...>` instead of `<...>` to specify type arguments
|
||||
|
|
||||
LL | foo::<BAR - BAR>();
|
||||
| ^^
|
||||
|
||||
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<bar<i32>()>();
|
||||
| ^ ^
|
||||
|
|
||||
help: use `::<...>` instead of `<...>` to specify type arguments
|
||||
|
|
||||
LL | foo::<bar<i32>()>();
|
||||
| ^^
|
||||
|
||||
error: expected one of `;` or `}`, found `>`
|
||||
--> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:26:19
|
||||
|
|
||||
LL | foo<bar<i32>()>();
|
||||
| ^ expected one of `;` or `}`
|
||||
|
||||
error: comparison operators cannot be chained
|
||||
--> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:30:8
|
||||
|
|
||||
LL | foo<bar::<i32>()>();
|
||||
| ^ ^
|
||||
|
|
||||
help: use `::<...>` instead of `<...>` to specify type arguments
|
||||
|
|
||||
LL | foo::<bar::<i32>()>();
|
||||
| ^^
|
||||
|
||||
error: comparison operators cannot be chained
|
||||
--> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:33:8
|
||||
|
|
||||
LL | foo<bar::<i32>() + BAR>();
|
||||
| ^ ^
|
||||
|
|
||||
help: use `::<...>` instead of `<...>` to specify type arguments
|
||||
|
|
||||
LL | foo::<bar::<i32>() + BAR>();
|
||||
| ^^
|
||||
|
||||
error: comparison operators cannot be chained
|
||||
--> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:36:8
|
||||
|
|
||||
LL | foo<bar::<i32>() - BAR>();
|
||||
| ^ ^
|
||||
|
|
||||
help: use `::<...>` instead of `<...>` to specify type arguments
|
||||
|
|
||||
LL | foo::<bar::<i32>() - BAR>();
|
||||
| ^^
|
||||
|
||||
error: comparison operators cannot be chained
|
||||
--> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:39:8
|
||||
|
|
||||
LL | foo<BAR - bar::<i32>()>();
|
||||
| ^ ^
|
||||
|
|
||||
help: use `::<...>` instead of `<...>` to specify type arguments
|
||||
|
|
||||
LL | foo::<BAR - bar::<i32>()>();
|
||||
| ^^
|
||||
|
||||
error: comparison operators cannot be chained
|
||||
--> $DIR/const-expression-suggest-missing-braces-without-turbofish.rs:42:8
|
||||
|
|
||||
LL | foo<BAR - bar::<i32>()>();
|
||||
| ^ ^
|
||||
|
|
||||
help: use `::<...>` instead of `<...>` to specify type arguments
|
||||
|
|
||||
LL | foo::<BAR - bar::<i32>()>();
|
||||
| ^^
|
||||
|
||||
error: aborting due to 13 previous errors
|
||||
|
@ -0,0 +1,55 @@
|
||||
#![feature(min_const_generics)]
|
||||
|
||||
fn foo<const C: usize>() {}
|
||||
|
||||
const BAR: usize = 42;
|
||||
|
||||
fn a() {
|
||||
foo::<BAR + 3>(); //~ ERROR expected one of
|
||||
}
|
||||
fn b() {
|
||||
// FIXME(const_generics): these diagnostics are awful, because trait objects without `dyn` were
|
||||
// a terrible mistake.
|
||||
foo::<BAR + BAR>();
|
||||
//~^ 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::<BAR - 3>(); //~ ERROR expected one of
|
||||
}
|
||||
fn e() {
|
||||
foo::<BAR - BAR>(); //~ ERROR expected one of
|
||||
}
|
||||
fn f() {
|
||||
foo::<100 - BAR>(); //~ ERROR expressions must be enclosed in braces
|
||||
}
|
||||
fn g() {
|
||||
foo::<bar<i32>()>(); //~ ERROR expected one of
|
||||
}
|
||||
fn h() {
|
||||
foo::<bar::<i32>()>(); //~ ERROR expected one of
|
||||
}
|
||||
fn i() {
|
||||
foo::<bar::<i32>() + BAR>(); //~ ERROR expected one of
|
||||
}
|
||||
fn j() {
|
||||
foo::<bar::<i32>() - BAR>(); //~ ERROR expected one of
|
||||
}
|
||||
fn k() {
|
||||
foo::<BAR - bar::<i32>()>(); //~ ERROR expected one of
|
||||
}
|
||||
fn l() {
|
||||
foo::<BAR - bar::<i32>()>(); //~ ERROR expected one of
|
||||
}
|
||||
|
||||
const fn bar<const C: usize>() -> usize {
|
||||
C
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,157 @@
|
||||
error: expected one of `,` or `>`, found `3`
|
||||
--> $DIR/const-expression-suggest-missing-braces.rs:8:17
|
||||
|
|
||||
LL | foo::<BAR + 3>();
|
||||
| ^ 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::<BAR - 3>();
|
||||
| ^ 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::<BAR - BAR>();
|
||||
| ^ 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::<bar<i32>()>();
|
||||
| ^ expected one of `,` or `>`
|
||||
|
|
||||
help: expressions must be enclosed in braces to be used as const generic arguments
|
||||
|
|
||||
LL | foo::<{ bar<i32>() }>();
|
||||
| ^ ^
|
||||
|
||||
error: expected one of `,` or `>`, found `(`
|
||||
--> $DIR/const-expression-suggest-missing-braces.rs:36:21
|
||||
|
|
||||
LL | foo::<bar::<i32>()>();
|
||||
| ^ expected one of `,` or `>`
|
||||
|
|
||||
help: expressions must be enclosed in braces to be used as const generic arguments
|
||||
|
|
||||
LL | foo::<{ bar::<i32>() }>();
|
||||
| ^ ^
|
||||
|
||||
error: expected one of `,` or `>`, found `(`
|
||||
--> $DIR/const-expression-suggest-missing-braces.rs:39:21
|
||||
|
|
||||
LL | foo::<bar::<i32>() + BAR>();
|
||||
| ^ expected one of `,` or `>`
|
||||
|
|
||||
help: expressions must be enclosed in braces to be used as const generic arguments
|
||||
|
|
||||
LL | foo::<{ bar::<i32>() + BAR }>();
|
||||
| ^ ^
|
||||
|
||||
error: expected one of `,` or `>`, found `(`
|
||||
--> $DIR/const-expression-suggest-missing-braces.rs:42:21
|
||||
|
|
||||
LL | foo::<bar::<i32>() - BAR>();
|
||||
| ^ expected one of `,` or `>`
|
||||
|
|
||||
help: expressions must be enclosed in braces to be used as const generic arguments
|
||||
|
|
||||
LL | foo::<{ bar::<i32>() - BAR }>();
|
||||
| ^ ^
|
||||
|
||||
error: expected one of `,` or `>`, found `-`
|
||||
--> $DIR/const-expression-suggest-missing-braces.rs:45:15
|
||||
|
|
||||
LL | foo::<BAR - bar::<i32>()>();
|
||||
| ^ expected one of `,` or `>`
|
||||
|
|
||||
help: expressions must be enclosed in braces to be used as const generic arguments
|
||||
|
|
||||
LL | foo::<{ BAR - bar::<i32>() }>();
|
||||
| ^ ^
|
||||
|
||||
error: expected one of `,` or `>`, found `-`
|
||||
--> $DIR/const-expression-suggest-missing-braces.rs:48:15
|
||||
|
|
||||
LL | foo::<BAR - bar::<i32>()>();
|
||||
| ^ expected one of `,` or `>`
|
||||
|
|
||||
help: expressions must be enclosed in braces to be used as const generic arguments
|
||||
|
|
||||
LL | foo::<{ BAR - bar::<i32>() }>();
|
||||
| ^ ^
|
||||
|
||||
error[E0404]: expected trait, found constant `BAR`
|
||||
--> $DIR/const-expression-suggest-missing-braces.rs:13:11
|
||||
|
|
||||
LL | foo::<BAR + BAR>();
|
||||
| ^^^ not a trait
|
||||
|
||||
error[E0404]: expected trait, found constant `BAR`
|
||||
--> $DIR/const-expression-suggest-missing-braces.rs:13:17
|
||||
|
|
||||
LL | foo::<BAR + BAR>();
|
||||
| ^^^ not a trait
|
||||
|
||||
warning: trait objects without an explicit `dyn` are deprecated
|
||||
--> $DIR/const-expression-suggest-missing-braces.rs:13:11
|
||||
|
|
||||
LL | foo::<BAR + BAR>();
|
||||
| ^^^^^^^^^ 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::<BAR + BAR>();
|
||||
| ^^^^^^^^^^^^^^^^ 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::<BAR + BAR>();
|
||||
| ^^^^^^^^^ 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`.
|
@ -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 {}
|
||||
}
|
||||
|
@ -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`.
|
||||
|
Loading…
Reference in New Issue
Block a user