Skip to content

Commit

Permalink
Auto merge of #129753 - folkertdev:stabilize-const-extern-fn, r=RalfJung
Browse files Browse the repository at this point in the history
stabilize `const_extern_fn`

closes #64926

tracking issue: #64926
reference PR: rust-lang/reference#1596

## Stabilizaton Report

### Summary

Using `const extern "Rust"` and `const extern "C"` was already stabilized (since version 1.62.0, see #95346). This PR stabilizes the other calling conventions: it is now possible to write  `const unsafe extern "calling-convention" fn` and `const extern "calling-convention" fn` for any supported calling convention:

```rust
const extern "C-unwind" fn foo1(val: u8) -> u8 { val + 1}
const extern "stdcall" fn foo2(val: u8) -> u8 { val + 1}
const unsafe extern "C-unwind" fn bar1(val: bool) -> bool { !val }
const unsafe extern "stdcall" fn bar2(val: bool) -> bool { !val }
```

This can be used to const-ify an `extern fn`, or conversely, to make a `const fn` callable from external code.

r? T-lang

cc `@RalfJung`
  • Loading branch information
bors committed Sep 14, 2024
2 parents 9b72238 + a528f4e commit 4f1be92
Show file tree
Hide file tree
Showing 29 changed files with 186 additions and 235 deletions.
25 changes: 6 additions & 19 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,22 +75,9 @@ struct PostExpansionVisitor<'a> {

impl<'a> PostExpansionVisitor<'a> {
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
fn check_abi(&self, abi: ast::StrLit, constness: ast::Const) {
fn check_abi(&self, abi: ast::StrLit) {
let ast::StrLit { symbol_unescaped, span, .. } = abi;

if let ast::Const::Yes(_) = constness {
match symbol_unescaped {
// Stable
sym::Rust | sym::C => {}
abi => gate!(
&self,
const_extern_fn,
span,
format!("`{}` as a `const fn` ABI is unstable", abi)
),
}
}

match abi::is_enabled(self.features, span, symbol_unescaped.as_str()) {
Ok(()) => (),
Err(abi::AbiDisabled::Unstable { feature, explain }) => {
Expand All @@ -110,9 +97,9 @@ impl<'a> PostExpansionVisitor<'a> {
}
}

fn check_extern(&self, ext: ast::Extern, constness: ast::Const) {
fn check_extern(&self, ext: ast::Extern) {
if let ast::Extern::Explicit(abi, _) = ext {
self.check_abi(abi, constness);
self.check_abi(abi);
}
}

Expand Down Expand Up @@ -239,7 +226,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
match &i.kind {
ast::ItemKind::ForeignMod(foreign_module) => {
if let Some(abi) = foreign_module.abi {
self.check_abi(abi, ast::Const::No);
self.check_abi(abi);
}
}

Expand Down Expand Up @@ -341,7 +328,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
match &ty.kind {
ast::TyKind::BareFn(bare_fn_ty) => {
// Function pointers cannot be `const`
self.check_extern(bare_fn_ty.ext, ast::Const::No);
self.check_extern(bare_fn_ty.ext);
self.check_late_bound_lifetime_defs(&bare_fn_ty.generic_params);
}
ast::TyKind::Never => {
Expand Down Expand Up @@ -446,7 +433,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) {
if let Some(header) = fn_kind.header() {
// Stability of const fn methods are covered in `visit_assoc_item` below.
self.check_extern(header.ext, header.constness);
self.check_extern(header.ext);
}

if let FnKind::Closure(ast::ClosureBinder::For { generic_params, .. }, ..) = fn_kind {
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/accepted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ declare_features! (
(accepted, conservative_impl_trait, "1.26.0", Some(34511)),
/// Allows calling constructor functions in `const fn`.
(accepted, const_constructor, "1.40.0", Some(61456)),
/// Allows the definition of `const extern fn` and `const unsafe extern fn`.
(accepted, const_extern_fn, "CURRENT_RUSTC_VERSION", Some(64926)),
/// Allows basic arithmetic on floating point types in a `const fn`.
(accepted, const_fn_floating_point_arithmetic, "1.82.0", Some(57241)),
/// Allows using and casting function pointers in a `const fn`.
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,8 +401,6 @@ declare_features! (
(unstable, const_async_blocks, "1.53.0", Some(85368)),
/// Allows `const || {}` closures in const contexts.
(incomplete, const_closures, "1.68.0", Some(106003)),
/// Allows the definition of `const extern fn` and `const unsafe extern fn`.
(unstable, const_extern_fn, "1.40.0", Some(64926)),
/// Allows `for _ in _` loops in const contexts.
(unstable, const_for, "1.56.0", Some(87575)),
/// Allows using `&mut` in constant functions.
Expand Down
3 changes: 0 additions & 3 deletions compiler/rustc_parse/src/parser/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -607,9 +607,6 @@ impl<'a> Parser<'a> {
let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?;
let whole_span = lo.to(self.prev_token.span);
if let ast::Const::Yes(span) = constness {
// If we ever start to allow `const fn()`, then update
// feature gating for `#![feature(const_extern_fn)]` to
// cover it.
self.dcx().emit_err(FnPointerCannotBeConst { span: whole_span, qualifier: span });
}
if let Some(ast::CoroutineKind::Async { span, .. }) = coroutine_kind {
Expand Down
3 changes: 2 additions & 1 deletion src/tools/clippy/clippy_config/src/msrvs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ macro_rules! msrv_aliases {

// names may refer to stabilized feature flags or library items
msrv_aliases! {
1,83,0 { CONST_EXTERN_FN }
1,83,0 { CONST_FLOAT_BITS_CONV }
1,81,0 { LINT_REASONS_STABILIZATION }
1,80,0 { BOX_INTO_ITER}
Expand All @@ -27,7 +28,7 @@ msrv_aliases! {
1,68,0 { PATH_MAIN_SEPARATOR_STR }
1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS }
1,63,0 { CLONE_INTO }
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE, CONST_EXTERN_FN }
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE, CONST_EXTERN_C_FN }
1,59,0 { THREAD_LOCAL_CONST_INIT }
1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF }
1,56,0 { CONST_FN_UNION }
Expand Down
10 changes: 4 additions & 6 deletions src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
.iter()
.any(|param| matches!(param.kind, GenericParamKind::Const { .. }));

if already_const(header)
|| has_const_generic_params
|| !could_be_const_with_abi(cx, &self.msrv, header.abi)
if already_const(header) || has_const_generic_params || !could_be_const_with_abi(&self.msrv, header.abi)
{
return;
}
Expand Down Expand Up @@ -183,13 +181,13 @@ fn already_const(header: hir::FnHeader) -> bool {
header.constness == Constness::Const
}

fn could_be_const_with_abi(cx: &LateContext<'_>, msrv: &Msrv, abi: Abi) -> bool {
fn could_be_const_with_abi(msrv: &Msrv, abi: Abi) -> bool {
match abi {
Abi::Rust => true,
// `const extern "C"` was stabilized after 1.62.0
Abi::C { unwind: false } => msrv.meets(msrvs::CONST_EXTERN_FN),
Abi::C { unwind: false } => msrv.meets(msrvs::CONST_EXTERN_C_FN),
// Rest ABIs are still unstable and need the `const_extern_fn` feature enabled.
_ => cx.tcx.features().const_extern_fn,
_ => msrv.meets(msrvs::CONST_EXTERN_FN),
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,6 @@ mod msrv {
extern "C" fn c() {}
}

mod with_extern {
extern "C-unwind" fn c_unwind() {}
extern "system" fn system() {}
extern "system-unwind" fn system_unwind() {}
}

mod with_ty_alias {
type Foo = impl std::fmt::Debug;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![warn(clippy::missing_const_for_fn)]
#![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)]
#![allow(unsupported_calling_conventions)]
#![feature(const_mut_refs)]
#![feature(const_trait_impl)]

Expand Down Expand Up @@ -204,3 +205,16 @@ mod with_ty_alias {
// in this test.
const fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {}
}

mod extern_fn {
const extern "C-unwind" fn c_unwind() {}
//~^ ERROR: this could be a `const fn`
const extern "system" fn system() {}
//~^ ERROR: this could be a `const fn`
const extern "system-unwind" fn system_unwind() {}
//~^ ERROR: this could be a `const fn`
pub const extern "stdcall" fn std_call() {}
//~^ ERROR: this could be a `const fn`
pub const extern "stdcall-unwind" fn std_call_unwind() {}
//~^ ERROR: this could be a `const fn`
}
14 changes: 14 additions & 0 deletions src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![warn(clippy::missing_const_for_fn)]
#![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)]
#![allow(unsupported_calling_conventions)]
#![feature(const_mut_refs)]
#![feature(const_trait_impl)]

Expand Down Expand Up @@ -204,3 +205,16 @@ mod with_ty_alias {
// in this test.
fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {}
}

mod extern_fn {
extern "C-unwind" fn c_unwind() {}
//~^ ERROR: this could be a `const fn`
extern "system" fn system() {}
//~^ ERROR: this could be a `const fn`
extern "system-unwind" fn system_unwind() {}
//~^ ERROR: this could be a `const fn`
pub extern "stdcall" fn std_call() {}
//~^ ERROR: this could be a `const fn`
pub extern "stdcall-unwind" fn std_call_unwind() {}
//~^ ERROR: this could be a `const fn`
}
Loading

0 comments on commit 4f1be92

Please sign in to comment.