diff --git a/src/items/external-blocks.md b/src/items/external-blocks.md index 9abce3fd4..bb59af898 100644 --- a/src/items/external-blocks.md +++ b/src/items/external-blocks.md @@ -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. @@ -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 @@ -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: ...); } ``` @@ -152,17 +150,17 @@ not specified. ```rust,ignore #[link(name = "crypto")] -extern { +unsafe extern { // … } #[link(name = "CoreFoundation", kind = "framework")] -extern { +unsafe extern { // … } #[link(wasm_import_module = "foo")] -extern { +unsafe extern { // … } ``` @@ -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(); } ``` @@ -306,9 +304,9 @@ it, and that assigned ordinal may change between builds of the binary. ```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); } ``` diff --git a/src/items/functions.md b/src/items/functions.md index ae698fba2..54988df31 100644 --- a/src/items/functions.md +++ b/src/items/functions.md @@ -8,7 +8,10 @@ >       ( [_BlockExpression_] | `;` ) > > _FunctionQualifiers_ :\ ->    `const`? `async`[^async-edition]? `unsafe`? (`extern` _Abi_?)? +>    `const`? `async`[^async-edition]? _ItemSafety_? (`extern` _Abi_?)? +> +> _ItemSafety_ :\ +>    `safe`[^extern-safe] | `unsafe` > > _Abi_ :\ >    [STRING_LITERAL] | [RAW_STRING_LITERAL] @@ -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. @@ -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 @@ -157,10 +165,12 @@ their _definition_: ```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, @@ -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 diff --git a/src/items/static-items.md b/src/items/static-items.md index 343d30cbe..f00f1aab8 100644 --- a/src/items/static-items.md +++ b/src/items/static-items.md @@ -2,8 +2,11 @@ > **Syntax**\ > _StaticItem_ :\ ->    `static` `mut`? [IDENTIFIER] `:` [_Type_] +>    [_ItemSafety_]?[^extern-safety] `static` `mut`? [IDENTIFIER] `:` [_Type_] > ( `=` [_Expression_] )? `;` +> +> [^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 @@ -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 @@ -139,3 +144,4 @@ following are true: [_Type_]: ../types.md#type-expressions [_Expression_]: ../expressions.md [value namespace]: ../names/namespaces.md +[_ItemSafety_]: functions.md diff --git a/src/keywords.md b/src/keywords.md index 46da9f1e2..c6472a37b 100644 --- a/src/keywords.md +++ b/src/keywords.md @@ -118,6 +118,7 @@ is possible to declare a variable or method with the name `union`. > > **Lexer 2015**\ > KW_DYN : `dyn` +* `safe` is used for functions and statics, which has meaning in [external blocks]. [items]: items.md [Variables]: variables.md @@ -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 diff --git a/src/types/never.md b/src/types/never.md index 3fbd2ad5c..7f58a3ace 100644 --- a/src/types/never.md +++ b/src/types/never.md @@ -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() -> !; } ``` diff --git a/src/unsafe-keyword.md b/src/unsafe-keyword.md index a29fc9432..df68a74cc 100644 --- a/src/unsafe-keyword.md +++ b/src/unsafe-keyword.md @@ -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. @@ -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 diff --git a/src/unsafety.md b/src/unsafety.md index 915fa5b03..237a4bfec 100644 --- a/src/unsafety.md +++ b/src/unsafety.md @@ -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