Skip to content

Commit a833be2

Browse files
committed
parser: fuse free fn parsing together.
1 parent 36a17e4 commit a833be2

28 files changed

+181
-185
lines changed

src/librustc_ast_passes/ast_validation.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,20 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
10891089

10901090
self.check_c_varadic_type(fk);
10911091

1092+
// Functions cannot both be `const async`
1093+
if let Some(FnHeader {
1094+
constness: Const::Yes(cspan),
1095+
asyncness: Async::Yes { span: aspan, .. },
1096+
..
1097+
}) = fk.header()
1098+
{
1099+
self.err_handler()
1100+
.struct_span_err(span, "functions cannot be both `const` and `async`")
1101+
.span_label(*cspan, "`const` because of this")
1102+
.span_label(*aspan, "`async` because of this")
1103+
.emit();
1104+
}
1105+
10921106
// Functions without bodies cannot have patterns.
10931107
if let FnKind::Fn(ctxt, _, sig, _, None) = fk {
10941108
Self::check_decl_no_pat(&sig.decl, |span, mut_ident| {

src/librustc_ast_passes/feature_gate.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,17 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
496496
if let Some(header) = fn_kind.header() {
497497
// Stability of const fn methods are covered in `visit_assoc_item` below.
498498
self.check_extern(header.ext);
499+
500+
if let (ast::Const::Yes(_), ast::Extern::Implicit)
501+
| (ast::Const::Yes(_), ast::Extern::Explicit(_)) = (header.constness, header.ext)
502+
{
503+
gate_feature_post!(
504+
&self,
505+
const_extern_fn,
506+
span,
507+
"`const extern fn` definitions are unstable"
508+
);
509+
}
499510
}
500511

501512
if fn_kind.ctxt() != Some(FnCtxt::Foreign) && fn_kind.decl().c_variadic() {
@@ -595,7 +606,6 @@ pub fn check_crate(
595606
gate_all!(async_closure, "async closures are unstable");
596607
gate_all!(generators, "yield syntax is experimental");
597608
gate_all!(or_patterns, "or-patterns syntax is experimental");
598-
gate_all!(const_extern_fn, "`const extern fn` definitions are unstable");
599609
gate_all!(raw_ref_op, "raw address of syntax is experimental");
600610
gate_all!(const_trait_bound_opt_out, "`?const` on trait bounds is experimental");
601611
gate_all!(const_trait_impl, "const trait impls are experimental");

src/librustc_parse/parser/item.rs

Lines changed: 25 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_span::symbol::{kw, sym, Symbol};
1111
use rustc_span::BytePos;
1212
use syntax::ast::{self, AttrKind, AttrStyle, AttrVec, Attribute, Ident, DUMMY_NODE_ID};
1313
use syntax::ast::{AssocItem, AssocItemKind, Item, ItemKind, UseTree, UseTreeKind};
14-
use syntax::ast::{Async, Const, Defaultness, Extern, IsAuto, PathSegment, StrLit, Unsafe};
14+
use syntax::ast::{Async, Const, Defaultness, IsAuto, PathSegment, StrLit, Unsafe};
1515
use syntax::ast::{BindingMode, Block, FnDecl, FnSig, Mac, MacArgs, MacDelimiter, Param, SelfKind};
1616
use syntax::ast::{EnumDef, Generics, StructField, TraitRef, Ty, TyKind, Variant, VariantData};
1717
use syntax::ast::{FnHeader, ForeignItem, ForeignItemKind, Mutability, Visibility, VisibilityKind};
@@ -96,53 +96,30 @@ impl<'a> Parser<'a> {
9696
return Ok(Some(item));
9797
}
9898

99+
if self.is_fn_front_matter() {
100+
// FUNCTION ITEM
101+
return self.parse_item_fn(lo, vis, attrs);
102+
}
103+
99104
if self.eat_keyword(kw::Extern) {
100105
if self.eat_keyword(kw::Crate) {
106+
// EXTERN CRATE
101107
return Ok(Some(self.parse_item_extern_crate(lo, vis, attrs)?));
102108
}
103-
109+
// EXTERN BLOCK
104110
let abi = self.parse_abi();
105-
106-
if self.eat_keyword(kw::Fn) {
107-
// EXTERN FUNCTION ITEM
108-
let header = FnHeader {
109-
unsafety: Unsafe::No,
110-
asyncness: Async::No,
111-
constness: Const::No,
112-
ext: Extern::from_abi(abi),
113-
};
114-
return self.parse_item_fn(lo, vis, attrs, header);
115-
} else if self.check(&token::OpenDelim(token::Brace)) {
116-
return Ok(Some(self.parse_item_foreign_mod(lo, abi, vis, attrs)?));
117-
}
118-
119-
self.unexpected()?;
111+
return Ok(Some(self.parse_item_foreign_mod(lo, abi, vis, attrs)?));
120112
}
121113

122114
if self.is_static_global() {
123-
self.bump();
124115
// STATIC ITEM
116+
self.bump();
125117
let m = self.parse_mutability();
126118
let info = self.parse_item_const(Some(m))?;
127119
return self.mk_item_with_info(attrs, lo, vis, info);
128120
}
129121

130-
let constness = self.parse_constness();
131-
if let Const::Yes(const_span) = constness {
132-
if [kw::Fn, kw::Unsafe, kw::Extern].iter().any(|k| self.check_keyword(*k)) {
133-
// CONST FUNCTION ITEM
134-
let unsafety = self.parse_unsafety();
135-
136-
if self.check_keyword(kw::Extern) {
137-
self.sess.gated_spans.gate(sym::const_extern_fn, lo.to(self.token.span));
138-
}
139-
let ext = self.parse_extern()?;
140-
self.expect_keyword(kw::Fn)?;
141-
142-
let header = FnHeader { unsafety, asyncness: Async::No, constness, ext };
143-
return self.parse_item_fn(lo, vis, attrs, header);
144-
}
145-
122+
if let Const::Yes(const_span) = self.parse_constness() {
146123
// CONST ITEM
147124
if self.eat_keyword(kw::Mut) {
148125
let prev_span = self.prev_span;
@@ -161,21 +138,6 @@ impl<'a> Parser<'a> {
161138
return self.mk_item_with_info(attrs, lo, vis, info);
162139
}
163140

164-
// Parses `async unsafe? fn`.
165-
if self.check_keyword(kw::Async) {
166-
let async_span = self.token.span;
167-
if self.is_keyword_ahead(1, &[kw::Fn]) || self.is_keyword_ahead(2, &[kw::Fn]) {
168-
// ASYNC FUNCTION ITEM
169-
let asyncness = self.parse_asyncness(); // `async`
170-
let unsafety = self.parse_unsafety(); // `unsafe`?
171-
self.expect_keyword(kw::Fn)?; // `fn`
172-
self.ban_async_in_2015(async_span);
173-
let header =
174-
FnHeader { unsafety, asyncness, constness: Const::No, ext: Extern::None };
175-
return self.parse_item_fn(lo, vis, attrs, header);
176-
}
177-
}
178-
179141
if self.check_keyword(kw::Unsafe) && self.is_keyword_ahead(1, &[kw::Trait, kw::Auto]) {
180142
// UNSAFE TRAIT ITEM
181143
let unsafety = self.parse_unsafety();
@@ -195,26 +157,6 @@ impl<'a> Parser<'a> {
195157
return self.mk_item_with_info(attrs, lo, vis, info);
196158
}
197159

198-
if self.check_keyword(kw::Fn) {
199-
// FUNCTION ITEM
200-
self.bump();
201-
let header = FnHeader::default();
202-
return self.parse_item_fn(lo, vis, attrs, header);
203-
}
204-
205-
if self.check_keyword(kw::Unsafe)
206-
&& self.look_ahead(1, |t| *t != token::OpenDelim(token::Brace))
207-
{
208-
// UNSAFE FUNCTION ITEM
209-
let unsafety = self.parse_unsafety();
210-
// `{` is also expected after `unsafe`; in case of error, include it in the diagnostic.
211-
self.check(&token::OpenDelim(token::Brace));
212-
let ext = self.parse_extern()?;
213-
self.expect_keyword(kw::Fn)?;
214-
let header = FnHeader { unsafety, asyncness: Async::No, constness: Const::No, ext };
215-
return self.parse_item_fn(lo, vis, attrs, header);
216-
}
217-
218160
if self.eat_keyword(kw::Mod) {
219161
// MODULE ITEM
220162
let info = self.parse_item_mod(&attrs[..])?;
@@ -1662,9 +1604,9 @@ impl<'a> Parser<'a> {
16621604
lo: Span,
16631605
vis: Visibility,
16641606
mut attrs: Vec<Attribute>,
1665-
header: FnHeader,
16661607
) -> PResult<'a, Option<P<Item>>> {
16671608
let cfg = ParamCfg { is_name_required: |_| true };
1609+
let header = self.parse_fn_front_matter()?;
16681610
let (ident, decl, generics) = self.parse_fn_sig(&cfg)?;
16691611
let body = self.parse_fn_body(&mut false, &mut attrs)?;
16701612
let kind = ItemKind::Fn(FnSig { decl, header }, generics, body);
@@ -1730,27 +1672,24 @@ impl<'a> Parser<'a> {
17301672
Ok(body)
17311673
}
17321674

1733-
/// Is the current token unambiguously the start of an `FnHeader`?
1675+
/// Is the current token the start of an `FnHeader` / not a valid parse?
17341676
fn is_fn_front_matter(&mut self) -> bool {
17351677
// We use an over-approximation here.
17361678
// `const const`, `fn const` won't parse, but we're not stepping over other syntax either.
1737-
// This works for `async fn` and similar as `async async` is an invalid
1738-
// parse and `async fn` is never a valid parse on previous editions.
1739-
const QUALIFIER: [Symbol; 4] = [kw::Const, kw::Async, kw::Unsafe, kw::Extern];
1740-
1741-
let check_qual_follow = |this: &mut Self, dist| {
1742-
this.look_ahead(dist, |t| {
1743-
// ...qualified and then `fn`, e.g. `const fn`.
1744-
t.is_keyword(kw::Fn)
1745-
// Two qualifiers. This is enough.
1746-
|| QUALIFIER.iter().any(|&kw| t.is_keyword(kw))
1747-
})
1748-
};
1679+
const QUALS: [Symbol; 4] = [kw::Const, kw::Async, kw::Unsafe, kw::Extern];
17491680
self.check_keyword(kw::Fn) // Definitely an `fn`.
17501681
// `$qual fn` or `$qual $qual`:
1751-
|| QUALIFIER.iter().any(|&kw| self.check_keyword(kw)) && check_qual_follow(self, 1)
1752-
// `extern ABI fn` or `extern ABI $qual`; skip 1 for the ABI.
1753-
|| self.check_keyword(kw::Extern) && check_qual_follow(self, 2)
1682+
|| QUALS.iter().any(|&kw| self.check_keyword(kw))
1683+
&& self.look_ahead(1, |t| {
1684+
// ...qualified and then `fn`, e.g. `const fn`.
1685+
t.is_keyword(kw::Fn)
1686+
// Two qualifiers. This is enough. Due `async` we need to check that it's reserved.
1687+
|| t.is_non_raw_ident_where(|i| QUALS.contains(&i.name) && i.is_reserved())
1688+
})
1689+
// `extern ABI fn`
1690+
|| self.check_keyword(kw::Extern)
1691+
&& self.look_ahead(1, |t| t.can_begin_literal_or_bool())
1692+
&& self.look_ahead(2, |t| t.is_keyword(kw::Fn))
17541693
}
17551694

17561695
/// Parses all the "front matter" (or "qualifiers") for a `fn` declaration,

src/libsyntax/token.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -402,12 +402,14 @@ impl Token {
402402

403403
/// Returns `true` if the token is any literal, a minus (which can prefix a literal,
404404
/// for example a '-42', or one of the boolean idents).
405+
///
406+
/// Keep this in sync with `Lit::from_token`.
405407
pub fn can_begin_literal_or_bool(&self) -> bool {
406408
match self.kind {
407409
Literal(..) | BinOp(Minus) => true,
408410
Ident(name, false) if name.is_bool_lit() => true,
409-
Interpolated(ref nt) => match **nt {
410-
NtLiteral(..) => true,
411+
Interpolated(ref nt) => match &**nt {
412+
NtExpr(e) | NtLiteral(e) => matches!(e.kind, ast::ExprKind::Lit(_)),
411413
_ => false,
412414
},
413415
_ => false,
@@ -530,7 +532,7 @@ impl Token {
530532
}
531533

532534
/// Returns `true` if the token is a non-raw identifier for which `pred` holds.
533-
fn is_non_raw_ident_where(&self, pred: impl FnOnce(ast::Ident) -> bool) -> bool {
535+
pub fn is_non_raw_ident_where(&self, pred: impl FnOnce(ast::Ident) -> bool) -> bool {
534536
match self.ident() {
535537
Some((id, false)) => pred(id),
536538
_ => false,

src/libsyntax/util/literal.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ impl Lit {
188188
}
189189

190190
/// Converts arbitrary token into an AST literal.
191+
///
192+
/// Keep this in sync with `Token::can_begin_literal_or_bool`.
191193
pub fn from_token(token: &Token) -> Result<Lit, LitError> {
192194
let lit = match token.kind {
193195
token::Ident(name, false) if name.is_bool_lit() => {

src/test/ui/async-await/no-async-const.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
// compile-flags: --crate-type lib
33

44
pub async const fn x() {}
5-
//~^ ERROR expected one of `fn` or `unsafe`, found keyword `const`
5+
//~^ ERROR expected one of `extern`, `fn`, or `unsafe`, found keyword `const`
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: expected one of `fn` or `unsafe`, found keyword `const`
1+
error: expected one of `extern`, `fn`, or `unsafe`, found keyword `const`
22
--> $DIR/no-async-const.rs:4:11
33
|
44
LL | pub async const fn x() {}
5-
| ^^^^^ expected one of `fn` or `unsafe`
5+
| ^^^^^ expected one of `extern`, `fn`, or `unsafe`
66

77
error: aborting due to previous error
88

src/test/ui/async-await/no-const-async.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,4 @@
22
// compile-flags: --crate-type lib
33

44
pub const async fn x() {}
5-
//~^ ERROR expected identifier, found keyword `async`
6-
//~^^ expected `:`, found keyword `fn`
5+
//~^ ERROR functions cannot be both `const` and `async`
Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
error: expected identifier, found keyword `async`
2-
--> $DIR/no-const-async.rs:4:11
1+
error: functions cannot be both `const` and `async`
2+
--> $DIR/no-const-async.rs:4:1
33
|
44
LL | pub const async fn x() {}
5-
| ^^^^^ expected identifier, found keyword
5+
| ^^^^-----^-----^^^^^^^^^^
6+
| | |
7+
| | `async` because of this
8+
| `const` because of this
69

7-
error: expected `:`, found keyword `fn`
8-
--> $DIR/no-const-async.rs:4:17
9-
|
10-
LL | pub const async fn x() {}
11-
| ^^ expected `:`
12-
13-
error: aborting due to 2 previous errors
10+
error: aborting due to previous error
1411

src/test/ui/async-await/no-unsafe-async.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ impl S {
88
}
99

1010
#[cfg(FALSE)]
11-
unsafe async fn f() {} //~ ERROR expected one of `extern`, `fn`, or `{`, found keyword `async`
11+
unsafe async fn f() {} //~ ERROR expected one of `extern` or `fn`, found keyword `async`

src/test/ui/async-await/no-unsafe-async.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ error: expected one of `extern` or `fn`, found keyword `async`
44
LL | unsafe async fn g() {}
55
| ^^^^^ expected one of `extern` or `fn`
66

7-
error: expected one of `extern`, `fn`, or `{`, found keyword `async`
7+
error: expected one of `extern` or `fn`, found keyword `async`
88
--> $DIR/no-unsafe-async.rs:11:8
99
|
1010
LL | unsafe async fn f() {}
11-
| ^^^^^ expected one of `extern`, `fn`, or `{`
11+
| ^^^^^ expected one of `extern` or `fn`
1212

1313
error: aborting due to 2 previous errors
1414

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
// Check that `const extern fn` and `const unsafe extern fn` are feature-gated.
22

3-
#[cfg(FALSE)] const extern fn foo1() {} //~ ERROR `const extern fn` definitions are unstable
4-
#[cfg(FALSE)] const extern "C" fn foo2() {} //~ ERROR `const extern fn` definitions are unstable
5-
#[cfg(FALSE)] const extern "Rust" fn foo3() {} //~ ERROR `const extern fn` definitions are unstable
6-
#[cfg(FALSE)] const unsafe extern fn bar1() {} //~ ERROR `const extern fn` definitions are unstable
7-
#[cfg(FALSE)] const unsafe extern "C" fn bar2() {}
8-
//~^ ERROR `const extern fn` definitions are unstable
9-
#[cfg(FALSE)] const unsafe extern "Rust" fn bar3() {}
10-
//~^ ERROR `const extern fn` definitions are unstable
3+
const extern fn foo1() {} //~ ERROR `const extern fn` definitions are unstable
4+
const extern "C" fn foo2() {} //~ ERROR `const extern fn` definitions are unstable
5+
const extern "Rust" fn foo3() {} //~ ERROR `const extern fn` definitions are unstable
6+
const unsafe extern fn bar1() {} //~ ERROR `const extern fn` definitions are unstable
7+
const unsafe extern "C" fn bar2() {} //~ ERROR `const extern fn` definitions are unstable
8+
const unsafe extern "Rust" fn bar3() {} //~ ERROR `const extern fn` definitions are unstable
119

1210
fn main() {}

0 commit comments

Comments
 (0)