about summary refs log tree commit diff
path: root/src/libsyntax/parse/parser
diff options
context:
space:
mode:
authorAaron Hill <aa1ronham@gmail.com>2019-09-29 19:22:18 -0400
committerAaron Hill <aa1ronham@gmail.com>2019-10-02 00:38:52 -0400
commit73b50d211b254bf72ce10b5ea4c3e0033e3189f4 (patch)
treeffd2406f05d8bae7607cac2399493bbd331657ef /src/libsyntax/parse/parser
parent7130fc54e05e247f93c7ecc2d10f56b314c97831 (diff)
downloadrust-73b50d211b254bf72ce10b5ea4c3e0033e3189f4.tar.gz
rust-73b50d211b254bf72ce10b5ea4c3e0033e3189f4.zip
Add support for 'extern const fn'
This works just as you might expect - an 'extern const fn' is a 'const
fn' that is callable from foreign code.

Currently, panicking is not allowed in consts. When RFC 2345 is
stabilized, then panicking in an 'extern const fn' will produce a
compile-time error when invoked at compile time, and an abort when
invoked at runtime.

Since this is extending the language (we're allowing the `const` keyword
in a new context), I believe that this will need an FCP. However, it's a
very minor change, so I didn't think that filing an RFC was necessary.

This will allow libc (and other FFI crates) to make many functions
`const`, without having to give up on making them `extern` as well.
Diffstat (limited to 'src/libsyntax/parse/parser')
-rw-r--r--src/libsyntax/parse/parser/item.rs52
1 files changed, 33 insertions, 19 deletions
diff --git a/src/libsyntax/parse/parser/item.rs b/src/libsyntax/parse/parser/item.rs
index c00a5807d52..d9fec72566c 100644
--- a/src/libsyntax/parse/parser/item.rs
+++ b/src/libsyntax/parse/parser/item.rs
@@ -149,17 +149,23 @@ impl<'a> Parser<'a> {
         }
         if self.eat_keyword(kw::Const) {
             let const_span = self.prev_span;
-            if self.check_keyword(kw::Fn)
-                || (self.check_keyword(kw::Unsafe)
-                    && self.is_keyword_ahead(1, &[kw::Fn])) {
+            if [kw::Fn, kw::Unsafe, kw::Extern].iter().any(|k| self.check_keyword(*k)) {
                 // CONST FUNCTION ITEM
+
                 let unsafety = self.parse_unsafety();
-                self.bump();
+
+                if self.check_keyword(kw::Extern) {
+                    self.sess.gated_spans.const_extern_fn.borrow_mut().push(
+                        lo.to(self.token.span)
+                    );
+                }
+                let abi = self.parse_extern_abi()?;
+
                 let header = FnHeader {
                     unsafety,
                     asyncness: respan(const_span, IsAsync::NotAsync),
                     constness: respan(const_span, Constness::Const),
-                    abi: Abi::Rust,
+                    abi,
                 };
                 return self.parse_item_fn(lo, visibility, attrs, header);
             }
@@ -257,11 +263,7 @@ impl<'a> Parser<'a> {
             self.bump(); // `unsafe`
             // `{` is also expected after `unsafe`; in case of error, include it in the diagnostic.
             self.check(&token::OpenDelim(token::Brace));
-            let abi = if self.eat_keyword(kw::Extern) {
-                self.parse_opt_abi()?.unwrap_or(Abi::C)
-            } else {
-                Abi::Rust
-            };
+            let abi = self.parse_extern_abi()?;
             self.expect_keyword(kw::Fn)?;
             let fn_span = self.prev_span;
             let header = FnHeader {
@@ -834,11 +836,7 @@ impl<'a> Parser<'a> {
         let (constness, unsafety, abi) = if is_const_fn {
             (respan(const_span, Constness::Const), unsafety, Abi::Rust)
         } else {
-            let abi = if self.eat_keyword(kw::Extern) {
-                self.parse_opt_abi()?.unwrap_or(Abi::C)
-            } else {
-                Abi::Rust
-            };
+            let abi = self.parse_extern_abi()?;
             (respan(self.prev_span, Constness::NotConst), unsafety, abi)
         };
         if !self.eat_keyword(kw::Fn) {
@@ -1278,14 +1276,30 @@ impl<'a> Parser<'a> {
         // Treat `const` as `static` for error recovery, but don't add it to expected tokens.
         if self.check_keyword(kw::Static) || self.token.is_keyword(kw::Const) {
             if self.token.is_keyword(kw::Const) {
-                self.diagnostic()
-                    .struct_span_err(self.token.span, "extern items cannot be `const`")
-                    .span_suggestion(
+                let mut err = self
+                    .struct_span_err(self.token.span, "extern items cannot be `const`");
+
+
+                // The user wrote 'const fn'
+                if self.is_keyword_ahead(1, &[kw::Fn, kw::Unsafe]) {
+                    err.emit();
+                    // Consume `const`
+                    self.bump();
+                    // Consume `unsafe` if present, since `extern` blocks
+                    // don't allow it. This will leave behind a plain 'fn'
+                    self.eat_keyword(kw::Unsafe);
+                    // Treat 'const fn` as a plain `fn` for error recovery purposes.
+                    // We've already emitted an error, so compilation is guaranteed
+                    // to fail
+                    return Ok(self.parse_item_foreign_fn(visibility, lo, attrs, extern_sp)?);
+                }
+                err.span_suggestion(
                         self.token.span,
                         "try using a static value",
                         "static".to_owned(),
                         Applicability::MachineApplicable
-                    ).emit();
+                );
+                err.emit();
             }
             self.bump(); // `static` or `const`
             return Ok(self.parse_item_foreign_static(visibility, lo, attrs)?);