Skip to content

Commit 73b50d2

Browse files
committed
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.
1 parent 7130fc5 commit 73b50d2

18 files changed

+302
-21
lines changed

src/libsyntax/feature_gate/active.rs

+3
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,9 @@ declare_features! (
519519
/// Allows the use of or-patterns (e.g., `0 | 1`).
520520
(active, or_patterns, "1.38.0", Some(54883), None),
521521

522+
/// Allows the definition of `const extern fn` and `const unsafe extern fn`.
523+
(active, const_extern_fn, "1.40.0", Some(64926), None),
524+
522525
// -------------------------------------------------------------------------
523526
// feature-group-end: actual feature gates
524527
// -------------------------------------------------------------------------

src/libsyntax/feature_gate/check.rs

+1
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,7 @@ pub fn check_crate(krate: &ast::Crate,
821821
gate_all!(async_closure, "async closures are unstable");
822822
gate_all!(yields, generators, "yield syntax is experimental");
823823
gate_all!(or_patterns, "or-patterns syntax is experimental");
824+
gate_all!(const_extern_fn, "`const extern fn` definitions are unstable");
824825

825826
visit::walk_crate(&mut visitor, krate);
826827
}

src/libsyntax/parse/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ pub struct GatedSpans {
5757
pub yields: Lock<Vec<Span>>,
5858
/// Spans collected for gating `or_patterns`, e.g. `Some(Foo | Bar)`.
5959
pub or_patterns: Lock<Vec<Span>>,
60+
/// Spans collected for gating `const_extern_fn`, e.g. `const extern fn foo`.
61+
pub const_extern_fn: Lock<Vec<Span>>,
6062
}
6163

6264
/// Info about a parsing session.

src/libsyntax/parse/parser.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1486,6 +1486,15 @@ impl<'a> Parser<'a> {
14861486
Ok(())
14871487
}
14881488

1489+
/// Parses `extern` followed by an optional ABI string, or nothing.
1490+
fn parse_extern_abi(&mut self) -> PResult<'a, Abi> {
1491+
if self.eat_keyword(kw::Extern) {
1492+
Ok(self.parse_opt_abi()?.unwrap_or(Abi::C))
1493+
} else {
1494+
Ok(Abi::Rust)
1495+
}
1496+
}
1497+
14891498
/// Parses a string as an ABI spec on an extern type or module. Consumes
14901499
/// the `extern` keyword, if one is found.
14911500
fn parse_opt_abi(&mut self) -> PResult<'a, Option<Abi>> {

src/libsyntax/parse/parser/item.rs

+33-19
Original file line numberDiff line numberDiff line change
@@ -149,17 +149,23 @@ impl<'a> Parser<'a> {
149149
}
150150
if self.eat_keyword(kw::Const) {
151151
let const_span = self.prev_span;
152-
if self.check_keyword(kw::Fn)
153-
|| (self.check_keyword(kw::Unsafe)
154-
&& self.is_keyword_ahead(1, &[kw::Fn])) {
152+
if [kw::Fn, kw::Unsafe, kw::Extern].iter().any(|k| self.check_keyword(*k)) {
155153
// CONST FUNCTION ITEM
154+
156155
let unsafety = self.parse_unsafety();
157-
self.bump();
156+
157+
if self.check_keyword(kw::Extern) {
158+
self.sess.gated_spans.const_extern_fn.borrow_mut().push(
159+
lo.to(self.token.span)
160+
);
161+
}
162+
let abi = self.parse_extern_abi()?;
163+
158164
let header = FnHeader {
159165
unsafety,
160166
asyncness: respan(const_span, IsAsync::NotAsync),
161167
constness: respan(const_span, Constness::Const),
162-
abi: Abi::Rust,
168+
abi,
163169
};
164170
return self.parse_item_fn(lo, visibility, attrs, header);
165171
}
@@ -257,11 +263,7 @@ impl<'a> Parser<'a> {
257263
self.bump(); // `unsafe`
258264
// `{` is also expected after `unsafe`; in case of error, include it in the diagnostic.
259265
self.check(&token::OpenDelim(token::Brace));
260-
let abi = if self.eat_keyword(kw::Extern) {
261-
self.parse_opt_abi()?.unwrap_or(Abi::C)
262-
} else {
263-
Abi::Rust
264-
};
266+
let abi = self.parse_extern_abi()?;
265267
self.expect_keyword(kw::Fn)?;
266268
let fn_span = self.prev_span;
267269
let header = FnHeader {
@@ -834,11 +836,7 @@ impl<'a> Parser<'a> {
834836
let (constness, unsafety, abi) = if is_const_fn {
835837
(respan(const_span, Constness::Const), unsafety, Abi::Rust)
836838
} else {
837-
let abi = if self.eat_keyword(kw::Extern) {
838-
self.parse_opt_abi()?.unwrap_or(Abi::C)
839-
} else {
840-
Abi::Rust
841-
};
839+
let abi = self.parse_extern_abi()?;
842840
(respan(self.prev_span, Constness::NotConst), unsafety, abi)
843841
};
844842
if !self.eat_keyword(kw::Fn) {
@@ -1278,14 +1276,30 @@ impl<'a> Parser<'a> {
12781276
// Treat `const` as `static` for error recovery, but don't add it to expected tokens.
12791277
if self.check_keyword(kw::Static) || self.token.is_keyword(kw::Const) {
12801278
if self.token.is_keyword(kw::Const) {
1281-
self.diagnostic()
1282-
.struct_span_err(self.token.span, "extern items cannot be `const`")
1283-
.span_suggestion(
1279+
let mut err = self
1280+
.struct_span_err(self.token.span, "extern items cannot be `const`");
1281+
1282+
1283+
// The user wrote 'const fn'
1284+
if self.is_keyword_ahead(1, &[kw::Fn, kw::Unsafe]) {
1285+
err.emit();
1286+
// Consume `const`
1287+
self.bump();
1288+
// Consume `unsafe` if present, since `extern` blocks
1289+
// don't allow it. This will leave behind a plain 'fn'
1290+
self.eat_keyword(kw::Unsafe);
1291+
// Treat 'const fn` as a plain `fn` for error recovery purposes.
1292+
// We've already emitted an error, so compilation is guaranteed
1293+
// to fail
1294+
return Ok(self.parse_item_foreign_fn(visibility, lo, attrs, extern_sp)?);
1295+
}
1296+
err.span_suggestion(
12841297
self.token.span,
12851298
"try using a static value",
12861299
"static".to_owned(),
12871300
Applicability::MachineApplicable
1288-
).emit();
1301+
);
1302+
err.emit();
12891303
}
12901304
self.bump(); // `static` or `const`
12911305
return Ok(self.parse_item_foreign_static(visibility, lo, attrs)?);

src/libsyntax_pos/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ symbols! {
196196
console,
197197
const_compare_raw_pointers,
198198
const_constructor,
199+
const_extern_fn,
199200
const_fn,
200201
const_fn_union,
201202
const_generics,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#![feature(const_extern_fn)]
2+
3+
extern "C" {
4+
fn regular_in_block();
5+
}
6+
7+
const extern fn bar() {
8+
unsafe {
9+
regular_in_block();
10+
//~^ ERROR: cannot call functions with `"C"` abi in `min_const_fn`
11+
}
12+
}
13+
14+
extern fn regular() {}
15+
16+
const extern fn foo() {
17+
unsafe {
18+
regular();
19+
//~^ ERROR: cannot call functions with `"C"` abi in `min_const_fn`
20+
}
21+
}
22+
23+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0723]: cannot call functions with `"C"` abi in `min_const_fn`
2+
--> $DIR/const-extern-fn-call-extern-fn.rs:9:9
3+
|
4+
LL | regular_in_block();
5+
| ^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
8+
= help: add `#![feature(const_fn)]` to the crate attributes to enable
9+
10+
error[E0723]: cannot call functions with `"C"` abi in `min_const_fn`
11+
--> $DIR/const-extern-fn-call-extern-fn.rs:18:9
12+
|
13+
LL | regular();
14+
| ^^^^^^^^^
15+
|
16+
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
17+
= help: add `#![feature(const_fn)]` to the crate attributes to enable
18+
19+
error: aborting due to 2 previous errors
20+
21+
For more information about this error, try `rustc --explain E0723`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![feature(const_extern_fn)]
2+
3+
const extern fn unsize(x: &[u8; 3]) -> &[u8] { x }
4+
//~^ ERROR unsizing casts are not allowed in const fn
5+
const unsafe extern "C" fn closure() -> fn() { || {} }
6+
//~^ ERROR function pointers in const fn are unstable
7+
const unsafe extern fn use_float() { 1.0 + 1.0; }
8+
//~^ ERROR only int, `bool` and `char` operations are stable in const fn
9+
const extern "C" fn ptr_cast(val: *const u8) { val as usize; }
10+
//~^ ERROR casting pointers to ints is unstable in const fn
11+
12+
13+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
error[E0723]: unsizing casts are not allowed in const fn
2+
--> $DIR/const-extern-fn-min-const-fn.rs:3:48
3+
|
4+
LL | const extern fn unsize(x: &[u8; 3]) -> &[u8] { x }
5+
| ^
6+
|
7+
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
8+
= help: add `#![feature(const_fn)]` to the crate attributes to enable
9+
10+
error[E0723]: function pointers in const fn are unstable
11+
--> $DIR/const-extern-fn-min-const-fn.rs:5:41
12+
|
13+
LL | const unsafe extern "C" fn closure() -> fn() { || {} }
14+
| ^^^^
15+
|
16+
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
17+
= help: add `#![feature(const_fn)]` to the crate attributes to enable
18+
19+
error[E0723]: only int, `bool` and `char` operations are stable in const fn
20+
--> $DIR/const-extern-fn-min-const-fn.rs:7:38
21+
|
22+
LL | const unsafe extern fn use_float() { 1.0 + 1.0; }
23+
| ^^^^^^^^^
24+
|
25+
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
26+
= help: add `#![feature(const_fn)]` to the crate attributes to enable
27+
28+
error[E0723]: casting pointers to ints is unstable in const fn
29+
--> $DIR/const-extern-fn-min-const-fn.rs:9:48
30+
|
31+
LL | const extern "C" fn ptr_cast(val: *const u8) { val as usize; }
32+
| ^^^^^^^^^^^^
33+
|
34+
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
35+
= help: add `#![feature(const_fn)]` to the crate attributes to enable
36+
37+
error: aborting due to 4 previous errors
38+
39+
For more information about this error, try `rustc --explain E0723`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#![feature(const_extern_fn)]
2+
3+
const unsafe extern fn foo() -> usize { 5 }
4+
5+
fn main() {
6+
let a: [u8; foo()];
7+
//~^ ERROR call to unsafe function is unsafe and requires unsafe function or block
8+
foo();
9+
//~^ ERROR call to unsafe function is unsafe and requires unsafe function or block
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
2+
--> $DIR/const-extern-fn-requires-unsafe.rs:8:5
3+
|
4+
LL | foo();
5+
| ^^^^^ call to unsafe function
6+
|
7+
= note: consult the function's documentation for information on how to avoid undefined behavior
8+
9+
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
10+
--> $DIR/const-extern-fn-requires-unsafe.rs:6:17
11+
|
12+
LL | let a: [u8; foo()];
13+
| ^^^^^ call to unsafe function
14+
|
15+
= note: consult the function's documentation for information on how to avoid undefined behavior
16+
17+
error: aborting due to 2 previous errors
18+
19+
For more information about this error, try `rustc --explain E0133`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// run-pass
2+
#![feature(const_extern_fn)]
3+
4+
const extern fn foo1(val: u8) -> u8 {
5+
val + 1
6+
}
7+
8+
const extern "C" fn foo2(val: u8) -> u8 {
9+
val + 1
10+
}
11+
12+
const unsafe extern fn bar1(val: bool) -> bool {
13+
!val
14+
}
15+
16+
const unsafe extern "C" fn bar2(val: bool) -> bool {
17+
!val
18+
}
19+
20+
21+
fn main() {
22+
let a: [u8; foo1(25) as usize] = [0; 26];
23+
let b: [u8; foo2(25) as usize] = [0; 26];
24+
assert_eq!(a, b);
25+
26+
let bar1_res = unsafe { bar1(false) };
27+
let bar2_res = unsafe { bar2(false) };
28+
assert!(bar1_res);
29+
assert_eq!(bar1_res, bar2_res);
30+
31+
let _foo1_cast: extern fn(u8) -> u8 = foo1;
32+
let _foo2_cast: extern fn(u8) -> u8 = foo2;
33+
let _bar1_cast: unsafe extern fn(bool) -> bool = bar1;
34+
let _bar2_cast: unsafe extern fn(bool) -> bool = bar2;
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Check that `const extern fn` and `const unsafe extern fn` are feature-gated.
2+
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
11+
12+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
error[E0658]: `const extern fn` definitions are unstable
2+
--> $DIR/feature-gate-const_extern_fn.rs:3:15
3+
|
4+
LL | #[cfg(FALSE)] const extern fn foo1() {}
5+
| ^^^^^^^^^^^^
6+
|
7+
= note: for more information, see https://github.com/rust-lang/rust/issues/64926
8+
= help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
9+
10+
error[E0658]: `const extern fn` definitions are unstable
11+
--> $DIR/feature-gate-const_extern_fn.rs:4:15
12+
|
13+
LL | #[cfg(FALSE)] const extern "C" fn foo2() {}
14+
| ^^^^^^^^^^^^
15+
|
16+
= note: for more information, see https://github.com/rust-lang/rust/issues/64926
17+
= help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
18+
19+
error[E0658]: `const extern fn` definitions are unstable
20+
--> $DIR/feature-gate-const_extern_fn.rs:5:15
21+
|
22+
LL | #[cfg(FALSE)] const extern "Rust" fn foo3() {}
23+
| ^^^^^^^^^^^^
24+
|
25+
= note: for more information, see https://github.com/rust-lang/rust/issues/64926
26+
= help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
27+
28+
error[E0658]: `const extern fn` definitions are unstable
29+
--> $DIR/feature-gate-const_extern_fn.rs:6:15
30+
|
31+
LL | #[cfg(FALSE)] const unsafe extern fn bar1() {}
32+
| ^^^^^^^^^^^^^^^^^^^
33+
|
34+
= note: for more information, see https://github.com/rust-lang/rust/issues/64926
35+
= help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
36+
37+
error[E0658]: `const extern fn` definitions are unstable
38+
--> $DIR/feature-gate-const_extern_fn.rs:7:15
39+
|
40+
LL | #[cfg(FALSE)] const unsafe extern "C" fn bar2() {}
41+
| ^^^^^^^^^^^^^^^^^^^
42+
|
43+
= note: for more information, see https://github.com/rust-lang/rust/issues/64926
44+
= help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
45+
46+
error[E0658]: `const extern fn` definitions are unstable
47+
--> $DIR/feature-gate-const_extern_fn.rs:9:15
48+
|
49+
LL | #[cfg(FALSE)] const unsafe extern "Rust" fn bar3() {}
50+
| ^^^^^^^^^^^^^^^^^^^
51+
|
52+
= note: for more information, see https://github.com/rust-lang/rust/issues/64926
53+
= help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
54+
55+
error: aborting due to 6 previous errors
56+
57+
For more information about this error, try `rustc --explain E0658`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
extern {
2+
const fn foo();
3+
//~^ ERROR extern items cannot be `const`
4+
const unsafe fn bar();
5+
//~^ ERROR extern items cannot be `const`
6+
}
7+
8+
fn main() {}

0 commit comments

Comments
 (0)