Skip to content

Commit 892eac1

Browse files
committed
Parse unambiguous const expressions in param position
When encountering the start of an expression that cannot be a type parameter, parse it as a const expression. For an expression to be valid they must not contain "greater than" or "shift right" operations. The new behavior only parses expressions that begin with a token that can start an expression but can't start a type. Calling functions and methods will cause a type path parsing error.
1 parent 870a7de commit 892eac1

9 files changed

+51
-54
lines changed

src/librustc_parse/parser/expr.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,12 @@ impl<'a> Parser<'a> {
331331
/// Also performs recovery for `and` / `or` which are mistaken for `&&` and `||` respectively.
332332
fn check_assoc_op(&self) -> Option<Spanned<AssocOp>> {
333333
let (op, span) = match (AssocOp::from_token(&self.token), self.token.ident()) {
334+
// When parsing const expressions, stop parsing when encountering `<` and `>`.
335+
(Some(AssocOp::ShiftRight), _) | (Some(AssocOp::Greater), _)
336+
if self.restrictions.contains(Restrictions::CONST_EXPR) =>
337+
{
338+
return None;
339+
}
334340
(Some(op), _) => (op, self.token.span),
335341
(None, Some((Ident { name: sym::and, span }, false))) => {
336342
self.error_bad_logical_op("and", "&&", "conjunction");
@@ -1585,7 +1591,9 @@ impl<'a> Parser<'a> {
15851591
let lo = self.prev_token.span;
15861592
let pat = self.parse_top_pat(GateOr::No)?;
15871593
self.expect(&token::Eq)?;
1588-
let expr = self.with_res(Restrictions::NO_STRUCT_LITERAL, |this| {
1594+
// `self.restrictions |` below is to account for `CONST_EXPR` so that we can correctly
1595+
// parse `let` expressions in `const` arguments.
1596+
let expr = self.with_res(self.restrictions | Restrictions::NO_STRUCT_LITERAL, |this| {
15891597
this.parse_assoc_expr_with(1 + prec_let_scrutinee_needs_par(), None.into())
15901598
})?;
15911599
let span = lo.to(expr.span);

src/librustc_parse/parser/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ bitflags::bitflags! {
3434
struct Restrictions: u8 {
3535
const STMT_EXPR = 1 << 0;
3636
const NO_STRUCT_LITERAL = 1 << 1;
37+
const CONST_EXPR = 1 << 2;
3738
}
3839
}
3940

src/librustc_parse/parser/path.rs

+13-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::ty::{AllowPlus, RecoverQPath};
2-
use super::{Parser, TokenType};
2+
use super::{Parser, Restrictions, TokenType};
33
use crate::maybe_whole;
44
use rustc_ast::ast::{self, AngleBracketedArg, AngleBracketedArgs, GenericArg, ParenthesizedArgs};
55
use rustc_ast::ast::{AnonConst, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode};
@@ -479,36 +479,34 @@ impl<'a> Parser<'a> {
479479
GenericArg::Lifetime(self.expect_lifetime())
480480
} else if self.check_const_arg() {
481481
// Parse const argument.
482-
let expr = if let token::OpenDelim(token::Brace) = self.token.kind {
482+
let value = if let token::OpenDelim(token::Brace) = self.token.kind {
483483
self.parse_block_expr(
484484
None,
485485
self.token.span,
486486
BlockCheckMode::Default,
487487
ast::AttrVec::new(),
488488
)?
489-
} else if self.token.is_ident() {
489+
} else {
490490
// FIXME(const_generics): to distinguish between idents for types and consts,
491491
// we should introduce a GenericArg::Ident in the AST and distinguish when
492492
// lowering to the HIR. For now, idents for const args are not permitted.
493-
if self.token.is_bool_lit() {
494-
self.parse_literal_maybe_minus()?
495-
} else {
496-
let span = self.token.span;
497-
let msg = "identifiers may currently not be used for const generics";
498-
self.struct_span_err(span, msg).emit();
499-
let block = self.mk_block_err(span);
500-
self.mk_expr(span, ast::ExprKind::Block(block, None), ast::AttrVec::new())
501-
}
502-
} else {
503-
self.parse_literal_maybe_minus()?
493+
let start = self.token.span;
494+
self.parse_expr_res(Restrictions::CONST_EXPR, None).map_err(|mut err| {
495+
err.span_label(
496+
start.shrink_to_lo(),
497+
"while parsing a `const` argument starting here",
498+
);
499+
err
500+
})?
504501
};
505-
GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value: expr })
502+
GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })
506503
} else if self.check_type() {
507504
// Parse type argument.
508505
GenericArg::Type(self.parse_ty()?)
509506
} else {
510507
return Ok(None);
511508
};
509+
// FIXME: recover from const expressions without braces that require them.
512510
Ok(Some(arg))
513511
}
514512
}

src/test/ui/const-generics/const-expression-missing-braces.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ fn b() {
1515
}
1616
fn c() {
1717
let bar = 3;
18-
foo::<3 + 3>();
19-
//~^ ERROR expected one of `,` or `>`, found `+`
18+
foo::<3 + 3>(); // ok
2019
}
2120

2221
fn main() {}

src/test/ui/const-generics/const-expression-missing-braces.stderr

+1-7
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,6 @@ error: expected one of `!`, `(`, `,`, `>`, `?`, `for`, lifetime, or path, found
44
LL | foo::<bar + 3>();
55
| ^ expected one of 8 possible tokens
66

7-
error: expected one of `,` or `>`, found `+`
8-
--> $DIR/const-expression-missing-braces.rs:18:13
9-
|
10-
LL | foo::<3 + 3>();
11-
| ^ expected one of `,` or `>`
12-
137
error: likely `const` expression parsed as trait bounds
148
--> $DIR/const-expression-missing-braces.rs:13:11
159
|
@@ -21,5 +15,5 @@ help: if you meant to write a `const` expression, surround the expression with b
2115
LL | foo::<{ bar + bar }>();
2216
| ^ ^
2317

24-
error: aborting due to 3 previous errors
18+
error: aborting due to 2 previous errors
2519

src/test/ui/const-generics/const-expression-parameter.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
// check-pass
2+
#![allow(incomplete_features)]
13
#![feature(const_generics)]
2-
//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash
34

45
fn i32_identity<const X: i32>() -> i32 {
56
5
@@ -10,7 +11,7 @@ fn foo_a() {
1011
}
1112

1213
fn foo_b() {
13-
i32_identity::<1 + 2>(); //~ ERROR expected one of `,` or `>`, found `+`
14+
i32_identity::<1 + 2>(); // ok
1415
}
1516

1617
fn foo_c() {

src/test/ui/const-generics/const-expression-parameter.stderr

-16
This file was deleted.

src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -232,10 +232,10 @@ fn inside_const_generic_arguments() {
232232
// In the cases above we have `ExprKind::Block` to help us out.
233233
// Below however, we would not have a block and so an implementation might go
234234
// from visiting expressions to types without banning `let` expressions down the tree.
235-
// This tests ensures that we are not caught by surprise should the parser
236-
// admit non-IDENT expressions in const generic arguments.
237-
235+
// The parser admits non-IDENT expressions in const generic arguments and is caught by the
236+
// test below.
238237
if A::<
239-
true && let 1 = 1 //~ ERROR expected one of `,` or `>`, found `&&`
238+
true && let 1 = 1 //~ ERROR `let` expressions are not supported here
239+
//~^ ERROR `match` is not allowed in a `const`
240240
>::O == 5 {}
241241
}

src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr

+19-7
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
error: expected one of `,` or `>`, found `&&`
2-
--> $DIR/disallowed-positions.rs:239:14
3-
|
4-
LL | true && let 1 = 1
5-
| ^^ expected one of `,` or `>`
6-
71
error: `let` expressions are not supported here
82
--> $DIR/disallowed-positions.rs:32:9
93
|
@@ -499,6 +493,15 @@ LL | true && let 1 = 1
499493
= note: only supported directly in conditions of `if`- and `while`-expressions
500494
= note: as well as when nested within `&&` and parenthesis in those conditions
501495

496+
error: `let` expressions are not supported here
497+
--> $DIR/disallowed-positions.rs:238:17
498+
|
499+
LL | true && let 1 = 1
500+
| ^^^^^^^^^
501+
|
502+
= note: only supported directly in conditions of `if`- and `while`-expressions
503+
= note: as well as when nested within `&&` and parenthesis in those conditions
504+
502505
warning: the feature `const_generics` is incomplete and may cause the compiler to crash
503506
--> $DIR/disallowed-positions.rs:20:12
504507
|
@@ -540,6 +543,15 @@ LL | true && let 1 = 1
540543
= note: see issue #49146 <https://github.com/rust-lang/rust/issues/49146> for more information
541544
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
542545

546+
error[E0658]: `match` is not allowed in a `const`
547+
--> $DIR/disallowed-positions.rs:238:17
548+
|
549+
LL | true && let 1 = 1
550+
| ^^^^^^^^^
551+
|
552+
= note: see issue #49146 <https://github.com/rust-lang/rust/issues/49146> for more information
553+
= help: add `#![feature(const_if_match)]` to the crate attributes to enable
554+
543555
error[E0308]: mismatched types
544556
--> $DIR/disallowed-positions.rs:32:8
545557
|
@@ -980,7 +992,7 @@ LL | let 0 = 0?;
980992
= help: the trait `std::ops::Try` is not implemented for `{integer}`
981993
= note: required by `std::ops::Try::into_result`
982994

983-
error: aborting due to 106 previous errors; 2 warnings emitted
995+
error: aborting due to 107 previous errors; 2 warnings emitted
984996

985997
Some errors have detailed explanations: E0277, E0308, E0600, E0614, E0658.
986998
For more information about an error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)