Skip to content

Commit

Permalink
Rollup merge of rust-lang#58678 - doctorn:refuse-async-fn-2015-editio…
Browse files Browse the repository at this point in the history
…n, r=varkor

Deny `async fn` in 2015 edition

This commit prevents code using `async fn` from being compiled in Rust 2015 edition.

Compiling code of the form:

```rust
async fn foo() {}
```

Will now result in the error:

```
error[E0670]: `async fn` is not permitted in the 2015 edition
 --> async.rs:1:1
  |
1 | async fn foo() {}
  | ^^^^^

error: aborting due to error

For more information about an error, try `rustc --explain E0670`.
```

This resolves rust-lang#58652 and also resolves rust-lang#53714.

r? @varkor
  • Loading branch information
Centril authored Feb 27, 2019
2 parents 66747ad + 8300f51 commit c734e24
Show file tree
Hide file tree
Showing 22 changed files with 215 additions and 62 deletions.
10 changes: 5 additions & 5 deletions src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2892,7 +2892,7 @@ impl<'a> LoweringContext<'a> {
// `impl Future<Output = T>` here because lower_body
// only cares about the input argument patterns in the function
// declaration (decl), not the return types.
let body_id = this.lower_async_body(decl, header.asyncness, body);
let body_id = this.lower_async_body(decl, header.asyncness.node, body);

let (generics, fn_decl) = this.add_in_band_defs(
generics,
Expand All @@ -2902,7 +2902,7 @@ impl<'a> LoweringContext<'a> {
decl,
Some((fn_def_id, idty)),
true,
header.asyncness.opt_return_id()
header.asyncness.node.opt_return_id()
),
);

Expand Down Expand Up @@ -3398,14 +3398,14 @@ impl<'a> LoweringContext<'a> {
)
}
ImplItemKind::Method(ref sig, ref body) => {
let body_id = self.lower_async_body(&sig.decl, sig.header.asyncness, body);
let body_id = self.lower_async_body(&sig.decl, sig.header.asyncness.node, body);
let impl_trait_return_allow = !self.is_in_trait_impl;
let (generics, sig) = self.lower_method_sig(
&i.generics,
sig,
impl_item_def_id,
impl_trait_return_allow,
sig.header.asyncness.opt_return_id(),
sig.header.asyncness.node.opt_return_id(),
);
(generics, hir::ImplItemKind::Method(sig, body_id))
}
Expand Down Expand Up @@ -3624,7 +3624,7 @@ impl<'a> LoweringContext<'a> {
fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader {
hir::FnHeader {
unsafety: self.lower_unsafety(h.unsafety),
asyncness: self.lower_asyncness(h.asyncness),
asyncness: self.lower_asyncness(h.asyncness.node),
constness: self.lower_constness(h.constness),
abi: h.abi,
}
Expand Down
10 changes: 5 additions & 5 deletions src/librustc/hir/map/def_collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ impl<'a> DefCollector<'a> {
decl: &'a FnDecl,
body: &'a Block,
) {
let (closure_id, return_impl_trait_id) = match header.asyncness {
let (closure_id, return_impl_trait_id) = match header.asyncness.node {
IsAsync::Async {
closure_id,
return_impl_trait_id,
Expand Down Expand Up @@ -129,10 +129,10 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
}
ItemKind::Fn(
ref decl,
ref header @ FnHeader { asyncness: IsAsync::Async { .. }, .. },
ref header,
ref generics,
ref body,
) => {
) if header.asyncness.node.is_async() => {
return self.visit_async_fn(
i.id,
i.ident.name,
Expand Down Expand Up @@ -242,9 +242,9 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
fn visit_impl_item(&mut self, ii: &'a ImplItem) {
let def_data = match ii.node {
ImplItemKind::Method(MethodSig {
header: ref header @ FnHeader { asyncness: IsAsync::Async { .. }, .. },
ref header,
ref decl,
}, ref body) => {
}, ref body) if header.asyncness.node.is_async() => {
return self.visit_async_fn(
ii.id,
ii.ident.name,
Expand Down
14 changes: 11 additions & 3 deletions src/librustc_passes/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.invalid_visibility(&impl_item.vis, None);
if let ImplItemKind::Method(ref sig, _) = impl_item.node {
self.check_trait_fn_not_const(sig.header.constness);
self.check_trait_fn_not_async(impl_item.span, sig.header.asyncness);
self.check_trait_fn_not_async(impl_item.span, sig.header.asyncness.node);
}
}
}
Expand All @@ -482,9 +482,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
.note("only trait implementations may be annotated with default").emit();
}
}
ItemKind::Fn(_, header, ref generics, _) => {
ItemKind::Fn(_, ref header, ref generics, _) => {
// We currently do not permit const generics in `const fn`, as
// this is tantamount to allowing compile-time dependent typing.
self.visit_fn_header(header);
if header.constness.node == Constness::Const {
// Look for const generics and error if we find any.
for param in &generics.params {
Expand Down Expand Up @@ -535,7 +536,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.no_questions_in_bounds(bounds, "supertraits", true);
for trait_item in trait_items {
if let TraitItemKind::Method(ref sig, ref block) = trait_item.node {
self.check_trait_fn_not_async(trait_item.span, sig.header.asyncness);
self.check_trait_fn_not_async(trait_item.span, sig.header.asyncness.node);
self.check_trait_fn_not_const(sig.header.constness);
if block.is_none() {
self.check_decl_no_pat(&sig.decl, |span, mut_ident| {
Expand Down Expand Up @@ -702,6 +703,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
.span_bug(mac.span, "macro invocation missed in expansion; did you forget to override \
the relevant `fold_*()` method in `PlaceholderExpander`?");
}

fn visit_fn_header(&mut self, header: &'a FnHeader) {
if header.asyncness.node.is_async() && self.session.rust_2015() {
struct_span_err!(self.session, header.asyncness.span, E0670,
"`async fn` is not permitted in the 2015 edition").emit();
}
}
}

pub fn check_crate(session: &Session, krate: &Crate) -> (bool, bool) {
Expand Down
12 changes: 12 additions & 0 deletions src/librustc_passes/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,18 @@ loop {
break;
}
```
"##,

E0670: r##"
Rust 2015 does not permit the use of `async fn`.
Example of erroneous code:
```compile_fail,E0670
async fn foo() {}
```
Switch to the Rust 2018 edition to use `async fn`.
"##
}

Expand Down
4 changes: 2 additions & 2 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -806,9 +806,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> {
debug!("(resolving function) entering function");
let (rib_kind, asyncness) = match function_kind {
FnKind::ItemFn(_, ref header, ..) =>
(ItemRibKind, header.asyncness),
(ItemRibKind, header.asyncness.node),
FnKind::Method(_, ref sig, _, _) =>
(TraitOrImplItemRibKind, sig.header.asyncness),
(TraitOrImplItemRibKind, sig.header.asyncness.node),
FnKind::Closure(_) =>
// Async closures aren't resolved through `visit_fn`-- they're
// processed separately
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_save_analysis/sig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ impl Sig for ast::Item {
if header.constness.node == ast::Constness::Const {
text.push_str("const ");
}
if header.asyncness.is_async() {
if header.asyncness.node.is_async() {
text.push_str("async ");
}
if header.unsafety == ast::Unsafety::Unsafe {
Expand Down Expand Up @@ -936,7 +936,7 @@ fn make_method_signature(
if m.header.constness.node == ast::Constness::Const {
text.push_str("const ");
}
if m.header.asyncness.is_async() {
if m.header.asyncness.node.is_async() {
text.push_str("async ");
}
if m.header.unsafety == ast::Unsafety::Unsafe {
Expand Down
4 changes: 2 additions & 2 deletions src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2216,7 +2216,7 @@ impl Item {
#[derive(Clone, Copy, RustcEncodable, RustcDecodable, Debug)]
pub struct FnHeader {
pub unsafety: Unsafety,
pub asyncness: IsAsync,
pub asyncness: Spanned<IsAsync>,
pub constness: Spanned<Constness>,
pub abi: Abi,
}
Expand All @@ -2225,7 +2225,7 @@ impl Default for FnHeader {
fn default() -> FnHeader {
FnHeader {
unsafety: Unsafety::Normal,
asyncness: IsAsync::NotAsync,
asyncness: dummy_spanned(IsAsync::NotAsync),
constness: dummy_spanned(Constness::NotConst),
abi: Abi::Rust,
}
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax/ext/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1017,7 +1017,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
ast::ItemKind::Fn(self.fn_decl(inputs, ast::FunctionRetTy::Ty(output)),
ast::FnHeader {
unsafety: ast::Unsafety::Normal,
asyncness: ast::IsAsync::NotAsync,
asyncness: dummy_spanned(ast::IsAsync::NotAsync),
constness: dummy_spanned(ast::Constness::NotConst),
abi: Abi::Rust,
},
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1898,7 +1898,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
match fn_kind {
FnKind::ItemFn(_, header, _, _) => {
// Check for const fn and async fn declarations.
if header.asyncness.is_async() {
if header.asyncness.node.is_async() {
gate_feature_post!(&self, async_await, span, "async fn is unstable");
}
// Stability of const fn methods are covered in
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -934,7 +934,7 @@ pub fn noop_flat_map_impl_item<T: MutVisitor>(mut item: ImplItem, visitor: &mut

pub fn noop_visit_fn_header<T: MutVisitor>(header: &mut FnHeader, vis: &mut T) {
let FnHeader { unsafety: _, asyncness, constness: _, abi: _ } = header;
vis.visit_asyncness(asyncness);
vis.visit_asyncness(&mut asyncness.node);
}

pub fn noop_visit_mod<T: MutVisitor>(Mod { inner, items, inline: _ }: &mut Mod, vis: &mut T) {
Expand Down
39 changes: 28 additions & 11 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5001,6 +5001,11 @@ impl<'a> Parser<'a> {
)
}

fn is_async_fn(&mut self) -> bool {
self.token.is_keyword(keywords::Async) &&
self.look_ahead(1, |t| t.is_keyword(keywords::Fn))
}

fn is_do_catch_block(&mut self) -> bool {
self.token.is_keyword(keywords::Do) &&
self.look_ahead(1, |t| t.is_keyword(keywords::Catch)) &&
Expand Down Expand Up @@ -5133,7 +5138,8 @@ impl<'a> Parser<'a> {
!self.is_union_item() &&
!self.is_crate_vis() &&
!self.is_existential_type_decl() &&
!self.is_auto_trait_item() {
!self.is_auto_trait_item() &&
!self.is_async_fn() {
let pth = self.parse_path(PathStyle::Expr)?;

if !self.eat(&token::Not) {
Expand Down Expand Up @@ -6384,7 +6390,7 @@ impl<'a> Parser<'a> {
/// Parses an item-position function declaration.
fn parse_item_fn(&mut self,
unsafety: Unsafety,
asyncness: IsAsync,
asyncness: Spanned<IsAsync>,
constness: Spanned<Constness>,
abi: Abi)
-> PResult<'a, ItemInfo> {
Expand Down Expand Up @@ -6416,14 +6422,15 @@ impl<'a> Parser<'a> {
-> PResult<'a, (
Spanned<Constness>,
Unsafety,
IsAsync,
Spanned<IsAsync>,
Abi
)>
{
let is_const_fn = self.eat_keyword(keywords::Const);
let const_span = self.prev_span;
let unsafety = self.parse_unsafety();
let asyncness = self.parse_asyncness();
let asyncness = respan(self.prev_span, asyncness);
let (constness, unsafety, abi) = if is_const_fn {
(respan(const_span, Constness::Const), unsafety, Abi::Rust)
} else {
Expand Down Expand Up @@ -7834,7 +7841,7 @@ impl<'a> Parser<'a> {
let abi = opt_abi.unwrap_or(Abi::C);
let (ident, item_, extra_attrs) =
self.parse_item_fn(Unsafety::Normal,
IsAsync::NotAsync,
respan(fn_span, IsAsync::NotAsync),
respan(fn_span, Constness::NotConst),
abi)?;
let prev_span = self.prev_span;
Expand Down Expand Up @@ -7878,7 +7885,7 @@ impl<'a> Parser<'a> {
self.bump();
let (ident, item_, extra_attrs) =
self.parse_item_fn(unsafety,
IsAsync::NotAsync,
respan(const_span, IsAsync::NotAsync),
respan(const_span, Constness::Const),
Abi::Rust)?;
let prev_span = self.prev_span;
Expand Down Expand Up @@ -7926,14 +7933,15 @@ impl<'a> Parser<'a> {
// ASYNC FUNCTION ITEM
let unsafety = self.parse_unsafety();
self.expect_keyword(keywords::Async)?;
let async_span = self.prev_span;
self.expect_keyword(keywords::Fn)?;
let fn_span = self.prev_span;
let (ident, item_, extra_attrs) =
self.parse_item_fn(unsafety,
IsAsync::Async {
respan(async_span, IsAsync::Async {
closure_id: ast::DUMMY_NODE_ID,
return_impl_trait_id: ast::DUMMY_NODE_ID,
},
}),
respan(fn_span, Constness::NotConst),
Abi::Rust)?;
let prev_span = self.prev_span;
Expand All @@ -7942,6 +7950,13 @@ impl<'a> Parser<'a> {
item_,
visibility,
maybe_append(attrs, extra_attrs));
if self.span.rust_2015() {
self.diagnostic().struct_span_err_with_code(
async_span,
"`async fn` is not permitted in the 2015 edition",
DiagnosticId::Error("E0670".into())
).emit();
}
return Ok(Some(item));
}
if self.check_keyword(keywords::Unsafe) &&
Expand Down Expand Up @@ -7989,7 +8004,7 @@ impl<'a> Parser<'a> {
let fn_span = self.prev_span;
let (ident, item_, extra_attrs) =
self.parse_item_fn(Unsafety::Normal,
IsAsync::NotAsync,
respan(fn_span, IsAsync::NotAsync),
respan(fn_span, Constness::NotConst),
Abi::Rust)?;
let prev_span = self.prev_span;
Expand All @@ -8015,7 +8030,7 @@ impl<'a> Parser<'a> {
let fn_span = self.prev_span;
let (ident, item_, extra_attrs) =
self.parse_item_fn(Unsafety::Unsafe,
IsAsync::NotAsync,
respan(fn_span, IsAsync::NotAsync),
respan(fn_span, Constness::NotConst),
abi)?;
let prev_span = self.prev_span;
Expand Down Expand Up @@ -8282,7 +8297,8 @@ impl<'a> Parser<'a> {
lo: Span,
visibility: Visibility
) -> PResult<'a, Option<P<Item>>> {
if macros_allowed && self.token.is_path_start() {
if macros_allowed && self.token.is_path_start() &&
!(self.is_async_fn() && self.span.rust_2015()) {
// MACRO INVOCATION ITEM

let prev_span = self.prev_span;
Expand Down Expand Up @@ -8337,7 +8353,8 @@ impl<'a> Parser<'a> {
fn parse_assoc_macro_invoc(&mut self, item_kind: &str, vis: Option<&Visibility>,
at_end: &mut bool) -> PResult<'a, Option<Mac>>
{
if self.token.is_path_start() {
if self.token.is_path_start() &&
!(self.is_async_fn() && self.span.rust_2015()) {
let prev_span = self.prev_span;
let lo = self.span;
let pth = self.parse_path(PathStyle::Mod)?;
Expand Down
4 changes: 2 additions & 2 deletions src/libsyntax/print/pprust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3195,7 +3195,7 @@ impl<'a> State<'a> {
ast::Constness::Const => self.word_nbsp("const")?
}

self.print_asyncness(header.asyncness)?;
self.print_asyncness(header.asyncness.node)?;
self.print_unsafety(header.unsafety)?;

if header.abi != Abi::Rust {
Expand Down Expand Up @@ -3247,7 +3247,7 @@ mod tests {
ast::FnHeader {
unsafety: ast::Unsafety::Normal,
constness: source_map::dummy_spanned(ast::Constness::NotConst),
asyncness: ast::IsAsync::NotAsync,
asyncness: source_map::dummy_spanned(ast::IsAsync::NotAsync),
abi: Abi::Rust,
},
abba_ident,
Expand Down
Loading

0 comments on commit c734e24

Please sign in to comment.