Parse const generics
Co-Authored-By: Gabriel Smith <yodaldevoid@users.noreply.github.com>
This commit is contained in:
parent
8d83521f0b
commit
0a8d98a270
2 changed files with 113 additions and 179 deletions
|
@ -397,6 +397,7 @@ crate enum TokenType {
|
||||||
Ident,
|
Ident,
|
||||||
Path,
|
Path,
|
||||||
Type,
|
Type,
|
||||||
|
Const,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TokenType {
|
impl TokenType {
|
||||||
|
@ -409,6 +410,7 @@ impl TokenType {
|
||||||
TokenType::Ident => "identifier".to_string(),
|
TokenType::Ident => "identifier".to_string(),
|
||||||
TokenType::Path => "path".to_string(),
|
TokenType::Path => "path".to_string(),
|
||||||
TokenType::Type => "type".to_string(),
|
TokenType::Type => "type".to_string(),
|
||||||
|
TokenType::Const => "const".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -946,6 +948,19 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_const_param(&mut self) -> bool {
|
||||||
|
self.check_keyword(keywords::Const)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_const_arg(&mut self) -> bool {
|
||||||
|
if self.token.can_begin_const_arg() {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
self.expected_tokens.push(TokenType::Const);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Expect and consume a `+`. if `+=` is seen, replace it with a `=`
|
/// Expect and consume a `+`. if `+=` is seen, replace it with a `=`
|
||||||
/// and continue. If a `+` is not seen, return false.
|
/// and continue. If a `+` is not seen, return false.
|
||||||
///
|
///
|
||||||
|
@ -5482,15 +5497,28 @@ impl<'a> Parser<'a> {
|
||||||
Ok((ident, TraitItemKind::Type(bounds, default), generics))
|
Ok((ident, TraitItemKind::Type(bounds, default), generics))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_const_param(&mut self, preceding_attrs: Vec<Attribute>) -> PResult<'a, GenericParam> {
|
||||||
|
self.expect_keyword(keywords::Const)?;
|
||||||
|
let ident = self.parse_ident()?;
|
||||||
|
self.expect(&token::Colon)?;
|
||||||
|
let ty = self.parse_ty()?;
|
||||||
|
|
||||||
|
Ok(GenericParam {
|
||||||
|
ident,
|
||||||
|
id: ast::DUMMY_NODE_ID,
|
||||||
|
attrs: preceding_attrs.into(),
|
||||||
|
bounds: Vec::new(),
|
||||||
|
kind: GenericParamKind::Const {
|
||||||
|
ty,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses (possibly empty) list of lifetime and type parameters, possibly including
|
/// Parses (possibly empty) list of lifetime and type parameters, possibly including
|
||||||
/// trailing comma and erroneous trailing attributes.
|
/// trailing comma and erroneous trailing attributes.
|
||||||
crate fn parse_generic_params(&mut self) -> PResult<'a, Vec<ast::GenericParam>> {
|
crate fn parse_generic_params(&mut self) -> PResult<'a, Vec<ast::GenericParam>> {
|
||||||
let mut lifetimes = Vec::new();
|
|
||||||
let mut params = Vec::new();
|
let mut params = Vec::new();
|
||||||
let mut seen_ty_param: Option<Span> = None;
|
let mut prev_param: Option<ParamKindOrd> = None;
|
||||||
let mut last_comma_span = None;
|
|
||||||
let mut bad_lifetime_pos = vec![];
|
|
||||||
let mut suggestions = vec![];
|
|
||||||
loop {
|
loop {
|
||||||
let attrs = self.parse_outer_attributes()?;
|
let attrs = self.parse_outer_attributes()?;
|
||||||
if self.check_lifetime() {
|
if self.check_lifetime() {
|
||||||
|
@ -5501,64 +5529,45 @@ impl<'a> Parser<'a> {
|
||||||
} else {
|
} else {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
};
|
};
|
||||||
lifetimes.push(ast::GenericParam {
|
params.push(ast::GenericParam {
|
||||||
ident: lifetime.ident,
|
ident: lifetime.ident,
|
||||||
id: lifetime.id,
|
id: lifetime.id,
|
||||||
attrs: attrs.into(),
|
attrs: attrs.into(),
|
||||||
bounds,
|
bounds,
|
||||||
kind: ast::GenericParamKind::Lifetime,
|
kind: ast::GenericParamKind::Lifetime,
|
||||||
});
|
});
|
||||||
if let Some(sp) = seen_ty_param {
|
prev_param = Some(ParamKindOrd::Lifetime);
|
||||||
let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span);
|
} else if self.check_const_param() {
|
||||||
bad_lifetime_pos.push(self.prev_span);
|
// Parse const parameter.
|
||||||
if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) {
|
params.push(self.parse_const_param(attrs)?);
|
||||||
suggestions.push((remove_sp, String::new()));
|
prev_param = Some(ParamKindOrd::Const);
|
||||||
suggestions.push((
|
|
||||||
sp.shrink_to_lo(),
|
|
||||||
format!("{}, ", snippet)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if self.check_ident() {
|
} else if self.check_ident() {
|
||||||
// Parse type parameter.
|
// Parse type parameter.
|
||||||
params.push(self.parse_ty_param(attrs)?);
|
params.push(self.parse_ty_param(attrs)?);
|
||||||
if seen_ty_param.is_none() {
|
prev_param = Some(ParamKindOrd::Type);
|
||||||
seen_ty_param = Some(self.prev_span);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Check for trailing attributes and stop parsing.
|
// Check for trailing attributes and stop parsing.
|
||||||
if !attrs.is_empty() {
|
if !attrs.is_empty() {
|
||||||
let param_kind = if seen_ty_param.is_some() { "type" } else { "lifetime" };
|
if let Some(prev_param) = prev_param {
|
||||||
self.struct_span_err(
|
self.struct_span_err(
|
||||||
attrs[0].span,
|
attrs[0].span,
|
||||||
&format!("trailing attribute after {} parameters", param_kind),
|
&format!(
|
||||||
|
"trailing attribute after {} parameter",
|
||||||
|
prev_param,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.span_label(attrs[0].span, "attributes must go before parameters")
|
.span_label(attrs[0].span, "attributes must go before parameters")
|
||||||
.emit();
|
.emit();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.eat(&token::Comma) {
|
if !self.eat(&token::Comma) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
last_comma_span = Some(self.prev_span);
|
|
||||||
}
|
}
|
||||||
if !bad_lifetime_pos.is_empty() {
|
Ok(params)
|
||||||
let mut err = self.struct_span_err(
|
|
||||||
bad_lifetime_pos,
|
|
||||||
"lifetime parameters must be declared prior to type parameters",
|
|
||||||
);
|
|
||||||
if !suggestions.is_empty() {
|
|
||||||
err.multipart_suggestion(
|
|
||||||
"move the lifetime parameter prior to the first type parameter",
|
|
||||||
suggestions,
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
err.emit();
|
|
||||||
}
|
|
||||||
lifetimes.extend(params); // ensure the correct order of lifetimes and type params
|
|
||||||
Ok(lifetimes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a set of optional generic type parameter declarations. Where
|
/// Parse a set of optional generic type parameter declarations. Where
|
||||||
|
@ -5740,35 +5749,16 @@ impl<'a> Parser<'a> {
|
||||||
fn parse_generic_args(&mut self) -> PResult<'a, (Vec<GenericArg>, Vec<TypeBinding>)> {
|
fn parse_generic_args(&mut self) -> PResult<'a, (Vec<GenericArg>, Vec<TypeBinding>)> {
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
let mut bindings = Vec::new();
|
let mut bindings = Vec::new();
|
||||||
|
let mut misplaced_assoc_ty_bindings: Vec<Span> = Vec::new();
|
||||||
|
let mut assoc_ty_bindings: Vec<Span> = Vec::new();
|
||||||
|
|
||||||
let mut seen_type = false;
|
let args_lo = self.span;
|
||||||
let mut seen_binding = false;
|
|
||||||
|
|
||||||
let mut last_comma_span = None;
|
|
||||||
let mut first_type_or_binding_span: Option<Span> = None;
|
|
||||||
let mut first_binding_span: Option<Span> = None;
|
|
||||||
|
|
||||||
let mut bad_lifetime_pos = vec![];
|
|
||||||
let mut bad_type_pos = vec![];
|
|
||||||
|
|
||||||
let mut lifetime_suggestions = vec![];
|
|
||||||
let mut type_suggestions = vec![];
|
|
||||||
loop {
|
loop {
|
||||||
if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
|
if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) {
|
||||||
// Parse lifetime argument.
|
// Parse lifetime argument.
|
||||||
args.push(GenericArg::Lifetime(self.expect_lifetime()));
|
args.push(GenericArg::Lifetime(self.expect_lifetime()));
|
||||||
|
misplaced_assoc_ty_bindings.append(&mut assoc_ty_bindings);
|
||||||
if seen_type || seen_binding {
|
|
||||||
let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span);
|
|
||||||
bad_lifetime_pos.push(self.prev_span);
|
|
||||||
|
|
||||||
if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) {
|
|
||||||
lifetime_suggestions.push((remove_sp, String::new()));
|
|
||||||
lifetime_suggestions.push((
|
|
||||||
first_type_or_binding_span.unwrap().shrink_to_lo(),
|
|
||||||
format!("{}, ", snippet)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if self.check_ident() && self.look_ahead(1, |t| t == &token::Eq) {
|
} else if self.check_ident() && self.look_ahead(1, |t| t == &token::Eq) {
|
||||||
// Parse associated type binding.
|
// Parse associated type binding.
|
||||||
let lo = self.span;
|
let lo = self.span;
|
||||||
|
@ -5782,133 +5772,61 @@ impl<'a> Parser<'a> {
|
||||||
ty,
|
ty,
|
||||||
span,
|
span,
|
||||||
});
|
});
|
||||||
|
assoc_ty_bindings.push(span);
|
||||||
seen_binding = true;
|
} else if self.check_const_arg() {
|
||||||
if first_type_or_binding_span.is_none() {
|
// Parse const argument.
|
||||||
first_type_or_binding_span = Some(span);
|
let expr = if let token::OpenDelim(token::Brace) = self.token {
|
||||||
}
|
self.parse_block_expr(None, self.span, BlockCheckMode::Default, ThinVec::new())?
|
||||||
if first_binding_span.is_none() {
|
} else if self.token.can_begin_literal_or_bool() {
|
||||||
first_binding_span = Some(span);
|
let lit = self.parse_lit()?;
|
||||||
}
|
self.mk_expr(lit.span, ExprKind::Lit(lit), ThinVec::new())
|
||||||
|
} 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.
|
||||||
|
return Err(
|
||||||
|
self.fatal("identifiers may currently not be used for const generics")
|
||||||
|
);
|
||||||
|
};
|
||||||
|
let value = AnonConst {
|
||||||
|
id: ast::DUMMY_NODE_ID,
|
||||||
|
value: expr,
|
||||||
|
};
|
||||||
|
args.push(GenericArg::Const(value));
|
||||||
|
misplaced_assoc_ty_bindings.append(&mut assoc_ty_bindings);
|
||||||
} else if self.check_type() {
|
} else if self.check_type() {
|
||||||
// Parse type argument.
|
// Parse type argument.
|
||||||
let ty_param = self.parse_ty()?;
|
args.push(GenericArg::Type(self.parse_ty()?));
|
||||||
if seen_binding {
|
misplaced_assoc_ty_bindings.append(&mut assoc_ty_bindings);
|
||||||
let remove_sp = last_comma_span.unwrap_or(self.prev_span).to(self.prev_span);
|
|
||||||
bad_type_pos.push(self.prev_span);
|
|
||||||
|
|
||||||
if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.prev_span) {
|
|
||||||
type_suggestions.push((remove_sp, String::new()));
|
|
||||||
type_suggestions.push((
|
|
||||||
first_binding_span.unwrap().shrink_to_lo(),
|
|
||||||
format!("{}, ", snippet)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if first_type_or_binding_span.is_none() {
|
|
||||||
first_type_or_binding_span = Some(ty_param.span);
|
|
||||||
}
|
|
||||||
args.push(GenericArg::Type(ty_param));
|
|
||||||
seen_type = true;
|
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.eat(&token::Comma) {
|
if !self.eat(&token::Comma) {
|
||||||
break
|
break
|
||||||
} else {
|
|
||||||
last_comma_span = Some(self.prev_span);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.maybe_report_incorrect_generic_argument_order(
|
// FIXME: we would like to report this in ast_validation instead, but we currently do not
|
||||||
bad_lifetime_pos, bad_type_pos, lifetime_suggestions, type_suggestions
|
// preserve ordering of generic parameters with respect to associated type binding, so we
|
||||||
|
// lose that information after parsing.
|
||||||
|
if misplaced_assoc_ty_bindings.len() > 0 {
|
||||||
|
let mut err = self.struct_span_err(
|
||||||
|
args_lo.to(self.prev_span),
|
||||||
|
"associated type bindings must be declared after generic parameters",
|
||||||
);
|
);
|
||||||
|
for span in misplaced_assoc_ty_bindings {
|
||||||
|
err.span_label(
|
||||||
|
span,
|
||||||
|
"this associated type binding should be moved after the generic parameters",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
err.emit();
|
||||||
|
}
|
||||||
|
|
||||||
Ok((args, bindings))
|
Ok((args, bindings))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Maybe report an error about incorrect generic argument order - "lifetime parameters
|
|
||||||
/// must be declared before type parameters", "type parameters must be declared before
|
|
||||||
/// associated type bindings" or both.
|
|
||||||
fn maybe_report_incorrect_generic_argument_order(
|
|
||||||
&self,
|
|
||||||
bad_lifetime_pos: Vec<Span>,
|
|
||||||
bad_type_pos: Vec<Span>,
|
|
||||||
lifetime_suggestions: Vec<(Span, String)>,
|
|
||||||
type_suggestions: Vec<(Span, String)>,
|
|
||||||
) {
|
|
||||||
let mut err = if !bad_lifetime_pos.is_empty() && !bad_type_pos.is_empty() {
|
|
||||||
let mut positions = bad_lifetime_pos.clone();
|
|
||||||
positions.extend_from_slice(&bad_type_pos);
|
|
||||||
|
|
||||||
self.struct_span_err(
|
|
||||||
positions,
|
|
||||||
"generic arguments must declare lifetimes, types and associated type bindings in \
|
|
||||||
that order",
|
|
||||||
)
|
|
||||||
} else if !bad_lifetime_pos.is_empty() {
|
|
||||||
self.struct_span_err(
|
|
||||||
bad_lifetime_pos.clone(),
|
|
||||||
"lifetime parameters must be declared prior to type parameters"
|
|
||||||
)
|
|
||||||
} else if !bad_type_pos.is_empty() {
|
|
||||||
self.struct_span_err(
|
|
||||||
bad_type_pos.clone(),
|
|
||||||
"type parameters must be declared prior to associated type bindings"
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if !bad_lifetime_pos.is_empty() {
|
|
||||||
for sp in &bad_lifetime_pos {
|
|
||||||
err.span_label(*sp, "must be declared prior to type parameters");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bad_type_pos.is_empty() {
|
|
||||||
for sp in &bad_type_pos {
|
|
||||||
err.span_label(*sp, "must be declared prior to associated type bindings");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !lifetime_suggestions.is_empty() && !type_suggestions.is_empty() {
|
|
||||||
let mut suggestions = lifetime_suggestions;
|
|
||||||
suggestions.extend_from_slice(&type_suggestions);
|
|
||||||
|
|
||||||
let plural = bad_lifetime_pos.len() + bad_type_pos.len() > 1;
|
|
||||||
err.multipart_suggestion(
|
|
||||||
&format!(
|
|
||||||
"move the parameter{}",
|
|
||||||
if plural { "s" } else { "" },
|
|
||||||
),
|
|
||||||
suggestions,
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
} else if !lifetime_suggestions.is_empty() {
|
|
||||||
err.multipart_suggestion(
|
|
||||||
&format!(
|
|
||||||
"move the lifetime parameter{} prior to the first type parameter",
|
|
||||||
if bad_lifetime_pos.len() > 1 { "s" } else { "" },
|
|
||||||
),
|
|
||||||
lifetime_suggestions,
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
} else if !type_suggestions.is_empty() {
|
|
||||||
err.multipart_suggestion(
|
|
||||||
&format!(
|
|
||||||
"move the type parameter{} prior to the first associated type binding",
|
|
||||||
if bad_type_pos.len() > 1 { "s" } else { "" },
|
|
||||||
),
|
|
||||||
type_suggestions,
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
err.emit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parses an optional `where` clause and places it in `generics`.
|
/// Parses an optional `where` clause and places it in `generics`.
|
||||||
///
|
///
|
||||||
/// ```ignore (only-for-syntax-highlight)
|
/// ```ignore (only-for-syntax-highlight)
|
||||||
|
@ -6526,6 +6444,7 @@ impl<'a> Parser<'a> {
|
||||||
// `<` (LIFETIME|IDENT) `,` - first generic parameter in a list
|
// `<` (LIFETIME|IDENT) `,` - first generic parameter in a list
|
||||||
// `<` (LIFETIME|IDENT) `:` - generic parameter with bounds
|
// `<` (LIFETIME|IDENT) `:` - generic parameter with bounds
|
||||||
// `<` (LIFETIME|IDENT) `=` - generic parameter with a default
|
// `<` (LIFETIME|IDENT) `=` - generic parameter with a default
|
||||||
|
// `<` const IDENT - generic const parameter
|
||||||
// The only truly ambiguous case is
|
// The only truly ambiguous case is
|
||||||
// `<` IDENT `>` `::` IDENT ...
|
// `<` IDENT `>` `::` IDENT ...
|
||||||
// we disambiguate it in favor of generics (`impl<T> ::absolute::Path<T> { ... }`)
|
// we disambiguate it in favor of generics (`impl<T> ::absolute::Path<T> { ... }`)
|
||||||
|
@ -6535,7 +6454,8 @@ impl<'a> Parser<'a> {
|
||||||
(self.look_ahead(1, |t| t == &token::Pound || t == &token::Gt) ||
|
(self.look_ahead(1, |t| t == &token::Pound || t == &token::Gt) ||
|
||||||
self.look_ahead(1, |t| t.is_lifetime() || t.is_ident()) &&
|
self.look_ahead(1, |t| t.is_lifetime() || t.is_ident()) &&
|
||||||
self.look_ahead(2, |t| t == &token::Gt || t == &token::Comma ||
|
self.look_ahead(2, |t| t == &token::Gt || t == &token::Comma ||
|
||||||
t == &token::Colon || t == &token::Eq))
|
t == &token::Colon || t == &token::Eq) ||
|
||||||
|
self.look_ahead(1, |t| t.is_keyword(keywords::Const)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_impl_body(&mut self) -> PResult<'a, (Vec<ImplItem>, Vec<Attribute>)> {
|
fn parse_impl_body(&mut self) -> PResult<'a, (Vec<ImplItem>, Vec<Attribute>)> {
|
||||||
|
|
|
@ -279,6 +279,20 @@ impl Token {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the token can appear at the start of a const param.
|
||||||
|
pub fn can_begin_const_arg(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
OpenDelim(Brace) => true,
|
||||||
|
Interpolated(ref nt) => match nt.0 {
|
||||||
|
NtExpr(..) => true,
|
||||||
|
NtBlock(..) => true,
|
||||||
|
NtLiteral(..) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
_ => self.can_begin_literal_or_bool(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` if the token can appear at the start of a generic bound.
|
/// Returns `true` if the token can appear at the start of a generic bound.
|
||||||
crate fn can_begin_bound(&self) -> bool {
|
crate fn can_begin_bound(&self) -> bool {
|
||||||
self.is_path_start() || self.is_lifetime() || self.is_keyword(keywords::For) ||
|
self.is_path_start() || self.is_lifetime() || self.is_keyword(keywords::For) ||
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue