diff options
| author | Aaron Hill <aa1ronham@gmail.com> | 2019-09-29 19:22:18 -0400 |
|---|---|---|
| committer | Aaron Hill <aa1ronham@gmail.com> | 2019-10-02 00:38:52 -0400 |
| commit | 73b50d211b254bf72ce10b5ea4c3e0033e3189f4 (patch) | |
| tree | ffd2406f05d8bae7607cac2399493bbd331657ef /src/libsyntax | |
| parent | 7130fc54e05e247f93c7ecc2d10f56b314c97831 (diff) | |
| download | rust-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')
| -rw-r--r-- | src/libsyntax/feature_gate/active.rs | 3 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate/check.rs | 1 | ||||
| -rw-r--r-- | src/libsyntax/parse/mod.rs | 2 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 9 | ||||
| -rw-r--r-- | src/libsyntax/parse/parser/item.rs | 52 |
5 files changed, 48 insertions, 19 deletions
diff --git a/src/libsyntax/feature_gate/active.rs b/src/libsyntax/feature_gate/active.rs index 47ee41f0adc..20a77fa37cf 100644 --- a/src/libsyntax/feature_gate/active.rs +++ b/src/libsyntax/feature_gate/active.rs @@ -519,6 +519,9 @@ declare_features! ( /// Allows the use of or-patterns (e.g., `0 | 1`). (active, or_patterns, "1.38.0", Some(54883), None), + /// Allows the definition of `const extern fn` and `const unsafe extern fn`. + (active, const_extern_fn, "1.40.0", Some(64926), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/src/libsyntax/feature_gate/check.rs b/src/libsyntax/feature_gate/check.rs index d7fc74955bb..9e40b1a26ac 100644 --- a/src/libsyntax/feature_gate/check.rs +++ b/src/libsyntax/feature_gate/check.rs @@ -821,6 +821,7 @@ pub fn check_crate(krate: &ast::Crate, gate_all!(async_closure, "async closures are unstable"); gate_all!(yields, generators, "yield syntax is experimental"); gate_all!(or_patterns, "or-patterns syntax is experimental"); + gate_all!(const_extern_fn, "`const extern fn` definitions are unstable"); visit::walk_crate(&mut visitor, krate); } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index fa4c1043122..1518da23b09 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -57,6 +57,8 @@ pub struct GatedSpans { pub yields: Lock<Vec<Span>>, /// Spans collected for gating `or_patterns`, e.g. `Some(Foo | Bar)`. pub or_patterns: Lock<Vec<Span>>, + /// Spans collected for gating `const_extern_fn`, e.g. `const extern fn foo`. + pub const_extern_fn: Lock<Vec<Span>>, } /// Info about a parsing session. diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 95f84d5cb33..6b0b60bfe47 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1486,6 +1486,15 @@ impl<'a> Parser<'a> { Ok(()) } + /// Parses `extern` followed by an optional ABI string, or nothing. + fn parse_extern_abi(&mut self) -> PResult<'a, Abi> { + if self.eat_keyword(kw::Extern) { + Ok(self.parse_opt_abi()?.unwrap_or(Abi::C)) + } else { + Ok(Abi::Rust) + } + } + /// Parses a string as an ABI spec on an extern type or module. Consumes /// the `extern` keyword, if one is found. fn parse_opt_abi(&mut self) -> PResult<'a, Option<Abi>> { 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)?); |
