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.
This commit is contained in:
parent
7130fc54e0
commit
73b50d211b
18 changed files with 302 additions and 21 deletions
|
@ -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
|
||||
// -------------------------------------------------------------------------
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>> {
|
||||
|
|
|
@ -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)?);
|
||||
|
|
|
@ -196,6 +196,7 @@ symbols! {
|
|||
console,
|
||||
const_compare_raw_pointers,
|
||||
const_constructor,
|
||||
const_extern_fn,
|
||||
const_fn,
|
||||
const_fn_union,
|
||||
const_generics,
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
#![feature(const_extern_fn)]
|
||||
|
||||
extern "C" {
|
||||
fn regular_in_block();
|
||||
}
|
||||
|
||||
const extern fn bar() {
|
||||
unsafe {
|
||||
regular_in_block();
|
||||
//~^ ERROR: cannot call functions with `"C"` abi in `min_const_fn`
|
||||
}
|
||||
}
|
||||
|
||||
extern fn regular() {}
|
||||
|
||||
const extern fn foo() {
|
||||
unsafe {
|
||||
regular();
|
||||
//~^ ERROR: cannot call functions with `"C"` abi in `min_const_fn`
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,21 @@
|
|||
error[E0723]: cannot call functions with `"C"` abi in `min_const_fn`
|
||||
--> $DIR/const-extern-fn-call-extern-fn.rs:9:9
|
||||
|
|
||||
LL | regular_in_block();
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
|
||||
= help: add `#![feature(const_fn)]` to the crate attributes to enable
|
||||
|
||||
error[E0723]: cannot call functions with `"C"` abi in `min_const_fn`
|
||||
--> $DIR/const-extern-fn-call-extern-fn.rs:18:9
|
||||
|
|
||||
LL | regular();
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
|
||||
= help: add `#![feature(const_fn)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0723`.
|
|
@ -0,0 +1,13 @@
|
|||
#![feature(const_extern_fn)]
|
||||
|
||||
const extern fn unsize(x: &[u8; 3]) -> &[u8] { x }
|
||||
//~^ ERROR unsizing casts are not allowed in const fn
|
||||
const unsafe extern "C" fn closure() -> fn() { || {} }
|
||||
//~^ ERROR function pointers in const fn are unstable
|
||||
const unsafe extern fn use_float() { 1.0 + 1.0; }
|
||||
//~^ ERROR only int, `bool` and `char` operations are stable in const fn
|
||||
const extern "C" fn ptr_cast(val: *const u8) { val as usize; }
|
||||
//~^ ERROR casting pointers to ints is unstable in const fn
|
||||
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,39 @@
|
|||
error[E0723]: unsizing casts are not allowed in const fn
|
||||
--> $DIR/const-extern-fn-min-const-fn.rs:3:48
|
||||
|
|
||||
LL | const extern fn unsize(x: &[u8; 3]) -> &[u8] { x }
|
||||
| ^
|
||||
|
|
||||
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
|
||||
= help: add `#![feature(const_fn)]` to the crate attributes to enable
|
||||
|
||||
error[E0723]: function pointers in const fn are unstable
|
||||
--> $DIR/const-extern-fn-min-const-fn.rs:5:41
|
||||
|
|
||||
LL | const unsafe extern "C" fn closure() -> fn() { || {} }
|
||||
| ^^^^
|
||||
|
|
||||
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
|
||||
= help: add `#![feature(const_fn)]` to the crate attributes to enable
|
||||
|
||||
error[E0723]: only int, `bool` and `char` operations are stable in const fn
|
||||
--> $DIR/const-extern-fn-min-const-fn.rs:7:38
|
||||
|
|
||||
LL | const unsafe extern fn use_float() { 1.0 + 1.0; }
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
|
||||
= help: add `#![feature(const_fn)]` to the crate attributes to enable
|
||||
|
||||
error[E0723]: casting pointers to ints is unstable in const fn
|
||||
--> $DIR/const-extern-fn-min-const-fn.rs:9:48
|
||||
|
|
||||
LL | const extern "C" fn ptr_cast(val: *const u8) { val as usize; }
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: for more information, see issue https://github.com/rust-lang/rust/issues/57563
|
||||
= help: add `#![feature(const_fn)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0723`.
|
|
@ -0,0 +1,10 @@
|
|||
#![feature(const_extern_fn)]
|
||||
|
||||
const unsafe extern fn foo() -> usize { 5 }
|
||||
|
||||
fn main() {
|
||||
let a: [u8; foo()];
|
||||
//~^ ERROR call to unsafe function is unsafe and requires unsafe function or block
|
||||
foo();
|
||||
//~^ ERROR call to unsafe function is unsafe and requires unsafe function or block
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
|
||||
--> $DIR/const-extern-fn-requires-unsafe.rs:8:5
|
||||
|
|
||||
LL | foo();
|
||||
| ^^^^^ call to unsafe function
|
||||
|
|
||||
= note: consult the function's documentation for information on how to avoid undefined behavior
|
||||
|
||||
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
|
||||
--> $DIR/const-extern-fn-requires-unsafe.rs:6:17
|
||||
|
|
||||
LL | let a: [u8; foo()];
|
||||
| ^^^^^ call to unsafe function
|
||||
|
|
||||
= note: consult the function's documentation for information on how to avoid undefined behavior
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0133`.
|
35
src/test/ui/consts/const-extern-fn/const-extern-fn.rs
Normal file
35
src/test/ui/consts/const-extern-fn/const-extern-fn.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
// run-pass
|
||||
#![feature(const_extern_fn)]
|
||||
|
||||
const extern fn foo1(val: u8) -> u8 {
|
||||
val + 1
|
||||
}
|
||||
|
||||
const extern "C" fn foo2(val: u8) -> u8 {
|
||||
val + 1
|
||||
}
|
||||
|
||||
const unsafe extern fn bar1(val: bool) -> bool {
|
||||
!val
|
||||
}
|
||||
|
||||
const unsafe extern "C" fn bar2(val: bool) -> bool {
|
||||
!val
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
let a: [u8; foo1(25) as usize] = [0; 26];
|
||||
let b: [u8; foo2(25) as usize] = [0; 26];
|
||||
assert_eq!(a, b);
|
||||
|
||||
let bar1_res = unsafe { bar1(false) };
|
||||
let bar2_res = unsafe { bar2(false) };
|
||||
assert!(bar1_res);
|
||||
assert_eq!(bar1_res, bar2_res);
|
||||
|
||||
let _foo1_cast: extern fn(u8) -> u8 = foo1;
|
||||
let _foo2_cast: extern fn(u8) -> u8 = foo2;
|
||||
let _bar1_cast: unsafe extern fn(bool) -> bool = bar1;
|
||||
let _bar2_cast: unsafe extern fn(bool) -> bool = bar2;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// Check that `const extern fn` and `const unsafe extern fn` are feature-gated.
|
||||
|
||||
#[cfg(FALSE)] const extern fn foo1() {} //~ ERROR `const extern fn` definitions are unstable
|
||||
#[cfg(FALSE)] const extern "C" fn foo2() {} //~ ERROR `const extern fn` definitions are unstable
|
||||
#[cfg(FALSE)] const extern "Rust" fn foo3() {} //~ ERROR `const extern fn` definitions are unstable
|
||||
#[cfg(FALSE)] const unsafe extern fn bar1() {} //~ ERROR `const extern fn` definitions are unstable
|
||||
#[cfg(FALSE)] const unsafe extern "C" fn bar2() {}
|
||||
//~^ ERROR `const extern fn` definitions are unstable
|
||||
#[cfg(FALSE)] const unsafe extern "Rust" fn bar3() {}
|
||||
//~^ ERROR `const extern fn` definitions are unstable
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,57 @@
|
|||
error[E0658]: `const extern fn` definitions are unstable
|
||||
--> $DIR/feature-gate-const_extern_fn.rs:3:15
|
||||
|
|
||||
LL | #[cfg(FALSE)] const extern fn foo1() {}
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: for more information, see https://github.com/rust-lang/rust/issues/64926
|
||||
= help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: `const extern fn` definitions are unstable
|
||||
--> $DIR/feature-gate-const_extern_fn.rs:4:15
|
||||
|
|
||||
LL | #[cfg(FALSE)] const extern "C" fn foo2() {}
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: for more information, see https://github.com/rust-lang/rust/issues/64926
|
||||
= help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: `const extern fn` definitions are unstable
|
||||
--> $DIR/feature-gate-const_extern_fn.rs:5:15
|
||||
|
|
||||
LL | #[cfg(FALSE)] const extern "Rust" fn foo3() {}
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= note: for more information, see https://github.com/rust-lang/rust/issues/64926
|
||||
= help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: `const extern fn` definitions are unstable
|
||||
--> $DIR/feature-gate-const_extern_fn.rs:6:15
|
||||
|
|
||||
LL | #[cfg(FALSE)] const unsafe extern fn bar1() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: for more information, see https://github.com/rust-lang/rust/issues/64926
|
||||
= help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: `const extern fn` definitions are unstable
|
||||
--> $DIR/feature-gate-const_extern_fn.rs:7:15
|
||||
|
|
||||
LL | #[cfg(FALSE)] const unsafe extern "C" fn bar2() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: for more information, see https://github.com/rust-lang/rust/issues/64926
|
||||
= help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: `const extern fn` definitions are unstable
|
||||
--> $DIR/feature-gate-const_extern_fn.rs:9:15
|
||||
|
|
||||
LL | #[cfg(FALSE)] const unsafe extern "Rust" fn bar3() {}
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: for more information, see https://github.com/rust-lang/rust/issues/64926
|
||||
= help: add `#![feature(const_extern_fn)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
8
src/test/ui/parser/no-const-fn-in-extern-block.rs
Normal file
8
src/test/ui/parser/no-const-fn-in-extern-block.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
extern {
|
||||
const fn foo();
|
||||
//~^ ERROR extern items cannot be `const`
|
||||
const unsafe fn bar();
|
||||
//~^ ERROR extern items cannot be `const`
|
||||
}
|
||||
|
||||
fn main() {}
|
14
src/test/ui/parser/no-const-fn-in-extern-block.stderr
Normal file
14
src/test/ui/parser/no-const-fn-in-extern-block.stderr
Normal file
|
@ -0,0 +1,14 @@
|
|||
error: extern items cannot be `const`
|
||||
--> $DIR/no-const-fn-in-extern-block.rs:2:5
|
||||
|
|
||||
LL | const fn foo();
|
||||
| ^^^^^
|
||||
|
||||
error: extern items cannot be `const`
|
||||
--> $DIR/no-const-fn-in-extern-block.rs:4:5
|
||||
|
|
||||
LL | const unsafe fn bar();
|
||||
| ^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
|
@ -8,9 +8,9 @@ LL | let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::tr
|
|||
| help: use parentheses to call this function: `std::mem::transmute(...)`
|
||||
|
|
||||
= note: expected type `unsafe extern "rust-intrinsic" fn(isize) -> usize`
|
||||
found type `unsafe extern "rust-intrinsic" fn(_) -> _ {std::intrinsics::transmute::<_, _>}`
|
||||
found type `unsafe extern "rust-intrinsic" fn(_) -> _ {std::mem::transmute::<_, _>}`
|
||||
|
||||
error[E0606]: casting `unsafe extern "rust-intrinsic" fn(_) -> _ {std::intrinsics::transmute::<_, _>}` as `unsafe extern "rust-intrinsic" fn(isize) -> usize` is invalid
|
||||
error[E0606]: casting `unsafe extern "rust-intrinsic" fn(_) -> _ {std::mem::transmute::<_, _>}` as `unsafe extern "rust-intrinsic" fn(isize) -> usize` is invalid
|
||||
--> $DIR/reify-intrinsic.rs:11:13
|
||||
|
|
||||
LL | let _ = std::mem::transmute as unsafe extern "rust-intrinsic" fn(isize) -> usize;
|
||||
|
|
Loading…
Add table
Reference in a new issue