From 2d57b1aec090c2568a5e3086b9d6e4fee263a494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 24 Apr 2020 13:54:09 -0700 Subject: [PATCH 1/6] Test for const expression missing braces --- .../const-expression-missing-braces.rs | 26 ++++++++++ .../const-expression-missing-braces.stderr | 48 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 src/test/ui/const-generics/const-expression-missing-braces.rs create mode 100644 src/test/ui/const-generics/const-expression-missing-braces.stderr diff --git a/src/test/ui/const-generics/const-expression-missing-braces.rs b/src/test/ui/const-generics/const-expression-missing-braces.rs new file mode 100644 index 0000000000000..d9a7054f6c4a4 --- /dev/null +++ b/src/test/ui/const-generics/const-expression-missing-braces.rs @@ -0,0 +1,26 @@ +#![allow(incomplete_features)] +#![feature(const_generics)] + +fn foo() {} + +fn a() { + let bar = 3; + foo::(); + //~^ ERROR expected one of `!`, `(`, `,`, `>`, `?`, `for`, lifetime, or path, found `3` +} +fn b() { + let bar = 3; + foo::(); + //~^ ERROR expected trait, found local variable `bar` + //~| ERROR expected trait, found local variable `bar` + //~| ERROR wrong number of const arguments: expected 1, found 0 + //~| ERROR wrong number of type arguments: expected 0, found 1 + //~| WARNING trait objects without an explicit `dyn` are deprecated +} +fn c() { + let bar = 3; + foo::<3 + 3>(); + //~^ ERROR expected one of `,` or `>`, found `+` +} + +fn main() {} diff --git a/src/test/ui/const-generics/const-expression-missing-braces.stderr b/src/test/ui/const-generics/const-expression-missing-braces.stderr new file mode 100644 index 0000000000000..3e03053f21ed8 --- /dev/null +++ b/src/test/ui/const-generics/const-expression-missing-braces.stderr @@ -0,0 +1,48 @@ +error: expected one of `!`, `(`, `,`, `>`, `?`, `for`, lifetime, or path, found `3` + --> $DIR/const-expression-missing-braces.rs:8:17 + | +LL | foo::(); + | ^ expected one of 8 possible tokens + +error: expected one of `,` or `>`, found `+` + --> $DIR/const-expression-missing-braces.rs:22:13 + | +LL | foo::<3 + 3>(); + | ^ expected one of `,` or `>` + +error[E0404]: expected trait, found local variable `bar` + --> $DIR/const-expression-missing-braces.rs:13:11 + | +LL | foo::(); + | ^^^ not a trait + +error[E0404]: expected trait, found local variable `bar` + --> $DIR/const-expression-missing-braces.rs:13:17 + | +LL | foo::(); + | ^^^ not a trait + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/const-expression-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-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-missing-braces.rs:13:11 + | +LL | foo::(); + | ^^^^^^^^^ unexpected type argument + +error: aborting due to 6 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0107, E0404. +For more information about an error, try `rustc --explain E0107`. From 870a7ded02aa4628b5f9952609cd3d9272f145aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 24 Apr 2020 14:16:53 -0700 Subject: [PATCH 2/6] Custom error for const expression parsed as trait object --- src/librustc_ast_lowering/lib.rs | 67 ++++++++++++++++++- src/librustc_middle/ty/sty.rs | 1 + src/librustc_resolve/late.rs | 67 +++++++++++++------ .../const-expression-missing-braces.rs | 6 +- .../const-expression-missing-braces.stderr | 37 ++-------- 5 files changed, 123 insertions(+), 55 deletions(-) diff --git a/src/librustc_ast_lowering/lib.rs b/src/librustc_ast_lowering/lib.rs index 47dd757823be2..18582d8012c1b 100644 --- a/src/librustc_ast_lowering/lib.rs +++ b/src/librustc_ast_lowering/lib.rs @@ -49,7 +49,7 @@ use rustc_ast_pretty::pprust; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; -use rustc_errors::struct_span_err; +use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX}; @@ -1136,6 +1136,71 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } } } + // Possible `a + b` expression that should be surrounded in braces but was parsed + // as trait bounds in a trait object. Suggest surrounding with braces. + if let TyKind::TraitObject(ref bounds, TraitObjectSyntax::None) = ty.kind { + // We cannot disambiguate multi-segment paths right now as that requires type + // checking. + let const_expr_without_braces = bounds.iter().all(|bound| match bound { + GenericBound::Trait( + PolyTraitRef { + bound_generic_params, + trait_ref: TraitRef { path, .. }, + .. + }, + TraitBoundModifier::None, + ) if bound_generic_params.is_empty() + && path.segments.len() == 1 + && path.segments[0].args.is_none() => + { + let part_res = self.resolver.get_partial_res(path.segments[0].id); + match part_res.map(|r| r.base_res()) { + Some(res) => { + !res.matches_ns(Namespace::TypeNS) + && res.matches_ns(Namespace::ValueNS) + } + None => true, + } + } + _ => false, + }); + if const_expr_without_braces { + self.sess.struct_span_err(ty.span, "likely `const` expression parsed as trait bounds") + .span_label(ty.span, "parsed as trait bounds but traits weren't found") + .multipart_suggestion( + "if you meant to write a `const` expression, surround the expression with braces", + vec![ + (ty.span.shrink_to_lo(), "{ ".to_string()), + (ty.span.shrink_to_hi(), " }".to_string()), + ], + Applicability::MachineApplicable, + ) + .emit(); + + let parent_def_id = self.current_hir_id_owner.last().unwrap().0; + let node_id = self.resolver.next_node_id(); + // Add a definition for the in-band const def. + self.resolver.definitions().create_def_with_parent( + parent_def_id, + node_id, + DefPathData::AnonConst, + ExpnId::root(), + ty.span, + ); + + let path_expr = Expr { + id: ty.id, + kind: ExprKind::Err, + span: ty.span, + attrs: AttrVec::new(), + }; + let value = self.with_new_scopes(|this| hir::AnonConst { + hir_id: this.lower_node_id(node_id), + body: this.lower_const_body(path_expr.span, Some(&path_expr)), + }); + return GenericArg::Const(ConstArg { value, span: ty.span }); + } + } GenericArg::Type(self.lower_ty_direct(&ty, itctx)) } ast::GenericArg::Const(ct) => GenericArg::Const(ConstArg { diff --git a/src/librustc_middle/ty/sty.rs b/src/librustc_middle/ty/sty.rs index ac5477edcc3c0..84c1f9acc8395 100644 --- a/src/librustc_middle/ty/sty.rs +++ b/src/librustc_middle/ty/sty.rs @@ -2275,6 +2275,7 @@ impl<'tcx> Const<'tcx> { let name = tcx.hir().name(hir_id); ty::ConstKind::Param(ty::ParamConst::new(index, name)) } + ExprKind::Err => ty::ConstKind::Error, _ => ty::ConstKind::Unevaluated( def_id.to_def_id(), InternalSubsts::identity_for_item(tcx, def_id.to_def_id()), diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs index f369e827a402b..32b4b873cbd9f 100644 --- a/src/librustc_resolve/late.rs +++ b/src/librustc_resolve/late.rs @@ -558,25 +558,22 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { let prev = replace(&mut self.diagnostic_metadata.currently_processing_generics, true); match arg { GenericArg::Type(ref ty) => { - // We parse const arguments as path types as we cannot distinguish them during - // parsing. We try to resolve that ambiguity by attempting resolution the type - // namespace first, and if that fails we try again in the value namespace. If - // resolution in the value namespace succeeds, we have an generic const argument on - // our hands. - if let TyKind::Path(ref qself, ref path) = ty.kind { - // We cannot disambiguate multi-segment paths right now as that requires type - // checking. - if path.segments.len() == 1 && path.segments[0].args.is_none() { - let mut check_ns = |ns| { - self.resolve_ident_in_lexical_scope( - path.segments[0].ident, - ns, - None, - path.span, - ) - .is_some() - }; - if !check_ns(TypeNS) && check_ns(ValueNS) { + let mut check_ns = |path: &Path, ns| { + self.resolve_ident_in_lexical_scope(path.segments[0].ident, ns, None, path.span) + .is_some() + && path.segments.len() == 1 + && path.segments[0].args.is_none() + }; + match ty.kind { + // We parse const arguments as path types as we cannot distinguish them during + // parsing. We try to resolve that ambiguity by attempting resolution the type + // namespace first, and if that fails we try again in the value namespace. If + // resolution in the value namespace succeeds, we have an generic const argument + // on our hands. + TyKind::Path(ref qself, ref path) => { + // We cannot disambiguate multi-segment paths right now as that requires type + // checking. + if !check_ns(path, TypeNS) && check_ns(path, ValueNS) { // This must be equivalent to `visit_anon_const`, but we cannot call it // directly due to visitor lifetimes so we have to copy-paste some code. self.with_constant_rib(|this| { @@ -597,6 +594,38 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> { return; } } + + // Possible `a + b` expression that should be surrounded in braces but was + // parsed as trait bounds in a trait object. Suggest surrounding with braces. + TyKind::TraitObject(ref bounds, TraitObjectSyntax::None) => { + // We cannot disambiguate multi-segment paths right now as that requires + // type checking. + let const_expr_without_braces = bounds.iter().all(|bound| match bound { + GenericBound::Trait( + PolyTraitRef { + bound_generic_params, + trait_ref: TraitRef { path, .. }, + .. + }, + TraitBoundModifier::None, + ) if bound_generic_params.is_empty() => { + !check_ns(path, TypeNS) && check_ns(path, ValueNS) + } + _ => false, + }); + if const_expr_without_braces { + // This will be handled and emit an appropriate error in + // `rustc_ast_lowering::LoweringContext::lower_generic_arg`. We do not + // `visit_ty` in this case to avoid extra unnecessary output. + self.r.session.delay_span_bug( + ty.span, + "`const` expression parsed as trait bounds", + ); + self.diagnostic_metadata.currently_processing_generics = prev; + return; + } + } + _ => {} } self.visit_ty(ty); diff --git a/src/test/ui/const-generics/const-expression-missing-braces.rs b/src/test/ui/const-generics/const-expression-missing-braces.rs index d9a7054f6c4a4..60cf9472d1ab0 100644 --- a/src/test/ui/const-generics/const-expression-missing-braces.rs +++ b/src/test/ui/const-generics/const-expression-missing-braces.rs @@ -11,11 +11,7 @@ fn a() { fn b() { let bar = 3; foo::(); - //~^ ERROR expected trait, found local variable `bar` - //~| ERROR expected trait, found local variable `bar` - //~| ERROR wrong number of const arguments: expected 1, found 0 - //~| ERROR wrong number of type arguments: expected 0, found 1 - //~| WARNING trait objects without an explicit `dyn` are deprecated + //~^ ERROR likely `const` expression parsed as trait bounds } fn c() { let bar = 3; diff --git a/src/test/ui/const-generics/const-expression-missing-braces.stderr b/src/test/ui/const-generics/const-expression-missing-braces.stderr index 3e03053f21ed8..c1dcc865b4326 100644 --- a/src/test/ui/const-generics/const-expression-missing-braces.stderr +++ b/src/test/ui/const-generics/const-expression-missing-braces.stderr @@ -5,44 +5,21 @@ LL | foo::(); | ^ expected one of 8 possible tokens error: expected one of `,` or `>`, found `+` - --> $DIR/const-expression-missing-braces.rs:22:13 + --> $DIR/const-expression-missing-braces.rs:18:13 | LL | foo::<3 + 3>(); | ^ expected one of `,` or `>` -error[E0404]: expected trait, found local variable `bar` +error: likely `const` expression parsed as trait bounds --> $DIR/const-expression-missing-braces.rs:13:11 | LL | foo::(); - | ^^^ not a trait - -error[E0404]: expected trait, found local variable `bar` - --> $DIR/const-expression-missing-braces.rs:13:17 + | ^^^^^^^^^ parsed as trait bounds but traits weren't found | -LL | foo::(); - | ^^^ not a trait - -warning: trait objects without an explicit `dyn` are deprecated - --> $DIR/const-expression-missing-braces.rs:13:11 +help: if you meant to write a `const` expression, surround the expression with braces | -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-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-missing-braces.rs:13:11 - | -LL | foo::(); - | ^^^^^^^^^ unexpected type argument +LL | foo::<{ bar + bar }>(); + | ^ ^ -error: aborting due to 6 previous errors; 1 warning emitted +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0107, E0404. -For more information about an error, try `rustc --explain E0107`. From 892eac14efea14555d4994221e5269f3afe7d903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 24 Apr 2020 16:27:32 -0700 Subject: [PATCH 3/6] 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. --- src/librustc_parse/parser/expr.rs | 10 ++++++- src/librustc_parse/parser/mod.rs | 1 + src/librustc_parse/parser/path.rs | 28 +++++++++---------- .../const-expression-missing-braces.rs | 3 +- .../const-expression-missing-braces.stderr | 8 +----- .../const-expression-parameter.rs | 5 ++-- .../const-expression-parameter.stderr | 16 ----------- .../disallowed-positions.rs | 8 +++--- .../disallowed-positions.stderr | 26 ++++++++++++----- 9 files changed, 51 insertions(+), 54 deletions(-) delete mode 100644 src/test/ui/const-generics/const-expression-parameter.stderr diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index 3f08fb79790fb..c15f9055e0985 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -331,6 +331,12 @@ 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 `<` and `>`. + (Some(AssocOp::ShiftRight), _) | (Some(AssocOp::Greater), _) + 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"); @@ -1585,7 +1591,9 @@ 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| { + // `self.restrictions |` below is to account for `CONST_EXPR` so that we can correctly + // parse `let` expressions in `const` arguments. + 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/src/librustc_parse/parser/mod.rs b/src/librustc_parse/parser/mod.rs index 9264fc8a73518..9dc1159b681ea 100644 --- a/src/librustc_parse/parser/mod.rs +++ b/src/librustc_parse/parser/mod.rs @@ -34,6 +34,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/src/librustc_parse/parser/path.rs b/src/librustc_parse/parser/path.rs index 9fa7bc027b878..99eda643bca40 100644 --- a/src/librustc_parse/parser/path.rs +++ b/src/librustc_parse/parser/path.rs @@ -1,5 +1,5 @@ use super::ty::{AllowPlus, RecoverQPath}; -use super::{Parser, TokenType}; +use super::{Parser, Restrictions, TokenType}; use crate::maybe_whole; use rustc_ast::ast::{self, AngleBracketedArg, AngleBracketedArgs, GenericArg, ParenthesizedArgs}; use rustc_ast::ast::{AnonConst, AssocTyConstraint, AssocTyConstraintKind, BlockCheckMode}; @@ -479,36 +479,34 @@ impl<'a> Parser<'a> { 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() { + } else { // 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()? + let start = self.token.span; + self.parse_expr_res(Restrictions::CONST_EXPR, None).map_err(|mut err| { + err.span_label( + start.shrink_to_lo(), + "while parsing a `const` argument starting here", + ); + err + })? }; - 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()?) } else { return Ok(None); }; + // FIXME: recover from const expressions without braces that require them. Ok(Some(arg)) } } diff --git a/src/test/ui/const-generics/const-expression-missing-braces.rs b/src/test/ui/const-generics/const-expression-missing-braces.rs index 60cf9472d1ab0..deb7ce683bb0c 100644 --- a/src/test/ui/const-generics/const-expression-missing-braces.rs +++ b/src/test/ui/const-generics/const-expression-missing-braces.rs @@ -15,8 +15,7 @@ fn b() { } fn c() { let bar = 3; - foo::<3 + 3>(); - //~^ ERROR expected one of `,` or `>`, found `+` + foo::<3 + 3>(); // ok } fn main() {} diff --git a/src/test/ui/const-generics/const-expression-missing-braces.stderr b/src/test/ui/const-generics/const-expression-missing-braces.stderr index c1dcc865b4326..96d63e812fb1d 100644 --- a/src/test/ui/const-generics/const-expression-missing-braces.stderr +++ b/src/test/ui/const-generics/const-expression-missing-braces.stderr @@ -4,12 +4,6 @@ error: expected one of `!`, `(`, `,`, `>`, `?`, `for`, lifetime, or path, found LL | foo::(); | ^ expected one of 8 possible tokens -error: expected one of `,` or `>`, found `+` - --> $DIR/const-expression-missing-braces.rs:18:13 - | -LL | foo::<3 + 3>(); - | ^ expected one of `,` or `>` - error: likely `const` expression parsed as trait bounds --> $DIR/const-expression-missing-braces.rs:13:11 | @@ -21,5 +15,5 @@ help: if you meant to write a `const` expression, surround the expression with b LL | foo::<{ bar + bar }>(); | ^ ^ -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors diff --git a/src/test/ui/const-generics/const-expression-parameter.rs b/src/test/ui/const-generics/const-expression-parameter.rs index 22c6c35162281..c923c4f6bb171 100644 --- a/src/test/ui/const-generics/const-expression-parameter.rs +++ b/src/test/ui/const-generics/const-expression-parameter.rs @@ -1,5 +1,6 @@ +// check-pass +#![allow(incomplete_features)] #![feature(const_generics)] -//~^ WARN the feature `const_generics` is incomplete and may cause the compiler to crash fn i32_identity() -> i32 { 5 @@ -10,7 +11,7 @@ fn foo_a() { } fn foo_b() { - i32_identity::<1 + 2>(); //~ ERROR expected one of `,` or `>`, found `+` + i32_identity::<1 + 2>(); // ok } fn foo_c() { diff --git a/src/test/ui/const-generics/const-expression-parameter.stderr b/src/test/ui/const-generics/const-expression-parameter.stderr deleted file mode 100644 index 6784aeebf0fec..0000000000000 --- a/src/test/ui/const-generics/const-expression-parameter.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error: expected one of `,` or `>`, found `+` - --> $DIR/const-expression-parameter.rs:13:22 - | -LL | i32_identity::<1 + 2>(); - | ^ expected one of `,` or `>` - -warning: the feature `const_generics` is incomplete and may cause the compiler to crash - --> $DIR/const-expression-parameter.rs:1:12 - | -LL | #![feature(const_generics)] - | ^^^^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - -error: aborting due to previous error; 1 warning emitted - 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 d5756737f1791..f217ed33240fe 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 @@ -232,10 +232,10 @@ fn inside_const_generic_arguments() { // In the cases above we have `ExprKind::Block` to help us out. // Below however, we would not have a block and so an implementation might go // from visiting expressions to types without banning `let` expressions down the tree. - // This tests ensures that we are not caught by surprise should the parser - // admit non-IDENT expressions in const generic arguments. - + // The parser admits non-IDENT expressions in const generic arguments and is caught by the + // test below. if A::< - true && let 1 = 1 //~ ERROR expected one of `,` or `>`, found `&&` + true && let 1 = 1 //~ ERROR `let` expressions are not supported here + //~^ ERROR `match` is not allowed in a `const` >::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 4f11c306f5036..ae18e5c0b5549 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,9 +1,3 @@ -error: expected one of `,` or `>`, found `&&` - --> $DIR/disallowed-positions.rs:239:14 - | -LL | true && let 1 = 1 - | ^^ expected one of `,` or `>` - error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:32:9 | @@ -499,6 +493,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:238: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 cause the compiler to crash --> $DIR/disallowed-positions.rs:20:12 | @@ -540,6 +543,15 @@ LL | true && let 1 = 1 = note: see issue #49146 for more information = help: add `#![feature(const_if_match)]` to the crate attributes to enable +error[E0658]: `match` is not allowed in a `const` + --> $DIR/disallowed-positions.rs:238:17 + | +LL | true && let 1 = 1 + | ^^^^^^^^^ + | + = note: see issue #49146 for more information + = help: add `#![feature(const_if_match)]` to the crate attributes to enable + error[E0308]: mismatched types --> $DIR/disallowed-positions.rs:32:8 | @@ -980,7 +992,7 @@ LL | let 0 = 0?; = help: the trait `std::ops::Try` is not implemented for `{integer}` = note: required by `std::ops::Try::into_result` -error: aborting due to 106 previous errors; 2 warnings emitted +error: aborting due to 107 previous errors; 2 warnings emitted Some errors have detailed explanations: E0277, E0308, E0600, E0614, E0658. For more information about an error, try `rustc --explain E0277`. From 00aeabcdd1ee75493d081d8f1707b4050ebbd1e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 26 Apr 2020 10:49:12 -0700 Subject: [PATCH 4/6] Recover from some const expr parse errors --- src/librustc_ast/ast.rs | 9 ++ src/librustc_ast/token.rs | 7 ++ src/librustc_parse/parser/expr.rs | 16 ++- src/librustc_parse/parser/path.rs | 110 +++++++++++++++++- .../const-expression-missing-braces.rs | 23 ++-- .../const-expression-missing-braces.stderr | 41 +++++-- .../parser/removed-syntax-closure-lifetime.rs | 2 +- .../disallowed-positions.rs | 2 +- 8 files changed, 187 insertions(+), 23 deletions(-) diff --git a/src/librustc_ast/ast.rs b/src/librustc_ast/ast.rs index 14181e440e96a..9599d7848c74a 100644 --- a/src/librustc_ast/ast.rs +++ b/src/librustc_ast/ast.rs @@ -228,6 +228,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/src/librustc_ast/token.rs b/src/librustc_ast/token.rs index be5d322ba1677..5e09f4930084f 100644 --- a/src/librustc_ast/token.rs +++ b/src/librustc_ast/token.rs @@ -313,6 +313,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/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index c15f9055e0985..4ab1481ad183e 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -329,12 +329,18 @@ impl<'a> Parser<'a> { /// The method does not advance the current token. /// /// Also performs recovery for `and` / `or` which are mistaken for `&&` and `||` respectively. - fn check_assoc_op(&self) -> Option> { + crate 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 `<` and `>`. - (Some(AssocOp::ShiftRight), _) | (Some(AssocOp::Greater), _) - if self.restrictions.contains(Restrictions::CONST_EXPR) => - { + // 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), diff --git a/src/librustc_parse/parser/path.rs b/src/librustc_parse/parser/path.rs index 99eda643bca40..ee873df05ab7b 100644 --- a/src/librustc_parse/parser/path.rs +++ b/src/librustc_parse/parser/path.rs @@ -6,7 +6,8 @@ use rustc_ast::ast::{AnonConst, AssocTyConstraint, AssocTyConstraintKind, BlockC use rustc_ast::ast::{Ident, Path, PathSegment, QSelf}; use rustc_ast::ptr::P; use rustc_ast::token::{self, Token}; -use rustc_errors::{pluralize, Applicability, PResult}; +use rustc_ast::util::parser::AssocOp; +use rustc_errors::{pluralize, Applicability, DiagnosticBuilder, PResult}; use rustc_span::source_map::{BytePos, Span}; use rustc_span::symbol::{kw, sym}; @@ -392,12 +393,110 @@ 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() { + // We will correctly parse a closing `>`, exit. + } else { + // Try to recover from possible `const` arg without braces. + 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) { + continue; + } + } + Err(mut err) => { + args.push(arg); + // We will emit a more generic error later. + err.delay_as_bug(); + } + } + } break; } } Ok(args) } + /// Try to recover from possible `const` arg without braces. + /// + /// 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 + /// when we have a high degree of certainty 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 + | AssocOp::Assign // Don't recover from `foo::` + | AssocOp::AssignOp(_) = op + { + None + } else { + Some(op) + } + }) + .is_some(); + // This will be true when a trait object type `Foo +` has been parsed. + let was_op = self.prev_token.kind == token::BinOp(token::Plus); + 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: to write a `const` expression, surround it with braces for it to + // be unambiguous + // | + // LL | let sr: Vec<{ (u32, _, _) = vec![] }; + // | ^ ^ + err.multipart_suggestion( + "to write a `const` expression, surround it with braces for it to be \ + unambiguous", + 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) + } + /// Parses a single argument in the angle arguments `<...>` of a path segment. fn parse_angle_arg(&mut self) -> PResult<'a, Option> { if self.check_ident() && self.look_ahead(1, |t| matches!(t.kind, token::Eq | token::Colon)) @@ -474,6 +573,7 @@ impl<'a> Parser<'a> { /// 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()) @@ -502,7 +602,13 @@ impl<'a> Parser<'a> { 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/const-expression-missing-braces.rs b/src/test/ui/const-generics/const-expression-missing-braces.rs index deb7ce683bb0c..b6b9cdd87c5f9 100644 --- a/src/test/ui/const-generics/const-expression-missing-braces.rs +++ b/src/test/ui/const-generics/const-expression-missing-braces.rs @@ -3,19 +3,28 @@ fn foo() {} +const BAR: usize = 42; + fn a() { - let bar = 3; - foo::(); - //~^ ERROR expected one of `!`, `(`, `,`, `>`, `?`, `for`, lifetime, or path, found `3` + foo::(); + //~^ ERROR expected one of } fn b() { - let bar = 3; - foo::(); + foo::(); //~^ ERROR likely `const` expression parsed as trait bounds } fn c() { - let bar = 3; foo::<3 + 3>(); // ok } - +fn d() { + foo::(); + //~^ ERROR expected one of +} +fn e() { + foo::(); + //~^ ERROR expected one of +} +fn f() { + foo::<100 - BAR>(); // ok +} fn main() {} diff --git a/src/test/ui/const-generics/const-expression-missing-braces.stderr b/src/test/ui/const-generics/const-expression-missing-braces.stderr index 96d63e812fb1d..fb8b0268b7b6b 100644 --- a/src/test/ui/const-generics/const-expression-missing-braces.stderr +++ b/src/test/ui/const-generics/const-expression-missing-braces.stderr @@ -1,19 +1,46 @@ -error: expected one of `!`, `(`, `,`, `>`, `?`, `for`, lifetime, or path, found `3` - --> $DIR/const-expression-missing-braces.rs:8:17 +error: expected one of `,` or `>`, found `3` + --> $DIR/const-expression-missing-braces.rs:9:17 | -LL | foo::(); - | ^ expected one of 8 possible tokens +LL | foo::(); + | ^ expected one of `,` or `>` + | +help: to write a `const` expression, surround it with braces for it to be unambiguous + | +LL | foo::<{ BAR + 3 }>(); + | ^ ^ + +error: expected one of `,` or `>`, found `-` + --> $DIR/const-expression-missing-braces.rs:20:15 + | +LL | foo::(); + | ^ expected one of `,` or `>` + | +help: to write a `const` expression, surround it with braces for it to be unambiguous + | +LL | foo::<{ BAR - 3 }>(); + | ^ ^ + +error: expected one of `,` or `>`, found `-` + --> $DIR/const-expression-missing-braces.rs:24:15 + | +LL | foo::(); + | ^ expected one of `,` or `>` + | +help: to write a `const` expression, surround it with braces for it to be unambiguous + | +LL | foo::<{ BAR - BAR }>(); + | ^ ^ error: likely `const` expression parsed as trait bounds --> $DIR/const-expression-missing-braces.rs:13:11 | -LL | foo::(); +LL | foo::(); | ^^^^^^^^^ parsed as trait bounds but traits weren't found | help: if you meant to write a `const` expression, surround the expression with braces | -LL | foo::<{ bar + bar }>(); +LL | foo::<{ BAR + BAR }>(); | ^ ^ -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors diff --git a/src/test/ui/parser/removed-syntax-closure-lifetime.rs b/src/test/ui/parser/removed-syntax-closure-lifetime.rs index ceac94080062d..969c49feb8905 100644 --- a/src/test/ui/parser/removed-syntax-closure-lifetime.rs +++ b/src/test/ui/parser/removed-syntax-closure-lifetime.rs @@ -1,2 +1,2 @@ type closure = Box; -//~^ ERROR expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `/` +//~^ ERROR expected one of 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 f217ed33240fe..f1544d8b6af84 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 @@ -232,7 +232,7 @@ fn inside_const_generic_arguments() { // In the cases above we have `ExprKind::Block` to help us out. // Below however, we would not have a block and so an implementation might go // from visiting expressions to types without banning `let` expressions down the tree. - // The parser admits non-IDENT expressions in const generic arguments and is caught by the + // The parser admits non-IDENT expressions in const generic arguments and is caught by the // test below. if A::< true && let 1 = 1 //~ ERROR `let` expressions are not supported here From 200034afadcf150127833184e6569b3ef06b11ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 26 Apr 2020 11:00:41 -0700 Subject: [PATCH 5/6] Account for `const fn` with type params calls in const expr recovery --- src/librustc_parse/parser/path.rs | 6 +- .../const-expression-missing-braces.rs | 23 +++++++ .../const-expression-missing-braces.stderr | 68 ++++++++++++++++++- 3 files changed, 94 insertions(+), 3 deletions(-) diff --git a/src/librustc_parse/parser/path.rs b/src/librustc_parse/parser/path.rs index ee873df05ab7b..d8477ca01507d 100644 --- a/src/librustc_parse/parser/path.rs +++ b/src/librustc_parse/parser/path.rs @@ -456,8 +456,10 @@ impl<'a> Parser<'a> { } }) .is_some(); - // This will be true when a trait object type `Foo +` has been parsed. - let was_op = self.prev_token.kind == token::BinOp(token::Plus); + // 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); diff --git a/src/test/ui/const-generics/const-expression-missing-braces.rs b/src/test/ui/const-generics/const-expression-missing-braces.rs index b6b9cdd87c5f9..36373532bf38d 100644 --- a/src/test/ui/const-generics/const-expression-missing-braces.rs +++ b/src/test/ui/const-generics/const-expression-missing-braces.rs @@ -27,4 +27,27 @@ fn e() { fn f() { foo::<100 - BAR>(); // ok } +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/const-expression-missing-braces.stderr b/src/test/ui/const-generics/const-expression-missing-braces.stderr index fb8b0268b7b6b..61b080c9716d0 100644 --- a/src/test/ui/const-generics/const-expression-missing-braces.stderr +++ b/src/test/ui/const-generics/const-expression-missing-braces.stderr @@ -31,6 +31,72 @@ help: to write a `const` expression, surround it with braces for it to be unambi LL | foo::<{ BAR - BAR }>(); | ^ ^ +error: expected one of `,` or `>`, found `(` + --> $DIR/const-expression-missing-braces.rs:31:19 + | +LL | foo::()>(); + | ^ expected one of `,` or `>` + | +help: to write a `const` expression, surround it with braces for it to be unambiguous + | +LL | foo::<{ bar() }>(); + | ^ ^ + +error: expected one of `,` or `>`, found `(` + --> $DIR/const-expression-missing-braces.rs:34:21 + | +LL | foo::()>(); + | ^ expected one of `,` or `>` + | +help: to write a `const` expression, surround it with braces for it to be unambiguous + | +LL | foo::<{ bar::() }>(); + | ^ ^ + +error: expected one of `,` or `>`, found `(` + --> $DIR/const-expression-missing-braces.rs:37:21 + | +LL | foo::() + BAR>(); + | ^ expected one of `,` or `>` + | +help: to write a `const` expression, surround it with braces for it to be unambiguous + | +LL | foo::<{ bar::() + BAR }>(); + | ^ ^ + +error: expected one of `,` or `>`, found `(` + --> $DIR/const-expression-missing-braces.rs:40:21 + | +LL | foo::() - BAR>(); + | ^ expected one of `,` or `>` + | +help: to write a `const` expression, surround it with braces for it to be unambiguous + | +LL | foo::<{ bar::() - BAR }>(); + | ^ ^ + +error: expected one of `,` or `>`, found `-` + --> $DIR/const-expression-missing-braces.rs:43:15 + | +LL | foo::()>(); + | ^ expected one of `,` or `>` + | +help: to write a `const` expression, surround it with braces for it to be unambiguous + | +LL | foo::<{ BAR - bar::() }>(); + | ^ ^ + +error: expected one of `,` or `>`, found `-` + --> $DIR/const-expression-missing-braces.rs:46:15 + | +LL | foo::()>(); + | ^ expected one of `,` or `>` + | +help: to write a `const` expression, surround it with braces for it to be unambiguous + | +LL | foo::<{ BAR - bar::() }>(); + | ^ ^ + error: likely `const` expression parsed as trait bounds --> $DIR/const-expression-missing-braces.rs:13:11 | @@ -42,5 +108,5 @@ help: if you meant to write a `const` expression, surround the expression with b LL | foo::<{ BAR + BAR }>(); | ^ ^ -error: aborting due to 4 previous errors +error: aborting due to 10 previous errors From 9a5a4b0814eb3bb6feabae4ad81915cf43ef3521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 29 Apr 2020 11:26:27 -0700 Subject: [PATCH 6/6] Deny bare `const` expressions --- src/librustc_parse/parser/path.rs | 40 ++++++++++++++--- .../const-expression-missing-braces.rs | 16 +++---- .../const-expression-missing-braces.stderr | 44 ++++++++++++++----- .../const-expression-parameter.rs | 3 +- .../const-expression-parameter.stderr | 14 ++++++ .../disallowed-positions.rs | 1 + .../disallowed-positions.stderr | 14 +++++- 7 files changed, 102 insertions(+), 30 deletions(-) create mode 100644 src/test/ui/const-generics/const-expression-parameter.stderr diff --git a/src/librustc_parse/parser/path.rs b/src/librustc_parse/parser/path.rs index d8477ca01507d..52bbf13c8aa43 100644 --- a/src/librustc_parse/parser/path.rs +++ b/src/librustc_parse/parser/path.rs @@ -593,13 +593,39 @@ impl<'a> Parser<'a> { // 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. let start = self.token.span; - self.parse_expr_res(Restrictions::CONST_EXPR, None).map_err(|mut err| { - err.span_label( - start.shrink_to_lo(), - "while parsing a `const` argument starting here", - ); - err - })? + 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` argument starting here", + ); + err + })?; + let valid_const = 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, + }; + if !valid_const { + self.struct_span_err( + expr.span, + "`const` generic expressions without braces are not supported", + ) + .note("only literals are supported as `const` generic without braces") + .multipart_suggestion( + "surround `const` expressions with braces", + vec![ + (expr.span.shrink_to_lo(), "{ ".to_string()), + (expr.span.shrink_to_hi(), " }".to_string()), + ], + Applicability::MachineApplicable, + ) + .emit(); + } + expr }; GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }) } else if self.check_type() { diff --git a/src/test/ui/const-generics/const-expression-missing-braces.rs b/src/test/ui/const-generics/const-expression-missing-braces.rs index 36373532bf38d..2e79182c67f0b 100644 --- a/src/test/ui/const-generics/const-expression-missing-braces.rs +++ b/src/test/ui/const-generics/const-expression-missing-braces.rs @@ -6,26 +6,22 @@ fn foo() {} const BAR: usize = 42; fn a() { - foo::(); - //~^ ERROR expected one of + foo::(); //~ ERROR expected one of } fn b() { - foo::(); - //~^ ERROR likely `const` expression parsed as trait bounds + foo::(); //~ ERROR likely `const` expression parsed as trait bounds } fn c() { - foo::<3 + 3>(); // ok + foo::<3 + 3>(); //~ ERROR `const` generic expressions without braces are not supported } fn d() { - foo::(); - //~^ ERROR expected one of + foo::(); //~ ERROR expected one of } fn e() { - foo::(); - //~^ ERROR expected one of + foo::(); //~ ERROR expected one of } fn f() { - foo::<100 - BAR>(); // ok + foo::<100 - BAR>(); //~ ERROR `const` generic expressions without braces are not supported } fn g() { foo::()>(); //~ ERROR expected one of diff --git a/src/test/ui/const-generics/const-expression-missing-braces.stderr b/src/test/ui/const-generics/const-expression-missing-braces.stderr index 61b080c9716d0..89539b3f1be6f 100644 --- a/src/test/ui/const-generics/const-expression-missing-braces.stderr +++ b/src/test/ui/const-generics/const-expression-missing-braces.stderr @@ -9,8 +9,20 @@ help: to write a `const` expression, surround it with braces for it to be unambi LL | foo::<{ BAR + 3 }>(); | ^ ^ +error: `const` generic expressions without braces are not supported + --> $DIR/const-expression-missing-braces.rs:15:11 + | +LL | foo::<3 + 3>(); + | ^^^^^ + | + = note: only literals are supported as `const` generic without braces +help: surround `const` expressions with braces + | +LL | foo::<{ 3 + 3 }>(); + | ^ ^ + error: expected one of `,` or `>`, found `-` - --> $DIR/const-expression-missing-braces.rs:20:15 + --> $DIR/const-expression-missing-braces.rs:18:15 | LL | foo::(); | ^ expected one of `,` or `>` @@ -21,7 +33,7 @@ LL | foo::<{ BAR - 3 }>(); | ^ ^ error: expected one of `,` or `>`, found `-` - --> $DIR/const-expression-missing-braces.rs:24:15 + --> $DIR/const-expression-missing-braces.rs:21:15 | LL | foo::(); | ^ expected one of `,` or `>` @@ -31,8 +43,20 @@ help: to write a `const` expression, surround it with braces for it to be unambi LL | foo::<{ BAR - BAR }>(); | ^ ^ +error: `const` generic expressions without braces are not supported + --> $DIR/const-expression-missing-braces.rs:24:11 + | +LL | foo::<100 - BAR>(); + | ^^^^^^^^^ + | + = note: only literals are supported as `const` generic without braces +help: surround `const` expressions with braces + | +LL | foo::<{ 100 - BAR }>(); + | ^ ^ + error: expected one of `,` or `>`, found `(` - --> $DIR/const-expression-missing-braces.rs:31:19 + --> $DIR/const-expression-missing-braces.rs:27:19 | LL | foo::()>(); | ^ expected one of `,` or `>` @@ -43,7 +67,7 @@ LL | foo::<{ bar() }>(); | ^ ^ error: expected one of `,` or `>`, found `(` - --> $DIR/const-expression-missing-braces.rs:34:21 + --> $DIR/const-expression-missing-braces.rs:30:21 | LL | foo::()>(); | ^ expected one of `,` or `>` @@ -54,7 +78,7 @@ LL | foo::<{ bar::() }>(); | ^ ^ error: expected one of `,` or `>`, found `(` - --> $DIR/const-expression-missing-braces.rs:37:21 + --> $DIR/const-expression-missing-braces.rs:33:21 | LL | foo::() + BAR>(); | ^ expected one of `,` or `>` @@ -65,7 +89,7 @@ LL | foo::<{ bar::() + BAR }>(); | ^ ^ error: expected one of `,` or `>`, found `(` - --> $DIR/const-expression-missing-braces.rs:40:21 + --> $DIR/const-expression-missing-braces.rs:36:21 | LL | foo::() - BAR>(); | ^ expected one of `,` or `>` @@ -76,7 +100,7 @@ LL | foo::<{ bar::() - BAR }>(); | ^ ^ error: expected one of `,` or `>`, found `-` - --> $DIR/const-expression-missing-braces.rs:43:15 + --> $DIR/const-expression-missing-braces.rs:39:15 | LL | foo::()>(); | ^ expected one of `,` or `>` @@ -87,7 +111,7 @@ LL | foo::<{ BAR - bar::() }>(); | ^ ^ error: expected one of `,` or `>`, found `-` - --> $DIR/const-expression-missing-braces.rs:46:15 + --> $DIR/const-expression-missing-braces.rs:42:15 | LL | foo::()>(); | ^ expected one of `,` or `>` @@ -98,7 +122,7 @@ LL | foo::<{ BAR - bar::() }>(); | ^ ^ error: likely `const` expression parsed as trait bounds - --> $DIR/const-expression-missing-braces.rs:13:11 + --> $DIR/const-expression-missing-braces.rs:12:11 | LL | foo::(); | ^^^^^^^^^ parsed as trait bounds but traits weren't found @@ -108,5 +132,5 @@ help: if you meant to write a `const` expression, surround the expression with b LL | foo::<{ BAR + BAR }>(); | ^ ^ -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors diff --git a/src/test/ui/const-generics/const-expression-parameter.rs b/src/test/ui/const-generics/const-expression-parameter.rs index c923c4f6bb171..6999d8266169b 100644 --- a/src/test/ui/const-generics/const-expression-parameter.rs +++ b/src/test/ui/const-generics/const-expression-parameter.rs @@ -1,4 +1,3 @@ -// check-pass #![allow(incomplete_features)] #![feature(const_generics)] @@ -11,7 +10,7 @@ fn foo_a() { } fn foo_b() { - i32_identity::<1 + 2>(); // ok + i32_identity::<1 + 2>(); //~ ERROR `const` generic expressions without braces are not supported } fn foo_c() { diff --git a/src/test/ui/const-generics/const-expression-parameter.stderr b/src/test/ui/const-generics/const-expression-parameter.stderr new file mode 100644 index 0000000000000..4685b67d75a98 --- /dev/null +++ b/src/test/ui/const-generics/const-expression-parameter.stderr @@ -0,0 +1,14 @@ +error: `const` generic expressions without braces are not supported + --> $DIR/const-expression-parameter.rs:13:20 + | +LL | i32_identity::<1 + 2>(); + | ^^^^^ + | + = note: only literals are supported as `const` generic without braces +help: surround `const` expressions with braces + | +LL | i32_identity::<{ 1 + 2 }>(); + | ^ ^ + +error: aborting due to previous error + 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 f1544d8b6af84..70c7fa74098a6 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 @@ -237,5 +237,6 @@ fn inside_const_generic_arguments() { if A::< true && let 1 = 1 //~ ERROR `let` expressions are not supported here //~^ ERROR `match` is not allowed in a `const` + //~| ERROR `const` generic expressions without braces are not supported >::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 ae18e5c0b5549..25f9db0819bcb 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,3 +1,15 @@ +error: `const` generic expressions without braces are not supported + --> $DIR/disallowed-positions.rs:238:9 + | +LL | true && let 1 = 1 + | ^^^^^^^^^^^^^^^^^ + | + = note: only literals are supported as `const` generic without braces +help: surround `const` expressions with braces + | +LL | { true && let 1 = 1 } + | ^ ^ + error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:32:9 | @@ -992,7 +1004,7 @@ LL | let 0 = 0?; = help: the trait `std::ops::Try` is not implemented for `{integer}` = note: required by `std::ops::Try::into_result` -error: aborting due to 107 previous errors; 2 warnings emitted +error: aborting due to 108 previous errors; 2 warnings emitted Some errors have detailed explanations: E0277, E0308, E0600, E0614, E0658. For more information about an error, try `rustc --explain E0277`.