Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for const unsafe? extern fn #64906

Merged
merged 3 commits into from
Oct 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/libsyntax/feature_gate/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
// -------------------------------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax/feature_gate/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
2 changes: 2 additions & 0 deletions src/libsyntax/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
9 changes: 9 additions & 0 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, I was going over parser/ty.rs and I noticed that parse_ty_bare_fn has the exact same logic. Would be good to de-duplicate that.

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>> {
Expand Down
53 changes: 34 additions & 19 deletions src/libsyntax/parse/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,17 +149,24 @@ 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) {
Centril marked this conversation as resolved.
Show resolved Hide resolved
self.sess.gated_spans.const_extern_fn.borrow_mut().push(
lo.to(self.token.span)
);
}
let abi = self.parse_extern_abi()?;
self.bump(); // 'fn'

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);
}
Expand Down Expand Up @@ -257,11 +264,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 {
Expand Down Expand Up @@ -834,11 +837,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) {
Expand Down Expand Up @@ -1278,14 +1277,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`");
Centril marked this conversation as resolved.
Show resolved Hide resolved


// 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)?);
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax_pos/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ symbols! {
console,
const_compare_raw_pointers,
const_constructor,
const_extern_fn,
const_fn,
const_fn_union,
const_generics,
Expand Down
Original file line number Diff line number Diff line change
@@ -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`
}
}
Aaron1011 marked this conversation as resolved.
Show resolved Hide resolved

fn main() {}
Original file line number Diff line number Diff line change
@@ -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`.
13 changes: 13 additions & 0 deletions src/test/ui/consts/const-extern-fn/const-extern-fn-min-const-fn.rs
Original file line number Diff line number Diff line change
@@ -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() {}
Original file line number Diff line number Diff line change
@@ -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`.
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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 changes: 35 additions & 0 deletions src/test/ui/consts/const-extern-fn/const-extern-fn.rs
Original file line number Diff line number Diff line change
@@ -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;
}
12 changes: 12 additions & 0 deletions src/test/ui/consts/const-extern-fn/feature-gate-const_extern_fn.rs
Original file line number Diff line number Diff line change
@@ -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() {}
Original file line number Diff line number Diff line change
@@ -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 changes: 8 additions & 0 deletions src/test/ui/parser/no-const-fn-in-extern-block.rs
Original file line number Diff line number Diff line change
@@ -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() {}
Loading