Parse generic const items
This commit is contained in:
parent
b6dd153fbc
commit
afd009a8d8
12 changed files with 220 additions and 34 deletions
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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()?;
|
||||
|
||||
// Parse the type of a `const` or `static mut?` item.
|
||||
// That is, the `":" $ty` fragment.
|
||||
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 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((id, ty, expr))
|
||||
|
||||
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((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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue