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

Changes for unsafe extern blocks (RFC 3484) #1536

Merged
merged 9 commits into from
Aug 9, 2024
72 changes: 35 additions & 37 deletions src/items/external-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,34 +23,32 @@ blocks is only allowed in an `unsafe` context.

The external block defines its functions and statics in the [value namespace] of the module or block where it is located.

The `unsafe` keyword is syntactically allowed to appear before the `extern`
keyword, but it is rejected at a semantic level. This allows macros to consume
the syntax and make use of the `unsafe` keyword, before removing it from the
token stream.

## Functions

Functions within external blocks are declared in the same way as other Rust
functions, with the exception that they must not have a body and are instead
terminated by a semicolon. Patterns are not allowed in parameters, only
[IDENTIFIER] or `_` may be used. Function qualifiers (`const`, `async`,
`unsafe`, and `extern`) are not allowed.
[IDENTIFIER] or `_` may be used. The `safe` and `unsafe` function qualifiers are
allowed, but other function qualifiers (e.g. `const`, `async`, `extern`) are
not.

Functions within external blocks may be called by Rust code, just like
functions defined in Rust. The Rust compiler automatically translates between
the Rust ABI and the foreign ABI.

A function declared in an extern block is implicitly `unsafe`. When coerced to
a function pointer, a function declared in an extern block has type `unsafe
extern "abi" for<'l1, ..., 'lm> fn(A1, ..., An) -> R`, where `'l1`, ... `'lm`
are its lifetime parameters, `A1`, ..., `An` are the declared types of its
parameters and `R` is the declared return type.
A function declared in an extern block is implicitly `unsafe` unless the `safe`
function qualifier is present.

When coerced to a function pointer, a function declared in an extern block has
type `extern "abi" for<'l1, ..., 'lm> fn(A1, ..., An) -> R`, where `'l1`,
... `'lm` are its lifetime parameters, `A1`, ..., `An` are the declared types of
its parameters, `R` is the declared return type.

## Statics

Statics within external blocks are declared in the same way as [statics] outside of external blocks,
except that they do not have an expression initializing their value.
It is `unsafe` to access a static item declared in an extern block, whether or
Unless a static item declared in an extern block is qualified as `safe`, it is `unsafe` to access that item, whether or
not it's mutable, because there is nothing guaranteeing that the bit pattern at the static's
memory is valid for the type it is declared with, since some arbitrary (e.g. C) code is in charge
of initializing the static.
Expand All @@ -69,34 +67,34 @@ standard C ABI on the specific platform. Other ABIs may be specified using an

```rust
// Interface to the Windows API
extern "stdcall" { }
unsafe extern "stdcall" { }
```

There are three ABI strings which are cross-platform, and which all compilers
are guaranteed to support:

* `extern "Rust"` -- The default ABI when you write a normal `fn foo()` in any
* `unsafe extern "Rust"` -- The default ABI when you write a normal `fn foo()` in any
Rust code.
* `extern "C"` -- This is the same as `extern fn foo()`; whatever the default
* `unsafe extern "C"` -- This is the same as `extern fn foo()`; whatever the default
your C compiler supports.
* `extern "system"` -- Usually the same as `extern "C"`, except on Win32, in
* `unsafe extern "system"` -- Usually the same as `extern "C"`, except on Win32, in
which case it's `"stdcall"`, or what you should use to link to the Windows
API itself

There are also some platform-specific ABI strings:

* `extern "cdecl"` -- The default for x86\_32 C code.
* `extern "stdcall"` -- The default for the Win32 API on x86\_32.
* `extern "win64"` -- The default for C code on x86\_64 Windows.
* `extern "sysv64"` -- The default for C code on non-Windows x86\_64.
* `extern "aapcs"` -- The default for ARM.
* `extern "fastcall"` -- The `fastcall` ABI -- corresponds to MSVC's
* `unsafe extern "cdecl"` -- The default for x86\_32 C code.
* `unsafe extern "stdcall"` -- The default for the Win32 API on x86\_32.
* `unsafe extern "win64"` -- The default for C code on x86\_64 Windows.
* `unsafe extern "sysv64"` -- The default for C code on non-Windows x86\_64.
* `unsafe extern "aapcs"` -- The default for ARM.
* `unsafe extern "fastcall"` -- The `fastcall` ABI -- corresponds to MSVC's
`__fastcall` and GCC and clang's `__attribute__((fastcall))`
* `extern "vectorcall"` -- The `vectorcall` ABI -- corresponds to MSVC's
* `unsafe extern "vectorcall"` -- The `vectorcall` ABI -- corresponds to MSVC's
`__vectorcall` and clang's `__attribute__((vectorcall))`
* `extern "thiscall"` -- The default for C++ member functions on MSVC -- corresponds to MSVC's
* `unsafe extern "thiscall"` -- The default for C++ member functions on MSVC -- corresponds to MSVC's
`__thiscall` and GCC and clang's `__attribute__((thiscall))`
* `extern "efiapi"` -- The ABI used for [UEFI] functions.
* `unsafe extern "efiapi"` -- The ABI used for [UEFI] functions.

## Variadic functions

Expand All @@ -105,10 +103,10 @@ last argument. The variadic parameter may optionally be specified with an
identifier.

```rust
extern "C" {
fn foo(...);
fn bar(x: i32, ...);
fn with_name(format: *const u8, args: ...);
unsafe extern "C" {
safe fn foo(...);
unsafe fn bar(x: i32, ...);
unsafe fn with_name(format: *const u8, args: ...);
}
```

Expand Down Expand Up @@ -152,17 +150,17 @@ not specified.
<!-- ignore: requires extern linking -->
```rust,ignore
#[link(name = "crypto")]
extern {
unsafe extern {
// …
}

#[link(name = "CoreFoundation", kind = "framework")]
extern {
unsafe extern {
// …
}

#[link(wasm_import_module = "foo")]
extern {
unsafe extern {
// …
}
```
Expand Down Expand Up @@ -277,9 +275,9 @@ block to indicate the symbol to import for the given function or static. It
uses the [_MetaNameValueStr_] syntax to specify the name of the symbol.

```rust
extern {
unsafe extern {
#[link_name = "actual_symbol_name"]
fn name_in_rust();
safe fn name_in_rust();
}
```

Expand All @@ -306,9 +304,9 @@ it, and that assigned ordinal may change between builds of the binary.
<!-- ignore: Only works on x86 Windows -->
```rust,ignore
#[link(name = "exporter", kind = "raw-dylib")]
extern "stdcall" {
unsafe extern "stdcall" {
#[link_ordinal(15)]
fn imported_function_stdcall(i: i32);
safe fn imported_function_stdcall(i: i32);
}
```

Expand Down
19 changes: 15 additions & 4 deletions src/items/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
> &nbsp;&nbsp; &nbsp;&nbsp; ( [_BlockExpression_] | `;` )
>
> _FunctionQualifiers_ :\
> &nbsp;&nbsp; `const`<sup>?</sup> `async`[^async-edition]<sup>?</sup> `unsafe`<sup>?</sup> (`extern` _Abi_<sup>?</sup>)<sup>?</sup>
> &nbsp;&nbsp; `const`<sup>?</sup> `async`[^async-edition]<sup>?</sup> _ItemSafety_<sup>?</sup> (`extern` _Abi_<sup>?</sup>)<sup>?</sup>
>
> _ItemSafety_ :\
> &nbsp;&nbsp; `safe`[^extern-safe] | `unsafe`
>
> _Abi_ :\
> &nbsp;&nbsp; [STRING_LITERAL] | [RAW_STRING_LITERAL]
Expand Down Expand Up @@ -39,6 +42,9 @@
>
> [^async-edition]: The `async` qualifier is not allowed in the 2015 edition.
>
> [^extern-safe]: The `safe` function qualifier is only allowed semantically within
> `extern` blocks.
>
> [^fn-param-2015]: Function parameters with only a type are only allowed
> in an associated function of a [trait item] in the 2015 edition.

Expand All @@ -58,6 +64,8 @@ fn answer_to_life_the_universe_and_everything() -> i32 {
}
```

The `safe` function is semantically only allowed when used in an [`extern` block].

## Function parameters

Function parameters are irrefutable [patterns], so any pattern that is valid in
Expand Down Expand Up @@ -157,10 +165,12 @@ their _definition_:

<!-- ignore: fake ABI -->
```rust,ignore
extern "ABI" {
fn foo(); /* no body */
unsafe extern "ABI" {
unsafe fn foo(); /* no body */
safe fn bar(); /* no body */
}
unsafe { foo() }
unsafe { foo() };
bar();
```

When `"extern" Abi?*` is omitted from `FunctionQualifiers` in function items,
Expand Down Expand Up @@ -415,3 +425,4 @@ fn foo_oof(#[some_inert_attribute] arg: u8) {
[implementation]: implementations.md
[value namespace]: ../names/namespaces.md
[variadic function]: external-blocks.md#variadic-functions
[`extern` block]: external-blocks.md
8 changes: 7 additions & 1 deletion src/items/static-items.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

> **<sup>Syntax</sup>**\
> _StaticItem_ :\
> &nbsp;&nbsp; `static` `mut`<sup>?</sup> [IDENTIFIER] `:` [_Type_]
> &nbsp;&nbsp; [_ItemSafety_]<sup>?</sup>[^extern-safety] `static` `mut`<sup>?</sup> [IDENTIFIER] `:` [_Type_]
> ( `=` [_Expression_] )<sup>?</sup> `;`
>
> [^extern-safety]: The `safe` and `unsafe` function qualifiers are only
> allowed semantically within `extern` blocks.

A *static item* is similar to a [constant], except that it represents a precise
memory location in the program. All references to the static refer to the same
Expand All @@ -28,6 +31,8 @@ statics:
The initializer expression must be omitted in an [external block], and must be
provided for free static items.

The `safe` and `unsafe` qualifiers are semantically only allowed when used in an [external block].

## Statics & generics

A static item defined in a generic scope (for example in a blanket or default
Expand Down Expand Up @@ -139,3 +144,4 @@ following are true:
[_Type_]: ../types.md#type-expressions
[_Expression_]: ../expressions.md
[value namespace]: ../names/namespaces.md
[_ItemSafety_]: functions.md
2 changes: 2 additions & 0 deletions src/keywords.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ is possible to declare a variable or method with the name `union`.
>
> **<sup>Lexer 2015</sup>**\
> KW_DYN : `dyn`
* `safe` is used for functions and statics, which has meaning in [external blocks].

[items]: items.md
[Variables]: variables.md
Expand All @@ -132,3 +133,4 @@ is possible to declare a variable or method with the name `union`.
[`dyn`]: types/trait-object.md
[loop label]: expressions/loop-expr.md#loop-labels
[generic lifetime parameter]: items/generics.md
[external blocks]: items/external-blocks.md
4 changes: 2 additions & 2 deletions src/types/never.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fn foo() -> ! {
```

```rust
extern "C" {
pub fn no_return_extern_func() -> !;
unsafe extern "C" {
pub safe fn no_return_extern_func() -> !;
}
```
10 changes: 8 additions & 2 deletions src/unsafe-keyword.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# The `unsafe` keyword

The `unsafe` keyword can occur in several different contexts:
unsafe functions (`unsafe fn`), unsafe blocks (`unsafe {}`), unsafe traits (`unsafe trait`), and unsafe trait implementations (`unsafe impl`).
unsafe functions (`unsafe fn`), unsafe blocks (`unsafe {}`), unsafe traits (`unsafe trait`), unsafe trait implementations (`unsafe impl`), and unsafe external blocks (`unsafe extern`).
It plays several different roles, depending on where it is used and whether the `unsafe_op_in_unsafe_fn` lint is enabled:
- it is used to mark code that *defines* extra safety conditions (`unsafe fn`, `unsafe trait`)
- it is used to mark code that needs to *satisfy* extra safety conditions (`unsafe {}`, `unsafe impl`, `unsafe fn` without [`unsafe_op_in_unsafe_fn`])
- it is used to mark code that needs to *satisfy* extra safety conditions (`unsafe {}`, `unsafe impl`, `unsafe fn` without [`unsafe_op_in_unsafe_fn`], `unsafe extern`)

The following discusses each of these cases.
See the [keyword documentation][keyword] for some illustrative examples.
Expand Down Expand Up @@ -56,3 +56,9 @@ Unsafe trait implementations are the logical dual to unsafe traits: where unsafe
[keyword]: ../std/keyword.unsafe.html
[`get_unchecked`]: ../std/primitive.slice.html#method.get_unchecked
[`unsafe_op_in_unsafe_fn`]: ../rustc/lints/listing/allowed-by-default.html#unsafe-op-in-unsafe-fn

## Unsafe external blocks (`unsafe extern`)

The programmer who declares an [external block] must assure that the signatures of the items contained within are correct. Failing to do so may lead to undefined behavior. That this obligation has been met is indicated by writing `unsafe extern`.

[external block]: items/external-blocks.md
2 changes: 2 additions & 0 deletions src/unsafety.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ Rust:
- Accessing a field of a [`union`], other than to assign to it.
- Calling an unsafe function (including an intrinsic or foreign function).
- Implementing an [unsafe trait].
- Declaring an [`extern`] block.

[`extern`]: items/external-blocks.md
[`union`]: items/unions.md
[mutable]: items/static-items.md#mutable-statics
[external]: items/external-blocks.md
Expand Down