Skip to content

Commit 0a8d98a

Browse files
varkoryodaldevoid
andcommitted
Parse const generics
Co-Authored-By: Gabriel Smith <[email protected]>
1 parent 8d83521 commit 0a8d98a

File tree

2 files changed

+112
-178
lines changed

2 files changed

+112
-178
lines changed

src/libsyntax/parse/parser.rs

Lines changed: 98 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ crate enum TokenType {
397397
Ident,
398398
Path,
399399
Type,
400+
Const,
400401
}
401402

402403
impl TokenType {
@@ -409,6 +410,7 @@ impl TokenType {
409410
TokenType::Ident => "identifier".to_string(),
410411
TokenType::Path => "path".to_string(),
411412
TokenType::Type => "type".to_string(),
413+
TokenType::Const => "const".to_string(),
412414
}
413415
}
414416
}
@@ -946,6 +948,19 @@ impl<'a> Parser<'a> {
946948
}
947949
}
948950

951+
fn check_const_param(&mut self) -> bool {
952+
self.check_keyword(keywords::Const)
953+
}
954+
955+
fn check_const_arg(&mut self) -> bool {
956+
if self.token.can_begin_const_arg() {
957+
true
958+
} else {
959+
self.expected_tokens.push(TokenType::Const);
960+
false
961+
}
962+
}
963+
949964
/// Expect and consume a `+`. if `+=` is seen, replace it with a `=`
950965
/// and continue. If a `+` is not seen, return false.
951966
///
@@ -5482,15 +5497,28 @@ impl<'a> Parser<'a> {
54825497
Ok((ident, TraitItemKind::Type(bounds, default), generics))
54835498
}
54845499

5500+
fn parse_const_param(&mut self, preceding_attrs: Vec<Attribute>) -> PResult<'a, GenericParam> {
5501+
self.expect_keyword(keywords::Const)?;
5502+
let ident = self.parse_ident()?;
5503+
self.expect(&token::Colon)?;
5504+
let ty = self.parse_ty()?;
5505+
5506+
Ok(GenericParam {
5507+
ident,
5508+
id: ast::DUMMY_NODE_ID,
5509+
attrs: preceding_attrs.into(),
5510+
bounds: Vec::new(),
5511+
kind: GenericParamKind::Const {
5512+
ty,
5513+
}
5514+
})
5515+
}
5516+
54855517
/// Parses (possibly empty) list of lifetime and type parameters, possibly including
54865518
/// trailing comma and erroneous trailing attributes.
54875519
crate fn parse_generic_params(&mut self) -> PResult<'a, Vec<ast::GenericParam>> {
5488-
let mut lifetimes = Vec::new();
54895520
let mut params = Vec::new();
5490-
let mut seen_ty_param: Option<Span> = None;
5491-
let mut last_comma_span = None;
5492-
let mut bad_lifetime_pos = vec![];
5493-
let mut suggestions = vec![];
5521+
let mut prev_param: Option<ParamKindOrd> = None;
54945522
loop {
54955523
let attrs = self.parse_outer_attributes()?;
54965524
if self.check_lifetime() {
@@ -5501,64 +5529,45 @@ impl<'a> Parser<'a> {
55015529
} else {
55025530
Vec::new()
55035531
};
5504-
lifetimes.push(ast::GenericParam {
5532+
params.push(ast::GenericParam {
55055533
ident: lifetime.ident,
55065534
id: lifetime.id,
55075535
attrs: attrs.into(),
55085536
bounds,
55095537
kind: ast::GenericParamKind::Lifetime,
55105538
});
5511-
if let Some(sp) = seen_ty_param {
5512-
let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span);
5513-
bad_lifetime_pos.push(self.prev_span);
5514-
if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) {
5515-
suggestions.push((remove_sp, String::new()));
5516-
suggestions.push((
5517-
sp.shrink_to_lo(),
5518-
format!("{}, ", snippet)));
5519-
}
5520-
}
5539+
prev_param = Some(ParamKindOrd::Lifetime);
5540+
} else if self.check_const_param() {
5541+
// Parse const parameter.
5542+
params.push(self.parse_const_param(attrs)?);
5543+
prev_param = Some(ParamKindOrd::Const);
55215544
} else if self.check_ident() {
55225545
// Parse type parameter.
55235546
params.push(self.parse_ty_param(attrs)?);
5524-
if seen_ty_param.is_none() {
5525-
seen_ty_param = Some(self.prev_span);
5526-
}
5547+
prev_param = Some(ParamKindOrd::Type);
55275548
} else {
55285549
// Check for trailing attributes and stop parsing.
55295550
if !attrs.is_empty() {
5530-
let param_kind = if seen_ty_param.is_some() { "type" } else { "lifetime" };
5531-
self.struct_span_err(
5532-
attrs[0].span,
5533-
&format!("trailing attribute after {} parameters", param_kind),
5534-
)
5535-
.span_label(attrs[0].span, "attributes must go before parameters")
5536-
.emit();
5551+
if let Some(prev_param) = prev_param {
5552+
self.struct_span_err(
5553+
attrs[0].span,
5554+
&format!(
5555+
"trailing attribute after {} parameter",
5556+
prev_param,
5557+
),
5558+
)
5559+
.span_label(attrs[0].span, "attributes must go before parameters")
5560+
.emit();
5561+
}
55375562
}
55385563
break
55395564
}
55405565

55415566
if !self.eat(&token::Comma) {
55425567
break
55435568
}
5544-
last_comma_span = Some(self.prev_span);
5545-
}
5546-
if !bad_lifetime_pos.is_empty() {
5547-
let mut err = self.struct_span_err(
5548-
bad_lifetime_pos,
5549-
"lifetime parameters must be declared prior to type parameters",
5550-
);
5551-
if !suggestions.is_empty() {
5552-
err.multipart_suggestion(
5553-
"move the lifetime parameter prior to the first type parameter",
5554-
suggestions,
5555-
Applicability::MachineApplicable,
5556-
);
5557-
}
5558-
err.emit();
55595569
}
5560-
lifetimes.extend(params); // ensure the correct order of lifetimes and type params
5561-
Ok(lifetimes)
5570+
Ok(params)
55625571
}
55635572

55645573
/// Parse a set of optional generic type parameter declarations. Where
@@ -5740,35 +5749,16 @@ impl<'a> Parser<'a> {
57405749
fn parse_generic_args(&mut self) -> PResult<'a, (Vec<GenericArg>, Vec<TypeBinding>)> {
57415750
let mut args = Vec::new();
57425751
let mut bindings = Vec::new();
5752+
let mut misplaced_assoc_ty_bindings: Vec<Span> = Vec::new();
5753+
let mut assoc_ty_bindings: Vec<Span> = Vec::new();
57435754

5744-
let mut seen_type = false;
5745-
let mut seen_binding = false;
5755+
let args_lo = self.span;
57465756

5747-
let mut last_comma_span = None;
5748-
let mut first_type_or_binding_span: Option<Span> = None;
5749-
let mut first_binding_span: Option<Span> = None;
5750-
5751-
let mut bad_lifetime_pos = vec![];
5752-
let mut bad_type_pos = vec![];
5753-
5754-
let mut lifetime_suggestions = vec![];
5755-
let mut type_suggestions = vec![];
57565757
loop {
57575758
if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
57585759
// Parse lifetime argument.
57595760
args.push(GenericArg::Lifetime(self.expect_lifetime()));
5760-
5761-
if seen_type || seen_binding {
5762-
let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span);
5763-
bad_lifetime_pos.push(self.prev_span);
5764-
5765-
if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) {
5766-
lifetime_suggestions.push((remove_sp, String::new()));
5767-
lifetime_suggestions.push((
5768-
first_type_or_binding_span.unwrap().shrink_to_lo(),
5769-
format!("{}, ", snippet)));
5770-
}
5771-
}
5761+
misplaced_assoc_ty_bindings.append(&mut assoc_ty_bindings);
57725762
} else if self.check_ident() && self.look_ahead(1, |t| t == &token::Eq) {
57735763
// Parse associated type binding.
57745764
let lo = self.span;
@@ -5782,131 +5772,59 @@ impl<'a> Parser<'a> {
57825772
ty,
57835773
span,
57845774
});
5785-
5786-
seen_binding = true;
5787-
if first_type_or_binding_span.is_none() {
5788-
first_type_or_binding_span = Some(span);
5789-
}
5790-
if first_binding_span.is_none() {
5791-
first_binding_span = Some(span);
5792-
}
5775+
assoc_ty_bindings.push(span);
5776+
} else if self.check_const_arg() {
5777+
// Parse const argument.
5778+
let expr = if let token::OpenDelim(token::Brace) = self.token {
5779+
self.parse_block_expr(None, self.span, BlockCheckMode::Default, ThinVec::new())?
5780+
} else if self.token.can_begin_literal_or_bool() {
5781+
let lit = self.parse_lit()?;
5782+
self.mk_expr(lit.span, ExprKind::Lit(lit), ThinVec::new())
5783+
} else {
5784+
// FIXME(const_generics): to distinguish between idents for types and consts,
5785+
// we should introduce a GenericArg::Ident in the AST and distinguish when
5786+
// lowering to the HIR. For now, idents for const args are not permitted.
5787+
return Err(
5788+
self.fatal("identifiers may currently not be used for const generics")
5789+
);
5790+
};
5791+
let value = AnonConst {
5792+
id: ast::DUMMY_NODE_ID,
5793+
value: expr,
5794+
};
5795+
args.push(GenericArg::Const(value));
5796+
misplaced_assoc_ty_bindings.append(&mut assoc_ty_bindings);
57935797
} else if self.check_type() {
57945798
// Parse type argument.
5795-
let ty_param = self.parse_ty()?;
5796-
if seen_binding {
5797-
let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span);
5798-
bad_type_pos.push(self.prev_span);
5799-
5800-
if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) {
5801-
type_suggestions.push((remove_sp, String::new()));
5802-
type_suggestions.push((
5803-
first_binding_span.unwrap().shrink_to_lo(),
5804-
format!("{}, ", snippet)));
5805-
}
5806-
}
5807-
5808-
if first_type_or_binding_span.is_none() {
5809-
first_type_or_binding_span = Some(ty_param.span);
5810-
}
5811-
args.push(GenericArg::Type(ty_param));
5812-
seen_type = true;
5799+
args.push(GenericArg::Type(self.parse_ty()?));
5800+
misplaced_assoc_ty_bindings.append(&mut assoc_ty_bindings);
58135801
} else {
58145802
break
58155803
}
58165804

58175805
if !self.eat(&token::Comma) {
58185806
break
5819-
} else {
5820-
last_comma_span = Some(self.prev_span);
5821-
}
5822-
}
5823-
5824-
self.maybe_report_incorrect_generic_argument_order(
5825-
bad_lifetime_pos, bad_type_pos, lifetime_suggestions, type_suggestions
5826-
);
5827-
5828-
Ok((args, bindings))
5829-
}
5830-
5831-
/// Maybe report an error about incorrect generic argument order - "lifetime parameters
5832-
/// must be declared before type parameters", "type parameters must be declared before
5833-
/// associated type bindings" or both.
5834-
fn maybe_report_incorrect_generic_argument_order(
5835-
&self,
5836-
bad_lifetime_pos: Vec<Span>,
5837-
bad_type_pos: Vec<Span>,
5838-
lifetime_suggestions: Vec<(Span, String)>,
5839-
type_suggestions: Vec<(Span, String)>,
5840-
) {
5841-
let mut err = if !bad_lifetime_pos.is_empty() && !bad_type_pos.is_empty() {
5842-
let mut positions = bad_lifetime_pos.clone();
5843-
positions.extend_from_slice(&bad_type_pos);
5844-
5845-
self.struct_span_err(
5846-
positions,
5847-
"generic arguments must declare lifetimes, types and associated type bindings in \
5848-
that order",
5849-
)
5850-
} else if !bad_lifetime_pos.is_empty() {
5851-
self.struct_span_err(
5852-
bad_lifetime_pos.clone(),
5853-
"lifetime parameters must be declared prior to type parameters"
5854-
)
5855-
} else if !bad_type_pos.is_empty() {
5856-
self.struct_span_err(
5857-
bad_type_pos.clone(),
5858-
"type parameters must be declared prior to associated type bindings"
5859-
)
5860-
} else {
5861-
return;
5862-
};
5863-
5864-
if !bad_lifetime_pos.is_empty() {
5865-
for sp in &bad_lifetime_pos {
5866-
err.span_label(*sp, "must be declared prior to type parameters");
5867-
}
5868-
}
5869-
5870-
if !bad_type_pos.is_empty() {
5871-
for sp in &bad_type_pos {
5872-
err.span_label(*sp, "must be declared prior to associated type bindings");
58735807
}
58745808
}
58755809

5876-
if !lifetime_suggestions.is_empty() && !type_suggestions.is_empty() {
5877-
let mut suggestions = lifetime_suggestions;
5878-
suggestions.extend_from_slice(&type_suggestions);
5879-
5880-
let plural = bad_lifetime_pos.len() + bad_type_pos.len() > 1;
5881-
err.multipart_suggestion(
5882-
&format!(
5883-
"move the parameter{}",
5884-
if plural { "s" } else { "" },
5885-
),
5886-
suggestions,
5887-
Applicability::MachineApplicable,
5888-
);
5889-
} else if !lifetime_suggestions.is_empty() {
5890-
err.multipart_suggestion(
5891-
&format!(
5892-
"move the lifetime parameter{} prior to the first type parameter",
5893-
if bad_lifetime_pos.len() > 1 { "s" } else { "" },
5894-
),
5895-
lifetime_suggestions,
5896-
Applicability::MachineApplicable,
5897-
);
5898-
} else if !type_suggestions.is_empty() {
5899-
err.multipart_suggestion(
5900-
&format!(
5901-
"move the type parameter{} prior to the first associated type binding",
5902-
if bad_type_pos.len() > 1 { "s" } else { "" },
5903-
),
5904-
type_suggestions,
5905-
Applicability::MachineApplicable,
5810+
// FIXME: we would like to report this in ast_validation instead, but we currently do not
5811+
// preserve ordering of generic parameters with respect to associated type binding, so we
5812+
// lose that information after parsing.
5813+
if misplaced_assoc_ty_bindings.len() > 0 {
5814+
let mut err = self.struct_span_err(
5815+
args_lo.to(self.prev_span),
5816+
"associated type bindings must be declared after generic parameters",
59065817
);
5818+
for span in misplaced_assoc_ty_bindings {
5819+
err.span_label(
5820+
span,
5821+
"this associated type binding should be moved after the generic parameters",
5822+
);
5823+
}
5824+
err.emit();
59075825
}
59085826

5909-
err.emit();
5827+
Ok((args, bindings))
59105828
}
59115829

59125830
/// Parses an optional `where` clause and places it in `generics`.
@@ -6526,6 +6444,7 @@ impl<'a> Parser<'a> {
65266444
// `<` (LIFETIME|IDENT) `,` - first generic parameter in a list
65276445
// `<` (LIFETIME|IDENT) `:` - generic parameter with bounds
65286446
// `<` (LIFETIME|IDENT) `=` - generic parameter with a default
6447+
// `<` const IDENT - generic const parameter
65296448
// The only truly ambiguous case is
65306449
// `<` IDENT `>` `::` IDENT ...
65316450
// we disambiguate it in favor of generics (`impl<T> ::absolute::Path<T> { ... }`)
@@ -6535,7 +6454,8 @@ impl<'a> Parser<'a> {
65356454
(self.look_ahead(1, |t| t == &token::Pound || t == &token::Gt) ||
65366455
self.look_ahead(1, |t| t.is_lifetime() || t.is_ident()) &&
65376456
self.look_ahead(2, |t| t == &token::Gt || t == &token::Comma ||
6538-
t == &token::Colon || t == &token::Eq))
6457+
t == &token::Colon || t == &token::Eq) ||
6458+
self.look_ahead(1, |t| t.is_keyword(keywords::Const)))
65396459
}
65406460

65416461
fn parse_impl_body(&mut self) -> PResult<'a, (Vec<ImplItem>, Vec<Attribute>)> {

0 commit comments

Comments
 (0)