Skip to content

Add explicit_extern_abis Feature and Enforce Explicit ABIs #135340

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

Merged
merged 3 commits into from
Apr 17, 2025
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
4 changes: 4 additions & 0 deletions compiler/rustc_ast_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ ast_passes_extern_types_cannot = `type`s inside `extern` blocks cannot have {$de
.suggestion = remove the {$remove_descr}
.label = `extern` block begins here
ast_passes_extern_without_abi = `extern` declarations without an explicit ABI are disallowed
.suggestion = specify an ABI
.help = prior to Rust 2024, a default ABI was inferred
ast_passes_feature_on_non_nightly = `#![feature]` may not be used on the {$channel} release channel
.suggestion = remove the attribute
.stable_since = the feature `{$name}` has been stable since `{$since}` and no longer requires an attribute to enable
Expand Down
12 changes: 7 additions & 5 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,7 @@ impl<'a> AstValidator<'a> {
self.dcx().emit_err(errors::PatternFnPointer { span });
});
if let Extern::Implicit(extern_span) = bfty.ext {
self.maybe_lint_missing_abi(extern_span, ty.id);
self.handle_missing_abi(extern_span, ty.id);
}
}
TyKind::TraitObject(bounds, ..) => {
Expand Down Expand Up @@ -717,10 +717,12 @@ impl<'a> AstValidator<'a> {
}
}

fn maybe_lint_missing_abi(&mut self, span: Span, id: NodeId) {
fn handle_missing_abi(&mut self, span: Span, id: NodeId) {
// FIXME(davidtwco): This is a hack to detect macros which produce spans of the
// call site which do not have a macro backtrace. See #61963.
if self
if span.edition().at_least_edition_future() && self.features.explicit_extern_abis() {
self.dcx().emit_err(errors::MissingAbi { span });
} else if self
.sess
.source_map()
.span_to_snippet(span)
Expand Down Expand Up @@ -996,7 +998,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}

if abi.is_none() {
self.maybe_lint_missing_abi(*extern_span, item.id);
self.handle_missing_abi(*extern_span, item.id);
}
self.with_in_extern_mod(*safety, |this| {
visit::walk_item(this, item);
Expand Down Expand Up @@ -1370,7 +1372,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
},
) = fk
{
self.maybe_lint_missing_abi(*extern_span, id);
self.handle_missing_abi(*extern_span, id);
}

// Functions without bodies cannot have patterns.
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -823,3 +823,12 @@ pub(crate) struct DuplicatePreciseCapturing {
#[label]
pub bound2: Span,
}

#[derive(Diagnostic)]
#[diag(ast_passes_extern_without_abi)]
#[help]
pub(crate) struct MissingAbi {
#[primary_span]
#[suggestion(code = "extern \"<abi>\"", applicability = "has-placeholders")]
pub span: Span,
}
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,8 @@ declare_features! (
(incomplete, ergonomic_clones, "1.87.0", Some(132290)),
/// Allows exhaustive pattern matching on types that contain uninhabited types.
(unstable, exhaustive_patterns, "1.13.0", Some(51085)),
/// Disallows `extern` without an explicit ABI.
(unstable, explicit_extern_abis, "CURRENT_RUSTC_VERSION", Some(134986)),
/// Allows explicit tail calls via `become` expression.
(incomplete, explicit_tail_calls, "1.72.0", Some(112788)),
/// Allows using `aapcs`, `efiapi`, `sysv64` and `win64` as calling conventions
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ lint_expectation = this lint expectation is unfulfilled
lint_extern_crate_not_idiomatic = `extern crate` is not idiomatic in the new edition
.suggestion = convert it to a `use`

lint_extern_without_abi = extern declarations without an explicit ABI are deprecated
lint_extern_without_abi = `extern` declarations without an explicit ABI are deprecated
.label = ABI should be specified here
.suggestion = explicitly specify the {$default_abi} ABI

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,7 @@ symbols! {
expf16,
expf32,
expf64,
explicit_extern_abis,
explicit_generic_args_with_impl_trait,
explicit_tail_calls,
export_name,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# `explicit_extern_abis`

The tracking issue for this feature is: #134986

------

Disallow `extern` without an explicit ABI. We should write `extern "C"`
(or another ABI) instead of just `extern`.

By making the ABI explicit, it becomes much clearer that "C" is just one of the
possible choices, rather than the "standard" way for external functions.
Removing the default makes it easier to add a new ABI on equal footing as "C".

```rust,editionfuture,compile_fail
#![feature(explicit_extern_abis)]

extern fn function1() {} // ERROR `extern` declarations without an explicit ABI
// are disallowed

extern "C" fn function2() {} // compiles

extern "aapcs" fn function3() {} // compiles
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// The purpose of this feature gate is to make something into a hard error in a
// future edition. Consequently, this test differs from most other feature gate
// tests. Instead of verifying that an error occurs when the feature gate is
// missing, it ensures that the hard error is only produced with the feature
// gate is present in the `future` edition -- and otherwise that only a warning
// is emitted.

//@ revisions: current current_feature future future_feature

//@ [current] run-rustfix
//@ [current] check-pass

//@ [current_feature] run-rustfix
//@ [current_feature] check-pass

//@ [future] edition: future
//@ [future] compile-flags: -Z unstable-options
//@ [future] run-rustfix
//@ [future] check-pass

//@ [future_feature] edition: future
//@ [future_feature] compile-flags: -Z unstable-options

#![cfg_attr(future_feature, feature(explicit_extern_abis))]
#![cfg_attr(current_feature, feature(explicit_extern_abis))]

extern "C" fn _foo() {}
//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated
//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed

unsafe extern "C" fn _bar() {}
//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated
//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed

unsafe extern "C" {}
//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated
//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
warning: `extern` declarations without an explicit ABI are deprecated
--> $DIR/feature-gate-explicit-extern-abis.rs:27:1
|
LL | extern fn _foo() {}
| ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"`
|
= note: `#[warn(missing_abi)]` on by default

warning: `extern` declarations without an explicit ABI are deprecated
--> $DIR/feature-gate-explicit-extern-abis.rs:33:8
|
LL | unsafe extern fn _bar() {}
| ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"`

warning: `extern` declarations without an explicit ABI are deprecated
--> $DIR/feature-gate-explicit-extern-abis.rs:39:8
|
LL | unsafe extern {}
| ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"`

warning: 3 warnings emitted

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// The purpose of this feature gate is to make something into a hard error in a
// future edition. Consequently, this test differs from most other feature gate
// tests. Instead of verifying that an error occurs when the feature gate is
// missing, it ensures that the hard error is only produced with the feature
// gate is present in the `future` edition -- and otherwise that only a warning
// is emitted.

//@ revisions: current current_feature future future_feature

//@ [current] run-rustfix
//@ [current] check-pass

//@ [current_feature] run-rustfix
//@ [current_feature] check-pass

//@ [future] edition: future
//@ [future] compile-flags: -Z unstable-options
//@ [future] run-rustfix
//@ [future] check-pass

//@ [future_feature] edition: future
//@ [future_feature] compile-flags: -Z unstable-options

#![cfg_attr(future_feature, feature(explicit_extern_abis))]
#![cfg_attr(current_feature, feature(explicit_extern_abis))]

extern "C" fn _foo() {}
//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated
//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed

unsafe extern "C" fn _bar() {}
//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated
//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed

unsafe extern "C" {}
//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated
//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
warning: `extern` declarations without an explicit ABI are deprecated
--> $DIR/feature-gate-explicit-extern-abis.rs:27:1
|
LL | extern fn _foo() {}
| ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"`
|
= note: `#[warn(missing_abi)]` on by default

warning: `extern` declarations without an explicit ABI are deprecated
--> $DIR/feature-gate-explicit-extern-abis.rs:33:8
|
LL | unsafe extern fn _bar() {}
| ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"`

warning: `extern` declarations without an explicit ABI are deprecated
--> $DIR/feature-gate-explicit-extern-abis.rs:39:8
|
LL | unsafe extern {}
| ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"`

warning: 3 warnings emitted

Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// The purpose of this feature gate is to make something into a hard error in a
// future edition. Consequently, this test differs from most other feature gate
// tests. Instead of verifying that an error occurs when the feature gate is
// missing, it ensures that the hard error is only produced with the feature
// gate is present in the `future` edition -- and otherwise that only a warning
// is emitted.

//@ revisions: current current_feature future future_feature

//@ [current] run-rustfix
//@ [current] check-pass

//@ [current_feature] run-rustfix
//@ [current_feature] check-pass

//@ [future] edition: future
//@ [future] compile-flags: -Z unstable-options
//@ [future] run-rustfix
//@ [future] check-pass

//@ [future_feature] edition: future
//@ [future_feature] compile-flags: -Z unstable-options

#![cfg_attr(future_feature, feature(explicit_extern_abis))]
#![cfg_attr(current_feature, feature(explicit_extern_abis))]

extern "C" fn _foo() {}
//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated
//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed

unsafe extern "C" fn _bar() {}
//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated
//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed

unsafe extern "C" {}
//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated
//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
warning: `extern` declarations without an explicit ABI are deprecated
--> $DIR/feature-gate-explicit-extern-abis.rs:27:1
|
LL | extern fn _foo() {}
| ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"`
|
= note: `#[warn(missing_abi)]` on by default

warning: `extern` declarations without an explicit ABI are deprecated
--> $DIR/feature-gate-explicit-extern-abis.rs:33:8
|
LL | unsafe extern fn _bar() {}
| ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"`

warning: `extern` declarations without an explicit ABI are deprecated
--> $DIR/feature-gate-explicit-extern-abis.rs:39:8
|
LL | unsafe extern {}
| ^^^^^^ help: explicitly specify the "C" ABI: `extern "C"`

warning: 3 warnings emitted

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
error: `extern` declarations without an explicit ABI are disallowed
--> $DIR/feature-gate-explicit-extern-abis.rs:27:1
|
LL | extern fn _foo() {}
| ^^^^^^ help: specify an ABI: `extern "<abi>"`
|
= help: prior to Rust 2024, a default ABI was inferred

error: `extern` declarations without an explicit ABI are disallowed
--> $DIR/feature-gate-explicit-extern-abis.rs:33:8
|
LL | unsafe extern fn _bar() {}
| ^^^^^^ help: specify an ABI: `extern "<abi>"`
|
= help: prior to Rust 2024, a default ABI was inferred

error: `extern` declarations without an explicit ABI are disallowed
--> $DIR/feature-gate-explicit-extern-abis.rs:39:8
|
LL | unsafe extern {}
| ^^^^^^ help: specify an ABI: `extern "<abi>"`
|
= help: prior to Rust 2024, a default ABI was inferred

error: aborting due to 3 previous errors

45 changes: 45 additions & 0 deletions tests/ui/feature-gates/feature-gate-explicit-extern-abis.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// The purpose of this feature gate is to make something into a hard error in a
// future edition. Consequently, this test differs from most other feature gate
// tests. Instead of verifying that an error occurs when the feature gate is
// missing, it ensures that the hard error is only produced with the feature
// gate is present in the `future` edition -- and otherwise that only a warning
// is emitted.

//@ revisions: current current_feature future future_feature

//@ [current] run-rustfix
//@ [current] check-pass

//@ [current_feature] run-rustfix
//@ [current_feature] check-pass

//@ [future] edition: future
//@ [future] compile-flags: -Z unstable-options
//@ [future] run-rustfix
//@ [future] check-pass

//@ [future_feature] edition: future
//@ [future_feature] compile-flags: -Z unstable-options

#![cfg_attr(future_feature, feature(explicit_extern_abis))]
#![cfg_attr(current_feature, feature(explicit_extern_abis))]

extern fn _foo() {}
//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated
//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed

unsafe extern fn _bar() {}
//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated
//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed

unsafe extern {}
//[current]~^ WARN `extern` declarations without an explicit ABI are deprecated
//[current_feature]~^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future]~^^^ WARN `extern` declarations without an explicit ABI are deprecated
//[future_feature]~^^^^ ERROR `extern` declarations without an explicit ABI are disallowed

fn main() {}
Loading
Loading