japaric/xargo 1025
The sysroot manager that lets you build and customize `std`
Gilnaa/memoffset 183
offsetof for Rust
Gankra/sptr 67
sptr: The Strict Provenance Polyfill
Automation for de Bruijn syntax and substitution in Coq [maintainers=@RalfJung,@co-dan]
Safe pointer-to-member functionality for rust
opatut/dudel 29
This used to be a webapp for scheduling meetings easily. Now it's no longer maintained. Have a look at Bitpoll instead:
A test framework for testing rustc diagnostics output
ansible playbooks for my servers
iris chan
issue commentrust-lang/opsem-team
Meeting Proposal: Valid uses of addr_of! (especially with respect to alignment)
I think this should be slightly broader -- basically this is about when exactly which properties of a place are required to avoid UB. Currently we have "whenever a place is constructed, it must be aligned and dereferenceable". That's also what Miri implements. In MiniRust we have "arbitrary places can be constructed; when doing a place projection, it must be inbounds (same rules as ptr::offset
); when loading from a place, it must be aligned and dereferenceable". Other options are conceivable.
I'm happy to help draft the discussion document.
comment created time in 6 hours
issue commentrust-lang/libc
Wrong structs dirent, dirent64
Yeah that comment sounds accurate for the current very conservative semantics we have for *ptr
(where the pointer has to be fully aligned and dereferenceable). That comment is also a good argument for why we'd probably want a more relaxed semantics...
comment created time in 12 hours
Pull request review commentrust-lang/rust
Don't check for misaligned raw pointer derefs inside Rvalue::AddressOf
struct PointerFinder<'tcx, 'a> { } impl<'tcx, 'a> Visitor<'tcx> for PointerFinder<'tcx, 'a> {+ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {+ if let Rvalue::AddressOf(..) = rvalue {+ // Ignore dereferences inside of an AddressOf
@thomcc would you be willing to create a meeting proposal at https://github.com/rust-lang/opsem-team/ ?
comment created time in 13 hours
pull request commentrust-lang/miri
TB: improve error messages (distinguish between accesses and reborrows)
@bors r+
comment created time in 13 hours
pull request commentrust-lang/miri
Dereference pointers in shims as correct types
@bors delegate-
comment created time in 13 hours
Pull request review commentrust-lang/miri
TB: improve error messages (distinguish between accesses and reborrows)
impl TbError<'_> { /// Produce a UB error. pub fn build<'tcx>(self) -> InterpError<'tcx> { use TransitionError::*;- let kind = self.access_kind;+ let cause = self.access_cause; let accessed = self.accessed_info; let conflicting = self.conflicting_info; let accessed_is_conflicting = accessed.tag == conflicting.tag;+ let title = format!("{cause} through {accessed} is forbidden"); let (title, details, conflicting_tag_name) = match self.error_kind { ChildAccessForbidden(perm) => { let conflicting_tag_name = if accessed_is_conflicting { "accessed" } else { "conflicting" };- let title = format!("{kind} through {accessed} is forbidden"); let mut details = Vec::new(); if !accessed_is_conflicting { details.push(format!( "the accessed tag {accessed} is a child of the conflicting tag {conflicting}" )); }+ let access = cause.print_as_access(false); details.push(format!(- "the {conflicting_tag_name} tag {conflicting} has state {perm} which forbids child {kind}es"+ "the {conflicting_tag_name} tag {conflicting} has state {perm} which forbids this {access}" )); (title, details, conflicting_tag_name) } ProtectedTransition(transition) => { let conflicting_tag_name = "protected";- let title = format!("{kind} through {accessed} is forbidden");+ let access = cause.print_as_access(true);
let access = cause.print_as_access(/* is_foreign*/ true);
comment created time in 13 hours
Pull request review commentrust-lang/miri
TB: improve error messages (distinguish between accesses and reborrows)
impl TbError<'_> { /// Produce a UB error. pub fn build<'tcx>(self) -> InterpError<'tcx> { use TransitionError::*;- let kind = self.access_kind;+ let cause = self.access_cause; let accessed = self.accessed_info; let conflicting = self.conflicting_info; let accessed_is_conflicting = accessed.tag == conflicting.tag;+ let title = format!("{cause} through {accessed} is forbidden"); let (title, details, conflicting_tag_name) = match self.error_kind { ChildAccessForbidden(perm) => { let conflicting_tag_name = if accessed_is_conflicting { "accessed" } else { "conflicting" };- let title = format!("{kind} through {accessed} is forbidden"); let mut details = Vec::new(); if !accessed_is_conflicting { details.push(format!( "the accessed tag {accessed} is a child of the conflicting tag {conflicting}" )); }+ let access = cause.print_as_access(false);
let access = cause.print_as_access(/* is_foreign*/ false);
comment created time in 13 hours
Pull request review commentrust-lang/rust
Document memory orderings of `thread::{park, unpark}`
pub fn sleep(dur: Duration) { /// /// * It can be implemented very efficiently on many platforms. ///+/// # Memory Orderings+///+/// Calls to `park` _synchronize-with_ calls to `unpark`, meaning that memory+/// operations performed before a call to `unpark` are made visible to the thread that+/// consumes the token and returns from `park`. Note that all `park` and `unpark`+/// operations for a given thread form a total order and `park` synchronizes-with+/// _all_ prior `unpark` operations.+///+/// In atomic ordering terms, `unpark` performs a `Release` operation and `park`+/// performs the corresponding `Acquire` operation. Calls to `unpark` for the same+/// thread form a [release sequence].+///+/// Note that being unblocked does not imply a call was made to `unpark`, because+/// wakeups can also be spurious. For example, a valid, but inefficient,+/// implementation could have `park` and `unpark` return immediately without doing anything.
/// Note that being unblocked does not imply a call was made to `unpark`, because
/// wakeups can also be spurious. For example, a valid, but inefficient,
/// implementation could have `park` and `unpark` return immediately without doing anything
/// (making *all* wakeups spurious).
comment created time in 13 hours
Pull request review commentrust-lang/rust
Document memory orderings of `thread::{park, unpark}`
pub fn sleep(dur: Duration) { /// /// * It can be implemented very efficiently on many platforms. ///+/// # Memory Orderings+///+/// Calls to `park` _synchronize-with_ calls to `unpark`, meaning that memory+/// operations performed before a call to `unpark` are made visible to the thread that+/// consumes the token and returns from `park`. Note that all `park` and `unpark`+/// operations for a given thread form a total order and `park` synchronizes-with+/// _all_ prior `unpark` operations.+///+/// In atomic ordering terms, `unpark` performs a `Release` operation and `park`+/// performs the corresponding `Acquire` operation. Calls to `unpark` for the same+/// thread form a [release sequence].+///+/// Note that being unblocked does not imply a call was made to `unpark`, because+/// wakeups can also be spurious. For example, a valid, but inefficient,+/// implementation could have `park` and `unpark` return immediately without doing anything.
I was thinking we want to provide some kind of liveness guarantee ("forward progress" in C++ terms). But I guess we don't make a good effort of documenting our liveness guarantees for any part of the concurrency stack so no reason to start here and now.
Also before we add anything like it I should come up with an example where that liveness guarantee is truly needed. Currently we have "either the wakeup was spurious and so there'll be another wakeup (as the token was not consumed), or it was proper and there was synchronization", which (together with fairness of memory event propagation) should be sufficient.
comment created time in 13 hours
issue commentcoq/coq
Strange typeclass resolution failure: Cannot unify <term> and <identical term>
@SkySkimmer how did you get that nice debug output (with the parenthetical explaining the unification failure)? Do I need to set some flags, or is this a Coq master thing that doesn't exist yet in 8.17?
comment created time in 14 hours
issue commentcoq/coq
Strange typeclass resolution failure: Cannot unify <term> and <identical term>
Okay, so that seems to confirm what Janno theorized above -- somewhere a heuristic is (unnecessarily) unfolding mapsto
(arguably violating the fact that it is to be treated opaquely) and then later, because it is opaque, this unfolding is getting in the way.
comment created time in 14 hours
Pull request review commentrust-lang/miri
TB: improve error messages (distinguish between accesses and reborrows)
LL | unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) } = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental = help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child) = help: the access would cause the protected tag <TAG> to transition from Frozen to Disabled- = help: this is a loss of read permissions, which is not allowed for protected tags+ = help: this transition would be a loss of read permissions, which is not allowed for protected tags+ = help: the write access that caused this error was inserted implicitly due to a deallocation
Correspondingly, here the message should say "deallocation through <TAG> forbidden"
comment created time in 14 hours
Pull request review commentrust-lang/miri
TB: improve error messages (distinguish between accesses and reborrows)
help: the accessed tag <TAG> later transitioned to Frozen due to a foreign read | LL | pub fn safe(x: &mut i32, y: &mut i32) { | ^- = help: this corresponds to a loss of write permissions+ = help: this transition corresponds to a loss of write permissions+ = help: the read access that caused this transition was inserted implicitly due to a reborrow
I think what I'd like to see here is a change already further up in this message:
help: the accessed tag <TAG> later transitioned to Frozen due to a foreign reborrow at offsets [0x0..0x4]
--> $DIR/aliasing_mut1.rs:LL:CC
|
LL | pub fn safe(x: &mut i32, y: &mut i32) {
| ^
= help: this transition corresponds to a loss of write permissions
Maybe with another line saying
= help: reborrows act like read access for the purpose of the aliasing rules
though I am not sure if that is needed (we don't have it for SB)
comment created time in 14 hours
Pull request review commentrust-lang/rust
Document memory orderings of `thread::{park, unpark}`
pub fn sleep(dur: Duration) { /// /// * It can be implemented very efficiently on many platforms. ///+/// # Memory Orderings+///+/// Calls to `park` _synchronize-with_ calls to `unpark`, meaning that memory+/// operations performed before a call to `unpark` are made visible to the thread that+/// consumes the token and returns from `park`. Note that all `park` and `unpark`+/// operations for a given thread form a total order and `park` synchronizes-with+/// _all_ prior `unpark` operations.+///+/// In atomic ordering terms, `unpark` performs a `Release` operation and `park`+/// performs the corresponding `Acquire` operation. Calls to `unpark` for the same+/// thread form a [release sequence].+///+/// Note that being unblocked does not imply a call was made to `unpark`, because+/// wakeups can also be spurious. For example, a valid, but inefficient,+/// implementation could have `park` and `unpark` return immediately without doing anything.
Don't we want to guarantee that eventually, there'll be a non-spurious wakeup (if unpark
was previously called in another thread)?
comment created time in 14 hours
Pull request review commentrust-lang/rfcs
RFC: `#[export]` (dynamically linked crates)
+- Feature Name: `export`+- Start Date: 2023-04-19+- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000)+- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)++# Dynamically Linked Crates++This is a proposal for a new `#[export]` attribute to greatly simplify+the creation and use of dynamic libraries.++This proposal complements the ["crabi" ABI](https://github.com/rust-lang/rust/pull/105586) proposal.++## Problem statement++Imagine a simple library crate with just one simple function, and an application that uses it:++```rust+//! library crate++pub fn hello() {+ println!("Hello!");+}+```++```rust+//! application crate++fn main() {+ library::hello();+}+```++By default, Cargo will automatically build both crates and **statically** link them into a single binary.++However, there are many reasons why one might want to **dynamically** link the library instead.+The use cases for dynamic linking can be roughly split into two:++1. Cases where both the dynamic library and application are compiled with the exact same compiler (on the same platform, with the same settings) and shipped together.+2. Cases where dynamic library and application can be compiled and shipped separately from each other.++The first situation is currently relatively well supported by Rust.+The Rust compiler itself falls into this category, where we ship a single `librustc_driver.so` (or .dll or equivalent)+file that is used by `rustc`, `rustfmt`, `rustdoc`, and `clippy`.+The motivation is simply to reduce the binary size of the overall package containing all these tools.++The second situation has far more use cases and currently not supported well by Rust.+A common use case is a library that is shipped as part of the system (e.g. `libz.so` or `kernel32.dll`),+in which case you want to use the version provided by the system the program is run on,+and not from the system it was compiled on.+In these cases, dynamically linking is important to make sure the library can be independently updated.+(And it also helps to not blow up binary sizes.)++We need a good solution for this second category of use cases.++### Solution today++Currently, a way to implement this would make use of a combination of `extern "C"`, `#[no_mangle]` and `unsafe`,+each of which has major downsides.++It'd look something like this:++```rust+//! library crate++pub fn hello() {+ println!("Hello!");+}++#[no_mangle]+pub extern "C" fn some_unique_name_for_hello() {+ hello();+}+```++```rust+//! library bindings crate++#[link(name = "library")]+extern "C" {+ fn some_unique_name_for_hello();+}++#[inline]+pub fn hello() {+ unsafe { some_unique_name_for_hello() };+}+```++```rust+//! application crate++fn main() {+ library_bindings::hello();+}+```++This is bad. It's very verbose and error prone. More specifically:++- `#[no_mangle]` is needed to export a symbol under a stable name, but it requires manually picking a good unique name that won't collide with other items from other crates.+- A stable ABI is necessary to allow linking code from a different compiler (version),+ but `extern "C"` puts severe limitations on the function signatures,+ as most Rust types can't directly pass through the C ABI.+- `unsafe` code is required, because the compiler cannot validate the imported symbol matches the expected function signature.+ Importing the wrong library (with the same symbol name) could result in unsoundness.+- There are now two library crates: one that will be compiled into the dynamic library (the .dll/.so/.dylib file),+ and one that provides the bindings to that dynamic library.+ The second library likely fully inlined into the final application,+ as it only has wrappers, just to bring back the original (safe) function signatures.++Much of this solution could be automated by a procedural macro,+but splitting a library crate in two falls outside of what a procedural macro can reasonably do.++### Proposed solution sketch++Instead of all the manual usage of `#[no_mangle]`, `extern`, and `unsafe`,+a much better solution would look as closely as possible to the original code.++With the proposal below, one only needs to add an `#[export]` attribute, and give the function a stable ABI+(e.g. `extern "C"` or (in the future) `extern "crabi"`):++```rust+//! library crate++#[export]+pub extern "C" fn hello() {+ println!("Hello!");+}+```++```rust+//! application crate++fn main() {+ library::hello();+}+```++The library can then be either linked statically or dynamically, by informing cargo of the choice:++```diff+ [dependencies]+- library = { path = "..." }++ library = { path = "...", dynamic = true }+```++## Proposal++Creating and using dynamic libraries involves three things:++1. A stable ABI that can be used for the items that are exported/imported.+2. A way to export and import items.+3. A way to create and use dynamic libraries.++For (1) we currently only have `extern "C"`, which only suffices for very simple cases.+This proposal does not include any improvements for (1),+but the ["crabi" proposal](https://github.com/rust-lang/rust/pull/105586) proposes the creation+of a new `extern "…"` ABI that is more flexible, which perfectly complements this proposal.++This proposal provides solutions for (2) and (3).+Exporting (and importing) items is done through a new language feature: the `#[export]` attribute.+Creating and using dynamic libraries is made easy through a new Cargo feature: `dynamic` dependencies.++### The `#[export]` Attribute++The `#[export]` attribute is used to mark items which are "stable" (in ABI/layout/signature)+such that they can be used across the border between (separately compiled) dynamically linked libraries/binaries.++The `#[export]` attribute can be applied to any public item that is *exportable*.+Which items are *exportable* is something that can increase over time with future proposals.+Initially, only the following items are *exportable*:++- Non-generic functions with a stable ABI (e.g. `extern "C"`)+ for which every user defined type used in the signature is also marked as `#[export]`.+ - This includes type associated functions ("methods").+- Structs/enums/unions with a stable representation (e.g. `repr(i32)` or `repr(C)`).+- Re-exports of those items (`use` statements, `type` aliases).++An `#[export]` attribute can also be applied to a crate, module, and non-generic type `impl` block,+which is simply equivalent to applying the attribute to every public item within it.++For types, the `#[export]` attribute represents the commitment to keep the representation of the type stable.+(To differentiate from, for example, a `#[repr(i32)]` that only exists as an optimization rather than as a stable promise.)++For functions, the `#[export]` attribute will make the function available from the dynamic library+under a stable "mangled" symbol that uniquely represents its crate and module path *and full signature*.+(More on that below.)++For aliases of functions, an `#[export]` attribute on the `use` statement will use the+path (and name) of the alias, not of the original function.+(So it won't 'leak' the name of any (possibly private/unstable) module it was re-exported from.)++### Privacy++It is an error to export an item that is not public, or is part of a non-public module.+The set of exported items of a crate will always be a subset of the crate's public interface.++It's fine to `#[export]` a public alias of a public type from a private module:++```rust+mod a {+ pub extern "C" fn f() { … }+}++#[export]+pub mod b {+ pub use super::a::f;+}+```++(This will export the function f as `b::f`.)++### Importing Exported Items++Normally, when using a crate as a dependency, any `#[export]` attributes of that crate have no effect+and the dependency is statically linked into the resulting binary.++When explicitly specifying `dynamic = true` for the dependency with `Cargo.toml`,+or when using a `extern dyn crate …;` statement in the source code,+only the items marked as `#[export]` will be available and the dependency will be linked dynamically.++### Building Dynamic Dependencies++When using `dynamic = true` for a dependency, there is no need to build that full crate:+only the signatures of its exported items are necessary.+Cargo will pass a flag to the Rust compiler which will stop it from generating+code for non-exported items and function bodies.++A clear separation between "public dependencies" (which used in the interface)+and "private dependencies" (which are only used in the implementation) is required+to avoid building unnecessary indirect dependencies.+A system for that has been proposed in [RFC 1977](https://rust-lang.github.io/rfcs/1977-public-private-dependencies.html).++### Name Mangling and Safety++Because a dynamic dependency and the crate that uses it are compiled separately+and only combined at runtime,+it is impossible for the compiler to perform any (safety, borrow, signature, …) checks.+However, making a (perhaps accidental) change to a function signature or type+should not lead to undefined behavior at runtime.++There are two ways to solve this problem:++1. Make it the responsibility of the user.+2. Make it the responsibility of the loader/linker.++Option (1) simply means making everything `unsafe`, which isn't very helpful to the user.+Option (2) means the loader (the part that loads the dynamic library at runtime) needs to perform the checks.++Unless we ship our own loader as part of Rust binaries,+we can only make use of the one functionality available in the loaders of all operating systems:+looking up symbols by their name.++So, in order to be able to provide safety, the symbol name has to be unique for the full signature,+including all relevant type descriptions.++To avoid extremely long symbol names that contain a full (encoded) version of the function signature+and all relevant type descriptions, we use a 128-bit hash based on all this information.++For example, an exported item in `foo::bar` in the crate `mycrate` would be exported with a symbol name such as:++```+_RNvNtC_7mycrate3foo3bar_f8771d213159376fafbff1d3b93bb212+```++Where the first part is the (mangled) path and name of the item,+and the second part is the hexadecimal representation of a 128-bit hash of all relevant signature and type information.+The hash algorithm is still to be determined.++(See also the "alternatives" section below.)++### Type Information++As mentioned above, the hash in a symbol name needs to cover _all_ relevant type information.+However, exactly which information is and isn't relevant for safety is a complicated question.++#### Types with Public Fields++For a simple user defined type where all fields are both public, like the `Point` struct below,+the relevant parts are the size, alignment, and recursively all field information.++```rust+#[export]+#[repr(C)]+pub struct Point {+ pub x: f32,+ pub y: f32,+ pub name: &str,+}+```++The `#[export]` attribute is the user's commitment to keep the type stable, but without `unsafe`,+any mistakes should _not_ result in unsoundness.+Accidentally changing the struct to swap the `x` and `name` fields should result in a different hash,+such that the `f32` won't get interpreted as a `&str`, for example.++Note that, technically, the names of the type and the fields are not relevant, at least _not for memory safety_.+Swapping the `x` and `y` fields result in surprises and bugs and shouldn't be done,+but it won't result in undefined behaviour, since any Rust code can swap the fields without using `unsafe`.++However, for public fields, the field names are already part of the stable API, so we include them in the hash as well.++It is an error to use a plain `#[export]` attribute on a type with out stable `#[repr(…)]`,+if it has any private fields,+or if any of the fields are not of an `#[export]`ed or builtin type.++#### Types with Private Fields++For types where not all fields are public, the situation is much more complicated.++Private fields usually come with certain *invariants*, and come with `unsafe` code that makes assumptions about them.+For example, the private fields of a `Vec` are assumed to represent a valid "owned" pointer to an allocation together with its capacity and initialized size.++If it would be possible to define a identically named type with the same fields but different (or no) invariants/assumptions,+or just change the invariants in an existing library,+it'd be possible to cause undefined behavior by loading the "wrong" dynamic library.++Therefore, we can't allow a regular `#[export]` attribute on a type with private fields,+since we have no way of automatically determining the invariants / unsafe assumptions about private fields.++Instead, for these types, we must require the user to *unsafely* commit to+ABI stability if they want to make the type available to exported functions.++Using `#[export(unsafe_stable_abi = «hash»)]`, one can make the (unsafe) promise+that the type will remain ABI compatible as long as the provided hash remains the same.+The hash must have been randomly generated to ensure uniqueness (which is part of the unsafe promise).++```rust+#[export(unsafe_stable_abi = "ca83050b302bf0644a1417ac3fa6982a")]+#[repr(C)]+pub struct ListNode {+ next: *const ListNode,+ value: i32,+}+```++In this case, using the type as part of a function signature will not result in a hash based on the full (recursive) type definition,+but will instead be based on the user provided hash (and the size and alignment of the type).
This seems potentially problematic if a type with custom invariants has as a field another type with such invariants. Like, imagine a type using Vec
but then also adding further invariants. Now whenever the Vec
invariant changes it is crucial that this type's invariant also changes! But by not doing any recursive traversal, this will be very hard for authors to ensure. (I assume changing the invariant is considered semver-compatible, at least currently it would be. Then crates using Vec
don't have any chance of knowing which of possible several semver-compatible Vec
s they will get.)
comment created time in 15 hours
Pull request review commentrust-lang/rfcs
RFC: `#[export]` (dynamically linked crates)
+- Feature Name: `export`+- Start Date: 2023-04-19+- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000)+- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)++# Dynamically Linked Crates++This is a proposal for a new `#[export]` attribute to greatly simplify+the creation and use of dynamic libraries.++This proposal complements the ["crabi" ABI](https://github.com/rust-lang/rust/pull/105586) proposal.++## Problem statement++Imagine a simple library crate with just one simple function, and an application that uses it:++```rust+//! library crate++pub fn hello() {+ println!("Hello!");+}+```++```rust+//! application crate++fn main() {+ library::hello();+}+```++By default, Cargo will automatically build both crates and **statically** link them into a single binary.++However, there are many reasons why one might want to **dynamically** link the library instead.+The use cases for dynamic linking can be roughly split into two:++1. Cases where both the dynamic library and application are compiled with the exact same compiler (on the same platform, with the same settings) and shipped together.+2. Cases where dynamic library and application can be compiled and shipped separately from each other.++The first situation is currently relatively well supported by Rust.+The Rust compiler itself falls into this category, where we ship a single `librustc_driver.so` (or .dll or equivalent)+file that is used by `rustc`, `rustfmt`, `rustdoc`, and `clippy`.+The motivation is simply to reduce the binary size of the overall package containing all these tools.++The second situation has far more use cases and currently not supported well by Rust.+A common use case is a library that is shipped as part of the system (e.g. `libz.so` or `kernel32.dll`),+in which case you want to use the version provided by the system the program is run on,+and not from the system it was compiled on.+In these cases, dynamically linking is important to make sure the library can be independently updated.+(And it also helps to not blow up binary sizes.)++We need a good solution for this second category of use cases.++### Solution today++Currently, a way to implement this would make use of a combination of `extern "C"`, `#[no_mangle]` and `unsafe`,+each of which has major downsides.++It'd look something like this:++```rust+//! library crate++pub fn hello() {+ println!("Hello!");+}++#[no_mangle]+pub extern "C" fn some_unique_name_for_hello() {+ hello();+}+```++```rust+//! library bindings crate++#[link(name = "library")]+extern "C" {+ fn some_unique_name_for_hello();+}++#[inline]+pub fn hello() {+ unsafe { some_unique_name_for_hello() };+}+```++```rust+//! application crate++fn main() {+ library_bindings::hello();+}+```++This is bad. It's very verbose and error prone. More specifically:++- `#[no_mangle]` is needed to export a symbol under a stable name, but it requires manually picking a good unique name that won't collide with other items from other crates.+- A stable ABI is necessary to allow linking code from a different compiler (version),+ but `extern "C"` puts severe limitations on the function signatures,+ as most Rust types can't directly pass through the C ABI.+- `unsafe` code is required, because the compiler cannot validate the imported symbol matches the expected function signature.+ Importing the wrong library (with the same symbol name) could result in unsoundness.+- There are now two library crates: one that will be compiled into the dynamic library (the .dll/.so/.dylib file),+ and one that provides the bindings to that dynamic library.+ The second library likely fully inlined into the final application,+ as it only has wrappers, just to bring back the original (safe) function signatures.++Much of this solution could be automated by a procedural macro,+but splitting a library crate in two falls outside of what a procedural macro can reasonably do.++### Proposed solution sketch++Instead of all the manual usage of `#[no_mangle]`, `extern`, and `unsafe`,+a much better solution would look as closely as possible to the original code.++With the proposal below, one only needs to add an `#[export]` attribute, and give the function a stable ABI+(e.g. `extern "C"` or (in the future) `extern "crabi"`):++```rust+//! library crate++#[export]+pub extern "C" fn hello() {+ println!("Hello!");+}+```++```rust+//! application crate++fn main() {+ library::hello();+}+```++The library can then be either linked statically or dynamically, by informing cargo of the choice:++```diff+ [dependencies]+- library = { path = "..." }++ library = { path = "...", dynamic = true }+```++## Proposal++Creating and using dynamic libraries involves three things:++1. A stable ABI that can be used for the items that are exported/imported.+2. A way to export and import items.+3. A way to create and use dynamic libraries.++For (1) we currently only have `extern "C"`, which only suffices for very simple cases.+This proposal does not include any improvements for (1),+but the ["crabi" proposal](https://github.com/rust-lang/rust/pull/105586) proposes the creation+of a new `extern "…"` ABI that is more flexible, which perfectly complements this proposal.++This proposal provides solutions for (2) and (3).+Exporting (and importing) items is done through a new language feature: the `#[export]` attribute.+Creating and using dynamic libraries is made easy through a new Cargo feature: `dynamic` dependencies.++### The `#[export]` Attribute++The `#[export]` attribute is used to mark items which are "stable" (in ABI/layout/signature)+such that they can be used across the border between (separately compiled) dynamically linked libraries/binaries.++The `#[export]` attribute can be applied to any public item that is *exportable*.+Which items are *exportable* is something that can increase over time with future proposals.+Initially, only the following items are *exportable*:++- Non-generic functions with a stable ABI (e.g. `extern "C"`)+ for which every user defined type used in the signature is also marked as `#[export]`.+ - This includes type associated functions ("methods").+- Structs/enums/unions with a stable representation (e.g. `repr(i32)` or `repr(C)`).+- Re-exports of those items (`use` statements, `type` aliases).++An `#[export]` attribute can also be applied to a crate, module, and non-generic type `impl` block,+which is simply equivalent to applying the attribute to every public item within it.++For types, the `#[export]` attribute represents the commitment to keep the representation of the type stable.+(To differentiate from, for example, a `#[repr(i32)]` that only exists as an optimization rather than as a stable promise.)++For functions, the `#[export]` attribute will make the function available from the dynamic library+under a stable "mangled" symbol that uniquely represents its crate and module path *and full signature*.+(More on that below.)++For aliases of functions, an `#[export]` attribute on the `use` statement will use the+path (and name) of the alias, not of the original function.+(So it won't 'leak' the name of any (possibly private/unstable) module it was re-exported from.)++### Privacy++It is an error to export an item that is not public, or is part of a non-public module.+The set of exported items of a crate will always be a subset of the crate's public interface.++It's fine to `#[export]` a public alias of a public type from a private module:++```rust+mod a {+ pub extern "C" fn f() { … }+}++#[export]+pub mod b {+ pub use super::a::f;+}+```++(This will export the function f as `b::f`.)++### Importing Exported Items++Normally, when using a crate as a dependency, any `#[export]` attributes of that crate have no effect+and the dependency is statically linked into the resulting binary.++When explicitly specifying `dynamic = true` for the dependency with `Cargo.toml`,+or when using a `extern dyn crate …;` statement in the source code,+only the items marked as `#[export]` will be available and the dependency will be linked dynamically.++### Building Dynamic Dependencies++When using `dynamic = true` for a dependency, there is no need to build that full crate:+only the signatures of its exported items are necessary.+Cargo will pass a flag to the Rust compiler which will stop it from generating+code for non-exported items and function bodies.++A clear separation between "public dependencies" (which used in the interface)+and "private dependencies" (which are only used in the implementation) is required+to avoid building unnecessary indirect dependencies.+A system for that has been proposed in [RFC 1977](https://rust-lang.github.io/rfcs/1977-public-private-dependencies.html).++### Name Mangling and Safety++Because a dynamic dependency and the crate that uses it are compiled separately+and only combined at runtime,+it is impossible for the compiler to perform any (safety, borrow, signature, …) checks.+However, making a (perhaps accidental) change to a function signature or type+should not lead to undefined behavior at runtime.++There are two ways to solve this problem:++1. Make it the responsibility of the user.+2. Make it the responsibility of the loader/linker.++Option (1) simply means making everything `unsafe`, which isn't very helpful to the user.+Option (2) means the loader (the part that loads the dynamic library at runtime) needs to perform the checks.++Unless we ship our own loader as part of Rust binaries,+we can only make use of the one functionality available in the loaders of all operating systems:+looking up symbols by their name.++So, in order to be able to provide safety, the symbol name has to be unique for the full signature,+including all relevant type descriptions.++To avoid extremely long symbol names that contain a full (encoded) version of the function signature+and all relevant type descriptions, we use a 128-bit hash based on all this information.++For example, an exported item in `foo::bar` in the crate `mycrate` would be exported with a symbol name such as:++```+_RNvNtC_7mycrate3foo3bar_f8771d213159376fafbff1d3b93bb212+```++Where the first part is the (mangled) path and name of the item,+and the second part is the hexadecimal representation of a 128 hash of all relevant signature and type information.+The hash algorithm is still to be determined.++(See also the "alternatives" section below.)++### Type Information++As mentioned above, the hash in a symbol name needs to cover _all_ relevant type information.+However, exactly which information is and isn't relevant for safety is a complicated question.++#### Types with Public Fields++For a simple user defined type where all fields are both public, like the `Point` struct below,+the relevant parts are the size, alignment, and recursively all field information.++```rust+#[export]+#[repr(C)]+pub struct Point {+ pub x: f32,+ pub y: f32,+ pub name: &str,+}+```++The `#[export]` attribute is the user's commitment to keep the type stable, but without `unsafe`,+any mistakes should _not_ result in unsoundness.+Accidentally changing the struct to swap the `x` and `name` fields should result in a different hash,+such that the `f32` won't get interpreted as a `&str`, for example.++Note that, technically, the names of the type and the fields are not relevant, at least _not for memory safety_.+Swapping the `x` and `y` fields result in surprises and bugs and shouldn't be done,+but it won't result in undefined behaviour, since any Rust code can swap the fields without using `unsafe`.++However, for public fields, the field names are already part of the stable API, so we include them in the hash as well.++It is an error to use a plain `#[export]` attribute on a type with out stable `#[repr(…)]`,+if it has any private fields,+or if any of the fields are not of an `#[export]`ed or builtin type.++#### Types with Private Fields
This effectively means that you can only interact with this type by reference and all operations on it must be done through exported functions.
I don't see how that helps about the private invariant issue -- if the invariants change, it is quite important that this is considered a separate type, even if everything is by-reference.
comment created time in 15 hours
Pull request review commentrust-lang/rust
Don't check for misaligned raw pointer derefs inside Rvalue::AddressOf
struct PointerFinder<'tcx, 'a> { } impl<'tcx, 'a> Visitor<'tcx> for PointerFinder<'tcx, 'a> {+ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {+ if let Rvalue::AddressOf(..) = rvalue {+ // Ignore dereferences inside of an AddressOf
Would be good to have a comment here that these derefs (if unaligned) are UB according to the reference, but we choose to skip them here because they there are stull debates wrt whether we want to relax this UB later or not.
Also (while I am here), IMO if let
without any binder is better replaced by if matches!
.
comment created time in 16 hours