diff options
| author | León Orell Valerian Liehr <me@fmease.dev> | 2023-05-04 16:08:33 +0200 |
|---|---|---|
| committer | León Orell Valerian Liehr <me@fmease.dev> | 2023-07-28 22:21:33 +0200 |
| commit | afd009a8d8a8fea07cfee279817c75427b707778 (patch) | |
| tree | 86ab1a1c08087cbff8615617bc350447b8ed2e65 /compiler | |
| parent | b6dd153fbcc06b3bc936aca800af4b8376c229e4 (diff) | |
| download | rust-afd009a8d8a8fea07cfee279817c75427b707778.tar.gz rust-afd009a8d8a8fea07cfee279817c75427b707778.zip | |
Parse generic const items
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_ast/src/ast.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_ast/src/mut_visit.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_ast/src/visit.rs | 11 | ||||
| -rw-r--r-- | compiler/rustc_ast_passes/src/feature_gate.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_ast_pretty/src/pprust/state/item.rs | 35 | ||||
| -rw-r--r-- | compiler/rustc_builtin_macros/src/test.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/build.rs | 11 | ||||
| -rw-r--r-- | compiler/rustc_feature/src/active.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_parse/messages.ftl | 8 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/errors.rs | 31 | ||||
| -rw-r--r-- | compiler/rustc_parse/src/parser/item.rs | 148 | ||||
| -rw-r--r-- | compiler/rustc_span/src/symbol.rs | 1 |
12 files changed, 220 insertions, 34 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 17b73468a31..dd4321bea1b 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2947,6 +2947,7 @@ pub struct StaticItem { #[derive(Clone, Encodable, Decodable, Debug)] pub struct ConstItem { pub defaultness: Defaultness, + pub generics: Generics, pub ty: P<Ty>, pub expr: Option<P<Expr>>, } @@ -3058,6 +3059,7 @@ impl ItemKind { match self { Self::Fn(box Fn { generics, .. }) | Self::TyAlias(box TyAlias { generics, .. }) + | Self::Const(box ConstItem { generics, .. }) | Self::Enum(_, generics) | Self::Struct(_, generics) | Self::Union(_, generics) diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 53a9c9a046e..84b56efd325 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1149,10 +1149,11 @@ pub fn noop_flat_map_assoc_item<T: MutVisitor>( } fn visit_const_item<T: MutVisitor>( - ConstItem { defaultness, ty, expr }: &mut ConstItem, + ConstItem { defaultness, generics, ty, expr }: &mut ConstItem, visitor: &mut T, ) { visit_defaultness(defaultness, visitor); + visitor.visit_generics(generics); visitor.visit_ty(ty); visit_opt(expr, |expr| visitor.visit_expr(expr)); } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index d9de5b8e197..aed24e11c4e 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -308,8 +308,12 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { match &item.kind { ItemKind::ExternCrate(_) => {} ItemKind::Use(use_tree) => visitor.visit_use_tree(use_tree, item.id, false), - ItemKind::Static(box StaticItem { ty, mutability: _, expr }) - | ItemKind::Const(box ConstItem { ty, expr, .. }) => { + ItemKind::Static(box StaticItem { ty, mutability: _, expr }) => { + visitor.visit_ty(ty); + walk_list!(visitor, visit_expr, expr); + } + ItemKind::Const(box ConstItem { defaultness: _, generics, ty, expr }) => { + visitor.visit_generics(generics); visitor.visit_ty(ty); walk_list!(visitor, visit_expr, expr); } @@ -677,7 +681,8 @@ pub fn walk_assoc_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a AssocItem, visitor.visit_ident(ident); walk_list!(visitor, visit_attribute, attrs); match kind { - AssocItemKind::Const(box ConstItem { ty, expr, .. }) => { + AssocItemKind::Const(box ConstItem { defaultness: _, generics, ty, expr }) => { + visitor.visit_generics(generics); visitor.visit_ty(ty); walk_list!(visitor, visit_expr, expr); } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index c0d2e76f310..c4efad7caf2 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -569,6 +569,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(const_closures, "const closures are experimental"); gate_all!(builtin_syntax, "`builtin #` syntax is unstable"); gate_all!(explicit_tail_calls, "`become` expression is experimental"); + gate_all!(generic_const_items, "generic const items are experimental"); if !visitor.features.negative_bounds { for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 5c01b7ea70a..d27a44f1206 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -30,10 +30,15 @@ impl<'a> State<'a> { ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); } - ast::ForeignItemKind::Static(ty, mutbl, body) => { - let def = ast::Defaultness::Final; - self.print_item_const(ident, Some(*mutbl), ty, body.as_deref(), vis, def); - } + ast::ForeignItemKind::Static(ty, mutbl, body) => self.print_item_const( + ident, + Some(*mutbl), + &ast::Generics::default(), + ty, + body.as_deref(), + vis, + ast::Defaultness::Final, + ), ast::ForeignItemKind::TyAlias(box ast::TyAlias { defaultness, generics, @@ -67,6 +72,7 @@ impl<'a> State<'a> { &mut self, ident: Ident, mutbl: Option<ast::Mutability>, + generics: &ast::Generics, ty: &ast::Ty, body: Option<&ast::Expr>, vis: &ast::Visibility, @@ -82,6 +88,7 @@ impl<'a> State<'a> { }; self.word_space(leading); self.print_ident(ident); + self.print_generic_params(&generics.params); self.word_space(":"); self.print_type(ty); if body.is_some() { @@ -92,6 +99,7 @@ impl<'a> State<'a> { self.word_space("="); self.print_expr(body); } + self.print_where_clause(&generics.where_clause); self.word(";"); self.end(); // end the outer cbox } @@ -158,20 +166,21 @@ impl<'a> State<'a> { self.word(";"); } ast::ItemKind::Static(box StaticItem { ty, mutability: mutbl, expr: body }) => { - let def = ast::Defaultness::Final; self.print_item_const( item.ident, Some(*mutbl), + &ast::Generics::default(), ty, body.as_deref(), &item.vis, - def, + ast::Defaultness::Final, ); } - ast::ItemKind::Const(box ast::ConstItem { defaultness, ty, expr }) => { + ast::ItemKind::Const(box ast::ConstItem { defaultness, generics, ty, expr }) => { self.print_item_const( item.ident, None, + generics, ty, expr.as_deref(), &item.vis, @@ -515,8 +524,16 @@ impl<'a> State<'a> { ast::AssocItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); } - ast::AssocItemKind::Const(box ast::ConstItem { defaultness, ty, expr }) => { - self.print_item_const(ident, None, ty, expr.as_deref(), vis, *defaultness); + ast::AssocItemKind::Const(box ast::ConstItem { defaultness, generics, ty, expr }) => { + self.print_item_const( + ident, + None, + generics, + ty, + expr.as_deref(), + vis, + *defaultness, + ); } ast::AssocItemKind::Type(box ast::TyAlias { defaultness, diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 6bc4f6fc1fc..1580a6f6dd3 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -255,6 +255,7 @@ pub fn expand_test_or_bench( ast::ItemKind::Const( ast::ConstItem { defaultness: ast::Defaultness::Final, + generics: ast::Generics::default(), ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))), // test::TestDescAndFn { expr: Some( diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 264f30fb10a..7de46994434 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -643,7 +643,16 @@ impl<'a> ExtCtxt<'a> { span, name, AttrVec::new(), - ast::ItemKind::Const(ast::ConstItem { defaultness, ty, expr: Some(expr) }.into()), + ast::ItemKind::Const( + ast::ConstItem { + defaultness, + // FIXME(generic_const_items): Pass the generics as a parameter. + generics: ast::Generics::default(), + ty, + expr: Some(expr), + } + .into(), + ), ) } diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index c03789f500a..22380a52104 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -424,6 +424,8 @@ declare_features! ( (incomplete, generic_associated_types_extended, "1.61.0", Some(95451), None), /// Allows non-trivial generic constants which have to have wfness manually propagated to callers (incomplete, generic_const_exprs, "1.56.0", Some(76560), None), + /// Allows generic parameters and where-clauses on free & associated const items. + (incomplete, generic_const_items, "CURRENT_RUSTC_VERSION", Some(113521), None), /// Allows using `..=X` as a patterns in slices. (active, half_open_range_patterns_in_slices, "1.66.0", Some(67264), None), /// Allows `if let` guard in match arms. diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 9787d98c1a4..4f1d282fe7d 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -690,6 +690,8 @@ parse_single_colon_import_path = expected `::`, found `:` parse_single_colon_struct_type = found single colon in a struct field type path .suggestion = write a path separator here +parse_static_with_generics = static items may not have generic parameters + parse_struct_literal_body_without_path = struct literal body without path .suggestion = you might have forgotten to add the struct literal inside the block @@ -847,6 +849,12 @@ parse_visibility_not_followed_by_item = visibility `{$vis}` is not followed by a .label = the visibility .help = you likely meant to define an item, e.g., `{$vis} fn foo() {"{}"}` +parse_where_clause_before_const_body = where clauses are not allowed before const item bodies + .label = unexpected where clause + .name_label = while parsing this const item + .body_label = the item body + .suggestion = move the body before the where clause + parse_where_clause_before_tuple_struct_body = where clauses are not allowed before tuple struct bodies .label = unexpected where clause .name_label = while parsing this tuple struct diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 96e1c0e3c6d..5456a708898 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2692,3 +2692,34 @@ pub(crate) struct ExpectedBuiltinIdent { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(parse_static_with_generics)] +pub(crate) struct StaticWithGenerics { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_where_clause_before_const_body)] +pub(crate) struct WhereClauseBeforeConstBody { + #[primary_span] + #[label] + pub span: Span, + #[label(parse_name_label)] + pub name: Span, + #[label(parse_body_label)] + pub body: Span, + #[subdiagnostic] + pub sugg: Option<WhereClauseBeforeConstBodySugg>, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")] +pub(crate) struct WhereClauseBeforeConstBodySugg { + #[suggestion_part(code = "= {snippet} ")] + pub left: Span, + pub snippet: String, + #[suggestion_part(code = "")] + pub right: Span, +} diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 1470180dea7..1301ed3e388 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -226,9 +226,9 @@ impl<'a> Parser<'a> { } else if self.is_static_global() { // STATIC ITEM self.bump(); // `static` - let m = self.parse_mutability(); - let (ident, ty, expr) = self.parse_item_global(Some(m))?; - (ident, ItemKind::Static(Box::new(StaticItem { ty, mutability: m, expr }))) + let mutability = self.parse_mutability(); + let (ident, item) = self.parse_static_item(mutability)?; + (ident, ItemKind::Static(Box::new(item))) } else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) { // CONST ITEM if self.token.is_keyword(kw::Impl) { @@ -236,8 +236,16 @@ impl<'a> Parser<'a> { self.recover_const_impl(const_span, attrs, def_())? } else { self.recover_const_mut(const_span); - let (ident, ty, expr) = self.parse_item_global(None)?; - (ident, ItemKind::Const(Box::new(ConstItem { defaultness: def_(), ty, expr }))) + let (ident, generics, ty, expr) = self.parse_const_item()?; + ( + ident, + ItemKind::Const(Box::new(ConstItem { + defaultness: def_(), + generics, + ty, + expr, + })), + ) } } else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() { // TRAIT ITEM @@ -878,6 +886,7 @@ impl<'a> Parser<'a> { self.sess.emit_err(errors::AssociatedStaticItemNotAllowed { span }); AssocItemKind::Const(Box::new(ConstItem { defaultness: Defaultness::Final, + generics: Generics::default(), ty, expr, })) @@ -892,7 +901,7 @@ impl<'a> Parser<'a> { /// Parses a `type` alias with the following grammar: /// ```ebnf - /// TypeAlias = "type" Ident Generics {":" GenericBounds}? {"=" Ty}? ";" ; + /// TypeAlias = "type" Ident Generics (":" GenericBounds)? WhereClause ("=" Ty)? WhereClause ";" ; /// ``` /// The `"type"` has already been eaten. fn parse_type_alias(&mut self, defaultness: Defaultness) -> PResult<'a, ItemInfo> { @@ -1220,33 +1229,132 @@ impl<'a> Parser<'a> { Ok(impl_info) } - /// Parse `["const" | ("static" "mut"?)] $ident ":" $ty (= $expr)?` with - /// `["const" | ("static" "mut"?)]` already parsed and stored in `m`. + /// Parse a static item with the prefix `"static" "mut"?` already parsed and stored in `mutability`. /// - /// When `m` is `"const"`, `$ident` may also be `"_"`. - fn parse_item_global( - &mut self, - m: Option<Mutability>, - ) -> PResult<'a, (Ident, P<Ty>, Option<P<ast::Expr>>)> { - let id = if m.is_none() { self.parse_ident_or_underscore() } else { self.parse_ident() }?; + /// ```ebnf + /// Static = "static" "mut"? $ident ":" $ty (= $expr)? ";" ; + /// ``` + fn parse_static_item(&mut self, mutability: Mutability) -> PResult<'a, (Ident, StaticItem)> { + let ident = self.parse_ident()?; + + if self.token.kind == TokenKind::Lt && self.may_recover() { + let generics = self.parse_generics()?; + self.sess.emit_err(errors::StaticWithGenerics { span: generics.span }); + } - // Parse the type of a `const` or `static mut?` item. - // That is, the `":" $ty` fragment. + // Parse the type of a static item. That is, the `":" $ty` fragment. + // FIXME: This could maybe benefit from `.may_recover()`? let ty = match (self.eat(&token::Colon), self.check(&token::Eq) | self.check(&token::Semi)) { - // If there wasn't a `:` or the colon was followed by a `=` or `;` recover a missing type. (true, false) => self.parse_ty()?, - (colon, _) => self.recover_missing_const_type(colon, m), + // If there wasn't a `:` or the colon was followed by a `=` or `;`, recover a missing type. + (colon, _) => self.recover_missing_global_item_type(colon, Some(mutability)), + }; + + let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None }; + + self.expect_semi()?; + + Ok((ident, StaticItem { ty, mutability, expr })) + } + + /// Parse a constant item with the prefix `"const"` already parsed. + /// + /// ```ebnf + /// Const = "const" ($ident | "_") Generics ":" $ty (= $expr)? WhereClause ";" ; + /// ``` + fn parse_const_item(&mut self) -> PResult<'a, (Ident, Generics, P<Ty>, Option<P<ast::Expr>>)> { + let ident = self.parse_ident_or_underscore()?; + + let mut generics = self.parse_generics()?; + + // Check the span for emptiness instead of the list of parameters in order to correctly + // recognize and subsequently flag empty parameter lists (`<>`) as unstable. + if !generics.span.is_empty() { + self.sess.gated_spans.gate(sym::generic_const_items, generics.span); + } + + // Parse the type of a constant item. That is, the `":" $ty` fragment. + // FIXME: This could maybe benefit from `.may_recover()`? + let ty = match ( + self.eat(&token::Colon), + self.check(&token::Eq) | self.check(&token::Semi) | self.check_keyword(kw::Where), + ) { + (true, false) => self.parse_ty()?, + // If there wasn't a `:` or the colon was followed by a `=`, `;` or `where`, recover a missing type. + (colon, _) => self.recover_missing_global_item_type(colon, None), }; + // Proactively parse a where-clause to be able to provide a good error message in case we + // encounter the item body following it. + let before_where_clause = + if self.may_recover() { self.parse_where_clause()? } else { WhereClause::default() }; + let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None }; + + let after_where_clause = self.parse_where_clause()?; + + // Provide a nice error message if the user placed a where-clause before the item body. + // Users may be tempted to write such code if they are still used to the deprecated + // where-clause location on type aliases and associated types. See also #89122. + if before_where_clause.has_where_token && let Some(expr) = &expr { + self.sess.emit_err(errors::WhereClauseBeforeConstBody { + span: before_where_clause.span, + name: ident.span, + body: expr.span, + sugg: if !after_where_clause.has_where_token { + self.sess.source_map().span_to_snippet(expr.span).ok().map(|body| { + errors::WhereClauseBeforeConstBodySugg { + left: before_where_clause.span.shrink_to_lo(), + snippet: body, + right: before_where_clause.span.shrink_to_hi().to(expr.span), + } + }) + } else { + // FIXME(generic_const_items): Provide a structured suggestion to merge the first + // where-clause into the second one. + None + }, + }); + } + + // Merge the predicates of both where-clauses since either one can be relevant. + // If we didn't parse a body (which is valid for associated consts in traits) and we were + // allowed to recover, `before_where_clause` contains the predicates, otherwise they are + // in `after_where_clause`. Further, both of them might contain predicates iff two + // where-clauses were provided which is syntactically ill-formed but we want to recover from + // it and treat them as one large where-clause. + let mut predicates = before_where_clause.predicates; + predicates.extend(after_where_clause.predicates); + let where_clause = WhereClause { + has_where_token: before_where_clause.has_where_token + || after_where_clause.has_where_token, + predicates, + span: if after_where_clause.has_where_token { + after_where_clause.span + } else { + before_where_clause.span + }, + }; + + if where_clause.has_where_token { + self.sess.gated_spans.gate(sym::generic_const_items, where_clause.span); + } + + generics.where_clause = where_clause; + self.expect_semi()?; - Ok((id, ty, expr)) + + Ok((ident, generics, ty, expr)) } /// We were supposed to parse `":" $ty` but the `:` or the type was missing. /// This means that the type is missing. - fn recover_missing_const_type(&mut self, colon_present: bool, m: Option<Mutability>) -> P<Ty> { + fn recover_missing_global_item_type( + &mut self, + colon_present: bool, + m: Option<Mutability>, + ) -> P<Ty> { // Construct the error and stash it away with the hope // that typeck will later enrich the error with a type. let kind = match m { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 1b426ef2048..8122fe96a5c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -783,6 +783,7 @@ symbols! { generic_associated_types, generic_associated_types_extended, generic_const_exprs, + generic_const_items, generic_param_attrs, get_context, global_allocator, |
