--- title: "Design meeting 2025-10-01: Topic" tags: ["T-lang", "design-meeting", "minutes"] date: 2025-10-01 discussion: https://rust-lang.zulipchat.com/#narrow/channel/410673-t-lang.2Fmeetings/topic/Design.20meeting.202025-10-01.3A.20Wasmtime/ url: https://hackmd.io/7k4hIEtlR8upyTiZMrw8Lw --- > From: > > https://hackmd.io/qBBoEgNISROHUpLBlYLf8w ## Top - [Introduction](#Introduction1) - [Topics](#Topics) - [Tail call optimizations for Pulley](#Tail-call-optimizations-for-Pulley) - [Non-overwritable `&mut T`](#Non-overwritable-ampmut-T) - [Read-only fields / unsafe fields](#Read-only-fields--unsafe-fields) - [Working with `Pin` is hard](#Working-with-Pin-is-hard) - [Unleakable types](#Unleakable-types) - [Send bounds on async closures](#Send-bounds-on-async-closures) - [Dyn-compatible `async fn`](#Dyn-compatible-async-fn) - [Function pointers may have over-constrained lifetimes](#Function-pointers-may-have-over-constrained-lifetimes) - [Removing remaining uses of C in Wasmtime](#Removing-remaining-uses-of-C-in-Wasmtime) - [Pulley, Threads, and the Rust Memory Model](#Pulley-Threads-and-the-Rust-Memory-Model) - [Bitpacking](#Bitpacking) - [Representing ownership to avoid dynamic checks](#Representing-ownership-to-avoid-dynamic-checks) - [x86\_64-unknown-none softfloat abi](#x86_64-unknown-none-softfloat-abi) - [Partial Borrows](#Partial-Borrows) - [Async entry points for WASI 0.3 target](#Async-entry-points-for-WASI-03-target) - [Extern “wasm” / repr(wasm)](#Extern-“wasm”--reprwasm) - [Infectiousness of Send with async](#Infectiousness-of-Send-with-async) - [Generic parameters in constant expressions](#Generic-parameters-in-constant-expressions) - [Appendixes](#Appendixes) - [Appendix A: Platform Comparison](#Appendix-A-Platform-Comparison) - [Appendix B: Glossary](#Appendix-B-Glossary) - [Appendix C: WASI Versions](#Appendix-C-WASI-Versions) ## Introduction Wasmtime is a WebAssembly (Wasm) runtime written in Rust with a focus on correctness. Just like Rust, Wasmtime incubated at Mozilla Research. And there has historically also been a fair amount of overlap between project members. Wasmtime in many ways is a “best case scenario” for the Rust language: the core team is largely composed of Rust experts, working in a domain Rust excels at (compiler-like tooling + async system bindings), for a programming ecosystem (WebAssembly) that was developed alongside Rust. The purpose of this meeting is to create a moment for the Wasmtime team and Rust language team to talk to each other. We hope for this meeting to be a cross between an informal chat and a Q&A session, with the provided topics serving as a starting point for the conversation. This meeting is considered a success if T-Lang walks away with a better understanding of which features and changes would help Wasmtime. Below is a listing of topics that the Wasmtime team has brainstormed about possible discussion points with T-lang. Most of these are centered around things we've run into when implementing Wasmtime itself in Rust. This is, however, also a great opportunity if T-lang would like to pick our own brains about wasm support and how it relates to Rust. We're of course willing to chat about anything related to wasm and development of the standard and such! And finally, while it may go without saying, it's worth saying anyway. Wasmtime is quite content with the choice to use Rust. It's been absolutely instrumental to the success of Wasmtime so far and is predicted to continue to do so into the future. This is very much not a laundry list of demands we have for the lang team by any means. Rather these are what we view as interesting topics that we're happy to explore in more depth if desired or perhaps just see where the discussion takes us. A number of points below are very pie-in-the-sky outside the scope of even the lang team, which we realize, but might be interesting to talk about nonetheless! [↑ go back to top](#Top) ## Topics ### Tail call optimizations for Pulley Wasmtime's interpreter, Pulley, can be compiled with either a "match loop" in stable Rust or a tail-call-dispatch-mode with a "tail loop". It'd be nice to commit to just one of these modes, so more-or-less this is a use case for the `become` keyword in Rust and guaranteed tail calls in Rust. There's not much uniquely special about this compared to other interpreters written in Rust and it's generally accepted that tail-call-dispatch-interpreters are a great way to write fast interpreters nowadays. Note that in Wasmtime we still need to fully evaluate performance implications of tail-calls vs match-loop, but from Wasmtime's perspective the main goal of Pulley is portability, so a solution where tail calls were only supported on some platforms would work but wouldn't be ideal for us. [↑ go back to top](#Top) ### Non-overwritable `&mut T` Wasmtime has a problem where host functions get `&mut Store<T>` more-or-less, but the problem is that it's not sound to do something like `*the_store = Store::new(some_ctor())`. To work around this we have our own type `StoreContextMut<'_, T>` which internally holds `&mut Store<T>`. This has decent enough ergonomics for external users given enough trait impls, but it has pretty awful ergonomics internally within Wasmtime. It'd be much nicer to "just use `&mut Thing`", but Wasmtime would need a Rust-level guarantee to ensure that the pointer can't be overwritten. [↑ go back to top](#Top) ### Read-only fields / unsafe fields We're (ab)using `Pin` right now to make it unsafe to get a `&mut vm::Instance` right now. It's not really about self-referential types at all, we just want to make it unsafe to get a `&mut vm::Instance` because it's not safe to mutate some fields. This works well-enough but it's a roundabout solution for what we want which is to disallow mutation of a field after creation (or overwriting it?). Or maybe require unsafe access to a field which would disallow overwriting the whole structure? [↑ go back to top](#Top) ### Working with Pin is hard Given above we're using `Pin` internally but the ergonomics are not great: * Lots of `self.as_mut().thing(...)` * `mut self: Pin<&mut Self>` is a mouthful * pin-projection requires `unsafe` or integration of foreign crates, just a bit awkward [↑ go back to top](#Top) ### Unleakable types For Rust-compiled-to-wasm the component model has async support now which is, if you squint enough, io-uring like in that you give a region of memory to the host and it fills it in later. Rust's "any type is safe to leak" forces safe bindings to always pass everything by ownership which is a bit of a bummer. Not that adding non-leak types is easy of course, but if we're listing out everything we may as well list this out. [↑ go back to top](#Top) ### Send bounds on async closures Wasmtime would love to use async closures more often in the codebases, but it currently can’t because it can’t bound the return type (e.g. `where F: AsyncFn(), F(..): Send`). This is unlikely to be news to T-Lang, but we still wanted to list this since it is important for Wasmtime. [↑ go back to top](#Top) ### Dyn-compatible `async fn` Wasmtime has a number of usages of `#[async_trait]` specifically for `dyn`-compatibility. This is again not going to come as a surprise to T-Lang, and solving this is not easy. But we wanted to let T-Lang know that this is a feature Wasmtime is definitely looking forward to. [↑ go back to top](#Top) ### Function pointers may have over-constrained lifetimes It seems weird that `fn(T)` is not `'static` unless `T: 'static`. This shouldn’t require `T: 'static`, but it’s unclear whether this is intentional or not. Would be nice if it weren't the case, but it's also not the end of the world if it isn’t. This is one of the major motivational factors to a pervasive `T: 'static` bound throughout Wasmtime's codebase which otherwise wouldn't be required. [↑ go back to top](#Top) ### Removing remaining uses of C in Wasmtime * AFAIK it's impossible to call `setjmp` in Rust safely -- we've actually recently removed Wasmtime's use of `setjmp` but this historically was a reason to use C. * Need to be able to define a symbol as "weak" (the defining half of weak linkage) * Need to be able to optionally import a symbol (the declaring half of weak linkage) -- this is used much more rarely but still used in Wasmtime. * Need to define "dllexport" symbols on Windows (we have a comment saying `#[export_name]` isn't sufficient in the codebase) [↑ go back to top](#Top) ### Pulley, Threads, and the Rust Memory Model Rust's memory model for atomics is incompatible with WebAssembly's. For example a data race in Rust is UB, but the same situation in WebAssembly is not UB. WebAssembly's memory model is the same as JS's which is defined by Ecmascript's `SharedArrayBuffer` interactions. This is primarily a problem when implementing Pulley, Wasmtime's interpreter, since racing reads cannot be UB which means we can't use Rust-native reads at all. This is a problem even for Wasmtime's Cranelift backend, however, since we need to implement the WebAssembly `memory.copy` instruction (e.g. `memcpy`) in Rust even when Cranelift is used as a backend. It'd be nice to use safe Rust instead of [the `ecmascript_atomics` crate](https://crates.io/crates/ecmascript_atomics). The main drawback of `ecmascript_atomics` is that it's inline asm, so not portable, but the main use case of Pulley is being portable. Background: > - https://github.com/bytecodealliance/wasmtime/issues/11747 > - [https://github.com/bytecodealliance/wasmtime/pull/9818](https://github.com/bytecodealliance/wasmtime/pull/9818 "https://github.com/bytecodealliance/wasmtime/pull/9818") > - [https://rust-lang.zulipchat.com/#narrow/channel/213817-t-lang/topic/Writing.20a.20WebAssembly.20interpreter.20in.20Rust.20for.20wasm.20threads/near/488915886](https://rust-lang.zulipchat.com/#narrow/channel/213817-t-lang/topic/Writing.20a.20WebAssembly.20interpreter.20in.20Rust.20for.20wasm.20threads/near/488915886 "https://rust-lang.zulipchat.com/#narrow/channel/213817-t-lang/topic/Writing.20a.20WebAssembly.20interpreter.20in.20Rust.20for.20wasm.20threads/near/488915886") [↑ go back to top](#Top) ### Bitpacking We would like better language-level mechanisms for bitpacking type definitions. Not just fields in `struct`s but also `enum`s, their discriminants, and each variants' fields. We often have a nice Rust `enum` type with variants and payload fields as well as its bitpacked equivalent, which is how it is stored "at rest". Converting from the bitpacked version to the nice `enum` is infallible (ignoring invalid discriminants), but converting the nice `enum` to the bitpacked representation is fallible because the `enum` variants' payload fields' ranges need to be checked to ensure that, e.g., a `u32` field fits in the 29 bits that are available for the field inside the bitpacked representation. Having integers of arbitrary bit width (e.g. `u29`) could help here, and would allow rustc to pack payloads and discriminants together into smaller values. However, what would be really fantastic would be to have control over the bitpacking and representation that rustc uses for a type ourselves, explicitly specifying which bits to use for an `enum`'s' discriminant, what the discriminant value is, and which bits to use for each variant's fields. And the compiler would ideally check that the specified bits are non-overlapping within a given `enum` variant for us. And this would allow us to use nice `match` statements and such all the time, only ever working with the one type definition, rather than having to convert between the nice `enum` representation and its packed version all over the place. A related thing that comes up in these kinds of scenarios is a `NonMaxU32`. In general, even better than having just arbitrary bit width integers would be having custom ranges of integers, like Ada has (something like `type MyInt = integer 36..=42;`). Examples of various things that fit under this umbrella: * [`FuncKey{,Kind,Namespace,Index}`, `into[_raw]_parts`, and `from[_raw]_parts`](https://github.com/bytecodealliance/wasmtime/blob/4f2fa1541aa8125bedbfcc272b0c6e3ae108f0a7/crates/environ/src/key.rs): an enum that ultimately should encode/pack into a `(u32, u32)` raw form, but there are a bunch of intermediary newtypes to represent just one half of the pair or the other or just the discriminant without any payload fields * [`RefType`](https://github.com/bytecodealliance/wasm-tools/blob/14db2f3b75a753b1363449707d76d70d0c289c0d/crates/wasmparser/src/readers/core/types.rs#L1006), [`PackedIndex`](https://github.com/bytecodealliance/wasm-tools/blob/14db2f3b75a753b1363449707d76d70d0c289c0d/crates/wasmparser/src/readers/core/types.rs#L64), and [`UnpackedIndex`](https://github.com/bytecodealliance/wasm-tools/blob/14db2f3b75a753b1363449707d76d70d0c289c0d/crates/wasmparser/src/readers/core/types.rs#L243): three-way enum in nice and bitpacked forms, stuffed into a `[u8;3]` in `RefType` so that it doesn't cause `enum ValueType { ..., Ref(RefType) }` to grow beyond size/align of 4 bytes. * [`ValueData`](https://github.com/bytecodealliance/wasmtime/blob/4f2fa1541aa8125bedbfcc272b0c6e3ae108f0a7/cranelift/codegen/src/ir/dfg.rs#L698) and [`ValueDataPacked`](https://github.com/bytecodealliance/wasmtime/blob/4f2fa1541aa8125bedbfcc272b0c6e3ae108f0a7/cranelift/codegen/src/ir/dfg.rs#L730): a four-way enum in nice and bitpacked forms * [`Operand` and `Operand{Pos,Kind,Constraint}` and `VReg`](https://github.com/bytecodealliance/regalloc2/blob/main/src/lib.rs#L630): has a bunch of fields and cannot just be divided into arbitrary bitwidth fields (steals bits from some fields when upper bits of the discriminant are in some states for more sub-discriminant) * [`PackedOption`](https://github.com/bytecodealliance/wasmtime/blob/4f2fa1541aa8125bedbfcc272b0c6e3ae108f0a7/cranelift/entity/src/packed_option.rs#L31): basically `Option<T>` where `T` is a newtype of a hypothetical `NonMaxU32`. [↑ go back to top](#Top) ### Representing ownership to avoid dynamic checks We currently have a bunch of dynamic "is same engine" and "is same store" checks, out of necessity, because our index-based handles could otherwise lead to incorrectness. We would need "generative" or "path-dependent" or "place-based" types/lifetimes to statically encode, in the types, that e.g. an `Instance` is associated with a particular engine, and ensure only values from the same engine interact; Rust's type system today mostly can't do this. (Gankra's masters thesis contains a neat closure-trick that is technically generative within the scope of a closure, but would be completely unergonomic in our APIs.) I know the Rust community generally is thinking about this; and Niko has a [blog post](https://smallcultfollowing.com/babysteps/blog/2024/06/02/the-borrow-checker-within/ "https://smallcultfollowing.com/babysteps/blog/2024/06/02/the-borrow-checker-within/") that mentions "place-based lifetimes" that is a step in the direction. I wrote up a random [idea](https://cfallin.org/blog/2024/06/12/rust-path-generics/ "https://cfallin.org/blog/2024/06/12/rust-path-generics/") last year (but haven't seriously pursued it); in general I'd just be curious if this problem has further thoughts from the lang-team, and emphasize that it occurs in practice and leads to dynamic checks. [↑ go back to top](#Top) ### x86_64-unknown-none softfloat abi The `x86_64-unknown-none` target uses softfloats in its ABI (passed on stack), which means that it is unsuitable for linking against C code that uses hardware floats (passed in registers), as the soft vs hard float ABI mismatch will result in UB/memory unsafety when passing floats across the Rust/C boundary. Both MicroSoft and F5 ran into this issue when attempting to use no-std x86_64 Wasmtime builds. There is no "normal sys-v on x86_64 but without `std`" target available in rustup, but people keep trying to use `x86_64-unknown-none` as if it were that target, and running into issues. In theory, the solution is to use a custom target and build `std` for that target, but that requires unstable nightly features. [↑ go back to top](#Top) ### Partial Borrows Wasmtime has got a giant `Store<T>` type which is passed everywhere internally. More and more methods need simultaneous borrows to multiple fields in this type, but in an effort to keep the implementation details private we keep adding more and more methods for various permutations of fields. It'd be nice if we could call "base methods" where each method accesses a disjoint set of fields. [↑ go back to top](#Top) ### Async entry points for WASI 0.3 target Wasm Components recently introduced native support for async and streams at the ABI level, which the upcoming WASI 0.3 target will make heavy use of. As a consequence of this, [the main entry point for WASI 0.3](https://docs.rs/wasip3/0.2.0+wasi-0.3.0-rc-2025-09-16/wasip3/exports/cli/run/trait.Guest.html) is an async function: ```rust // wasip3::exports::cli::run::Guest pub trait Guest { async fn run() -> Result<(), ()>; } ``` Most Rust targets require a fair amount of ceremony to stand up async support, typically involving initializing an in-process async runtime. With WASI 0.3 the async runtime is *external* to the program, and communication happens via an async ABI. This means that there is no way to configure or have opinions about async runtimes from within the program, and so having an `async fn main` *just work* for WASI 0.3 is something that should be possible if we wanted it to. [↑ go back to top](#Top) ### Extern “wasm” / repr(wasm) This would be an eventual “nice to have”, but it would be nice to eventually drive towards better integration of Rust and WebAssembly. Creating bindings between Rust and WebAssembly Components currently depends on the `wit-bindgen` tool. While this works well, like with any other bindgen tool this does add intermediate codegen files and adds additional friction. As we steer towards the eventual stabilization of Wasm Components and WASI, it would be nice to discuss what a more native integration of WebAssembly in Rust could look like. Working towards something like `extern "wasm"` and `#[repr(wasm)]` being the ideal. [↑ go back to top](#Top) ### Infectiousness of Send with async Wasmtime's current async support effectively ends up meaning that we require Send on pretty much everything. There's no way currently to use Wasmtime in a non-Send context or a single-threaded async executor. Unsure what a solution would look like, but it's something we've come up against. [↑ go back to top](#Top) ### Generic parameters in constant expressions We've run into this not compiling: ```rust trait A { const N: usize; fn foo(&self, slice: &[u8; Self::N]); } ``` and it'd be a nice simplification of canonical ABI lifting/lowering if were were able to express this [↑ go back to top](#Top) ### Wasm proc macros Rustc should have more wasm in it 😈 (this is a joke, not a serious ask) [↑ go back to top](#Top) ## Appendixes ### Appendix A: Platform Comparison The WebAssembly targets follow a structure that is very similar to other platforms, even if some of the words used may be new to people here. Here is how the various platforms compare: | | WebAssembly | Linux | Windows | macOS | | --------------------------------- | ------------------------------------ | ------------------------------------------- | --------------------------------- | ----------------------------------------------------- | | **Instruction Format** | [Core Wasm] | x86, ARM, etc. | x86, ARM | ARM | | **Container Format** | [Wasm Components] | [Executable and Linkable Format][ELF] (ELF) | [Portable Executable][PE] (PE) | [Mach-O] | | **Interface Definition Language** | [Wasm Interface Types][WIT] (WIT) | C header files | [Windows Metadata][WINMD] (WinMD) | (Objective-)C header files + Mach IDL + Swift Modules | | **System Interfaces** | [Wasm System Interface][WASI] (WASI) | [POSIX] + [Linux User-Space APIs] | [Win32] + [UWP] | [POSIX] + Darwin Syscalls | [Core Wasm]: https://webassembly.github.io/spec/core/ [ELF]: https://en.wikipedia.org/wiki/Executable_and_Linkable_Format [Linux User-Space APIs]: https://docs.kernel.org/userspace-api/index.html [MIDL]: https://learn.microsoft.com/en-us/uwp/midl-3/ [Mach-O]: https://en.wikipedia.org/wiki/Mach-O [PE]: https://en.wikipedia.org/wiki/Portable_Executable [POSIX]: https://en.wikipedia.org/wiki/POSIX [UWP]: https://en.wikipedia.org/wiki/Universal_Windows_Platform [WASI]: https://wasi.dev/ [WIT]: https://component-model.bytecodealliance.org/design/wit.html [Wasm Components]: https://github.com/WebAssembly/component-model [Win32]: https://en.wikipedia.org/wiki/Windows_API [WINMD]: https://learn.microsoft.com/en-us/uwp/winrt-cref/winmd-files [↑ go back to top](#Top) ### Appendix B: Glossary In conversation it’s likely that the Wasmtime developers will use terms the Rust language team may have heard of but not necessarily remember what they mean. To help with that, here is an explainer of some common terms: - **Core WebAssembly**: The WebAssembly instructions/bytecode. “WebAssembly” and “Core WebAssembly” can be used interchangeably. - **WebAssembly Components**: A portable container format for Core WebAssembly instructions. It defines its own calling convention. - **WebAssembly Interface Types**: The IDL used to define the Wasm Component interfaces with. - **WASI 0.1** also known as “WASI snapshot 1”, “WASI P1”, and “WASI Preview 1”. This is the experimental C-ABI-based syscall/stdlib layer that was introduced in 2019. This is no longer actively being developed. - **WASI 0.2**: also known as “WASI P2” and “WASI Preview 2”. This defines a set of standard interfaces for things like sockets, file IO, and HTTP using Wasm Components. - **WASI 0.3**: aka “WASI P3” or “WASI Preview 3”. We’re adding native async + streams to Wasm Components (e.g. at the ABI level). WASI 0.3 rebases the WASI 0.2 interfaces on this newly introduced system. The work isn’t stable yet, but Wasmtime already implements it behind a flag. [↑ go back to top](#Top) ### Appendix C: WASI Versions To expand on the glossary terms above: * **WASIp1** - no longer in development. Completely unrelated to the component model. Completely custom ABI and type system used to define APIs, and most APIs looked more-or-less like a C signature. * **WASIp2** - first release of WASI "rebased" on the Component Model. All APIs defined in terms of WIT using component model types (e.g. `record`, `string`, etc). Functionality-wise WASIp2 is a superset of WASIp1 and notably includes support for TCP/UDP sockets. This more-or-less conicided with the first "release" of the component model itself, although the component model doesn't have an official release cadence. In lieu of that WASI versions have been used to introduce component model features. Regardless this is why Rust's `wasm32-wasip2` target, for example, produces a component instead of a core module as output. * **WASIp3** - next in-development version of WASI, not currently released/stamped. Major feature over WASIp2 is native async support in the component model itself, reflected in WIT as `async` functions for example. Additionally there are two new types in WIT, `future<T>` and `stream<T>`. Functionality remains the same as WASIp2 except that doing async operations is "much nicer" in terms of how languages integrate. Semantically it's now possible to invoke concurrent computations within a single component if it's exported as an `async` function in WIT. Notably WASIp1 is *incompatible* with WASIp{2,3} due to how its APIs are defined. The core-level ABI and types of WASIp1 are incompatible with the component model and WASIp{2,3}. For WASIp{2,3}, however, the underyling format is "just a component" which means that a component can simultaneously import, and export WASIp2 and WASIp3 APIs. This means that all development of WASIp3-as-a-standard has been using the `wasm32-wasip2` Rust target, for example. WASIp3 does not mean that WASIp2 is gone and inaccessible, but rather WASIp3 means that there's access to more functions. Idiomatically a true WASIp3 target wouldn't use WASIp2 at all, and that's the end goal, but this is not a requirement in the transition period from WASIp2 to WASIp3. [↑ go back to top](#Top) --- # Discussion ## Attendance - People: TC, Aapo Alasuutari, Yosh, Niko, Pat Hickey, Tyler Mandry, Alex Crichton, Till Schneidereit, Chris Fallin, Jack, Jamey Sharp, Tomas Sedovic, Nick Fitzgerald, Amanieu ## Meeting roles - Driver: TC - Minutes: Tomas Sedovic ## Check in with the author TC: Yosh, tell us about the context and history here and what you're hoping for from the meaning. Yosh: I talk to both y'all groups regularly. Would be great if everyone talked to each other. I'm happy everyone made the time to chat here. Alex: Thank you so much everyone. This is not a laundry list of demands. This is stuff we ran into. We're extremely happy with Rust, it's an absolutely foundational part of wasmtime. We want to keep y'all informed, keeping the relationship. Josh: Echoing this right back. I think Rust overall has been really happy with how WASM and Rust fit really well together. It's great how much of WASM has been Rust first and that in large part thanks to the wasmtime folks. When people go write WASM, the obvious choice for people is to use Rust. Given how much our communities benefit each other, there's a lot of what we want already and also novel things where seeing concrete usecases that can help us. Maybe we can hand some long-standing background issues to hand over to someone in WebAssembly folks. ## Vibe checks ### nikomatsakis Love this doc so much. I want to do everything. lol. I am curious which of these are "fundamental" -- i.e., not just inconvenient, but blocking or impacting ability of Rust to deliver or be used with wasm. I feel like e.g. linear types might fit from what Till once told me (but don't know the latest details). Fitzgen: The only thing that's fundamental is the Pulley thread stuff. Niko: I'm curious about the linear types. Alex: Linear types are ones, send and lifetime pointers, unsafe fields -- that's limited a lot of the internal implementation of wasmtime. ### tmandry I'm really grateful for the feedback in this document; it's nice to hear actual problems people encounter with Rust. The reality check is great, especially from what I would call some very important "foundational software", to borrow a term from Niko. The "pervasiveness of Send with async" problem makes me pretty sad. There are different kinds of async environments, some of which care about sending tasks to other threads and some of which do not. I think we will have to accept that most users are just going to want a "mode switch" however you spell it. As a general comment, it's funny to me how often I see asks in this doc that are similar to asks from the Linux kernel. Those seem like very different applications, but on reflection they aren't so different. ### scottmcm (Not present.) ### Josh First of all, huge appreciation to the wasmtime team for taking the time to talk to us. And just as wasmtime folks are excited about Rust, we're really excited about WebAssembly, and the degree to which people using WebAssembly have treated Rust as an ideal language to implement things. It's been a great boon to Rust. Made a bunch of responses to individual items, and I'm realizing that many of those those should be down in the questions sections people added for each of those. These should probably pivot so that we have vibes organized by topic rather than by person. :) High-level: I think there are novel takes here about many things that we've wanted to do, or things we've had vague notions about, and we should evaluate those. For some of the others, there are proposals in progress, and I wonder to what extent those proposals align with wasmtime's needs. ### TC Great document. Thanks for putting this together for us. It's valuable to hear this feedback, and I'm interested in how we might be able to keep a tighter feedback loop, on an ongoing basis, as we do with RfL. It seems too that it'd be valuable if we could find more ways to use WASM in Rust itself, e.g. for our proc macros. Perhaps a greater use of WASM within Rust would be helpful to encourage more of this particular back and forth. ### Aapo Very familiar ground from Nova JavaScript engine, hence going to focus on commonalities and differences. * async, Pin: Not using these in Nova; async is implemented manually, and heap structure and reference style is purposefully chosen to be aggressively Unpin. ## :lightning: Lightning round :lightning: ### Tail call optimizations for Pulley nikomatsakis: let's do it -- but yeah we have some stuff in progress, right? I prefer `loop match` Tyler: I think we need a future here. Whether it's become or loop match. I'd probably pick loop match just because of the straightforward imperative control flow. People have been asknig for this for many years. Josh: We have features in progress for *both* tail calls *and* efficient match loops. It'd be helpful to know, given the efficiency work being driven by Folkert on the latter, which one is faster and which one is the preferred architecture. Regardless, :+1: for having stable tail calls and for making match loops as efficient as possible. Josh: I really want us to ship a `yeet`-like experiment for tail calls. Josh: Oh, we *also* have features in progress for computed/asm goto, if that's what you desire. TC: I absolutely think we need guaranteed tail call elimination. I'm interested in the thoughts of the wasmtime team on the complexities. The platform where this is the hardest to do was actually wasm itself until it added TCO natively. ### Non-overwritable `&mut T` Josh: We've thus far dealt with non-overwritable `&mut` the same way wasmtime has (e.g. in our still-unstable traits for uninitialized buffers). Would be interesting to consider native features for that, and what the right type/structure might be to present that to users. I wonder if partial borrows *might* be a partial solution here? e.g. `&mut 'everythingbutoverwrite T`. If you have methods on `T` that take `&mut 'everythingbutoverwrite self` and that doesn't allow overwrite, could that work? nikomatsakis: want it, hard to get it. It is hard to get it without splitting Rust to some extent but there would be a lot of advantages. Tyler: I had this come up every now and then. I feel &mut gives you more power than I wish it did. Bringing it back is going to be difficult. Curious about some of the other ideas: making more ergonomic custom reference types. TC: Niko wrote a fantastic proposal (the Overwrite trait). I think what makes overwrite hard is that our inclination that we want most of our types to be overwrite. What if we loosened that? It's okay if most types we wanted to be overwrite might not be but that would make the ?? Niko: I think we would make most types not be overwrite. relevant blog post: https://smallcultfollowing.com/babysteps/blog/2024/09/26/overwrite-trait/ Josh: I think it's worth to think "here's what we'd do if we did a major overhaul" and "where's what we could do more easily". Alex: Rust is giving us a lot of tools in the toolbox and we're not looking for any one particular solution. We have these high level goals and we're very flexible in terms of how to solve them. I wouldn't want y'all to over-rotate on a particular solution or design. ### Read-only fields / unsafe fields nikomatsakis: it seemed like what this really wanted was *truly immutable fields* -- and not so much `unsafe` as we've traditionally understood it. That said, if it's the narrow case of access `x.y` (and not protect against swaps) we should probably just do it. Tyler: +1 for that clarification. I think you can with an unsafe field simulate a read-only field by having an accessor method that only gives you the shared reference. nikomatsakis: That doesn't help guarantee nobody swaps the entire value on top of that. Josh: Unsafe fields have a proposal; would the current unsafe fields proposal work for wasmtime? There's also a proposal for "restrictions", which could be used for read-only fields. (Some versions of proposal could allow for enough nuance for things like "pub but only privately mutable" as well as "never mutable after initialization".) TC: I agree with all that. I think we accepted the RFC on restrictions. But nobody's picked up the impl. I'd be interested in the thoughts of the wasmtime team on the RFC and restrictions. A recent change to that RFC was consolidating the fields to where it's unsafe to touch the field at all. Is that useful to wasmtime? Does it fit the model? Josh: I think they did but there may be some subsequent pushback on the syntax. https://github.com/rust-lang/rfcs/blob/master/text/3323-restrictions.md https://github.com/rust-lang/rfcs/pull/3458 (and *many* discussions about how it should work). ### Working with Pin is hard TC: We have an in-progress lang experiemnt for pin-ergomonics which is addressing a lot of these points. As we go along with that, I'd be particularly interested in you guys trying it out and how it imppcats wasmtime. Josh: :100: `:mood:`. I would be interested to know to what degree your preferred solutions would be "we need to use Pin for the necessary things for the types we're using" or "in an ideal world we'd rather not use Pin". Where is Pin coming up and are these cases where there's absolutely no way other than using Pin? For instance, if we had native support for self-referential types. nikomatsakis: we're working on it I think, does the existing scope handle these issues? Tyler: +1. I think we're working on all the issues the doc brought up. ### Unleakable types Josh: Unleakable types - would love to have these (some form of linear typing). At least at the moment, passing ownership around has been our expected model for working with things like io-uring as well, but I'd personally be interested in seeing what better models might be possible if we had linear types. I'd really like to do this using one of the simple proposals that doesn't make a larger overhaul to Rust; those larger overhauls seem relevant for being able to handle these types generically (on par with `?Sized` or possibly worse), but it seems like we could defer them for many simple cases. nikomatsakis: I think we should do this and soon, see my detailed comments below. I don't think it's that hard from the language perspective. This has always been in the back of my mind. It's contorting us in other ways where it shows up, e.g. why async is annoying around references which is pushing us towards Arc etc. We keep finding workarounds that kind of work but that push costs on our users. Tyler: +1 to that. I wrote a whole blog post about this problem and how linear types could help us. I'm excited about the idea of adding unleakable types and possibly undroppable types. Also nervous about the transition. I think about the ability to have a scoped async task and that would be a huge one. Aapo: Linear types: mojo recently did a blog post on linear types. That seemed very interesting and straightforward and nice. TC: Interested to dig into Niko's porposal. What's most exciting an unique about the Rust language is that we're willing to tackles some of these problems in a way that it's useful in the language. Josh: Seconded. A lot of languages have big new ideas at their inception, but have a harder time introducing major things later. Rust seems to be well positioned (and unafraid) to do these big overhauls, TC often says: "Rust is its own successor", we don't have to wait for another language to provide the next big idea. ### Send bounds on async closures Niko: It's been a huge problem. I'm hitting it too, it's very frustrating. We need to fix it. TC: Are we waiting on someone implementing it? Niko: We don't have a great syntax for it. We could use RTN but even that's very awkwarde. Tyler: We need to solve this problem. I thought we had a syntax we could use and we're waiting on the implementation on it. Niko: I think we had an RTN syntax on it, but it wasn't a one-liner. Yosh: This is currently blocked on the new trait solver -- based on my talking to the Types team. Josh: I'm not aware of any syntactic blocker and I thought we can get to the experiment. Josh: we have an accepted feature for this (return-type notation, "RTN"), does it solve what wasmtime needs? TC: I second everything said there. I'm looking forward to the new solver landing. It's in a critical path for many useful things. ### Dyn-compatible `async fn` nikomatsakis: I think we should do this and soon, see my detailed comments below. It seems we've gone down this path, we have and we're focused on general designs and I'd still like to ship a version that's still useful for the users without waiting for the full completely generic solution. Tyler: I worked on a different proc macro (dynosaur) that should remove the need to remove async trait directly (do a static dispatch). I think the docs are improving and there needs to be a clear onboarding path. Niko: I love dynosaur. A lot of times it's `dyn async fn` that I really want. Josh: yes please. Would be interested to know what your constraints and desires for a solution would be, in terms of heap allocation vs stack allocation vs other potential solutions. TC: I agree with that. I've heard exciting things about dynosaur and need to look into it. Josh: Second plug for dynosaur. It's been significantly written by my colleague Santiago Pastorino who is now looking for new sources of support, raising that here. ### Function pointers may have over-constrained lifetimes Josh: This is the first time I've heard of this problem! Sounds plausible, we'll need to dig into whether there's some underlying property being enforced with this requirement or whether it could be relaxed. Thanks for raising a novel issue that hasn't seemed to be on the radar! Niko: This is RFC 1214. It's not so easy to fix. Editions will not help us much. I think we should at least try and implement it and see how bad the Crater run is. Or we can find some other fix. It's worth a deep dive. Tyler: I didn't realize this problem existed. It does seem nice if we could fix it. TC: I've run into this problem myself. I'm very interested that Niko now realises what we should have done. Can we get away with doing the right thing now? ### Removing remaining uses of C in Wasmtime Niko: Stroustroup says "there should be no room for a language between assembly and C++" and I feel the same way, "there should be reason to reach for C just because some random thing is missing". Similarly, when you're using Rust there should be no reason to reach for C. "Just don't make me check the checkbox please. ;)" Tyler: +1 to that. We should make sure people always have a path forward in Rust. Josh: - We've talked about ways to support `setjmp`, but I'm happy you don't need this anymore. :) - :+1: for weak symbols, and dllexport. - :+1: for a convenient way to import weak symbols. This would need to use `Option`, I think? Is that the interface you'd expect? TC: To second what Niko said. One of his talking points is: "don't leave anything on the table". Rust shouldn't leave anything on the table. Jeff Bezos said "your margin is my opportunity". No one should be able to undercut Rust on getting down to the metal. ### Pulley, Threads, and the Rust Memory Model Niko: I thought this was fascinating. It's a really useful example to talk to Ralf Jung about: as we go about defining what is and isn't UB in Rust. Holding strongly to an upper and lower bound (e.g. "this is always UB" vs. "this could be, but ??"). In principle removing UB should never cause problems, unless the compiler relies on it for optimizations. Aapo: I wrote/copied the `ecmascript_atomics` library. One of the things that was present in the Firefox source was [a paper of optimisations in C](https://www.usenix.org/legacy/event/hotpar11/tech/final_files/Boehm.pdf) that it can or cannot do that would break when racy atomics are allowed. It's maybe worth to note that the ecmascript memory model doesn't say data races are okay. It says you shouldn't do it but we don't have any way to stop it. Aapo: There was a question about atomic memcpy being sufficient to fix it. It doesn't suffice, whenever you have aligned atomic read and aligned write, those two can tear: mixed-sized atomic reads and writes are allowed as well. Tyler: Maybe we should reserve space for "you can't write code that does this" but also "you can't assume that this will never happen". I think this is an interesting and important usecase to surface. Josh: I think we *should* have a mechanism for declaring an intentionally racing read. That's going to take some long conversations with the memory model semantics folks, but I don't think "no, you can't have that" (or "you have to write assembly") is an answer we should settle for there. Being able to do a racing read (with some limitations) is important for various other kinds of non-blocking synchronization, such as RCU. Josh: :+1: for separating apart the two issues of tearing/byte-by-byte races versus handling racing reads/writes on aligned values. The latter seems most important, the former seems like it'd be okay if it's more of a pain as long as it's *possible*. TC: Seconding what's been said here. What makes me think about this in the specification work and writing down the memory model makes it clear to the security people. Niko: We have some minor precedent on this point. ### Bitpacking Niko: I'm so mad Erlang has cool bitpacking stuff and we don't. Erlang is a high-level VM language and they have this. What gives. We should fix this. Tyler: We should support this. We have unaligned fields, doesn't seem like a far cry from this. Josh: - pattern types (which *should* also solve variable-width integers but we might want some dedicated support for those) - Stronger ways of doing structure packing and providing niches - Would move-only types ("the structure has one of these but can't give you a normal reference to it because it isn't materialized in memory") help here? Josh: if we had that, we could let the compiler use to optimize. And we could let people write this. TC: Let's do it. Leave nothing on the table. Tyler: Also I want more tools for customizing layout of a type, even without bitpacking. ### Representing ownership to avoid dynamic checks Josh: would like to dig into this one in more detail, here or later. TC: This is asking for the generativity patter to be better built into the language. There are a number of hard language patterns that would rely on that. This gets into Ralf's paper on get Cell (??). Aapo: The same problem comes at you if you had allocators that need to be passed every time. That's a thing at least in some kernels. I want to use that as well. Then the `A` parameter to your `Vec` is a ZST (zero-sized type) which carries the brand of the allocator Tyler: I was just giving a talk where I teased something like this... ```rust fn token<exists 'a>() -> GhostToken<'a> { ... } let token = token(); let cell = GhostCell::new(State::new()); cell.borrow_mut(&mut token).modify(); let value = cell.borrow(&token).get(); fn ``` nikomatsakis: At some point we talked about having a syntax like: ```rust { ... do scope = rayon::scope(); ... } ``` which is sugar for ```rust { ... rayon::scope(|scope| ...) } ``` I wonder if that'd be relevant. ### x86_64-unknown-none softfloat abi nikomatsakis: oh god I hate this kind of thing. let josh talk. Josh: :+1: for having a -none hardfloat target. We need a better story here in general. We should give better ways of handling this than writing the full target json (which will probably never be feasible to stabilize). I'd like to see a good design for this. I'd like it to have a good third or half of the power you get with target.json Tyler: Agreed. I'd also like to be able to have a target that doesn't have targets at all. Like the kernel. ## Partial borrows Niko: I want it: https://smallcultfollowing.com/babysteps/blog/2024/06/02/the-borrow-checker-within/ Tyler: Niko's blog post is really interesting. I'm also exploring new types in other applications. Josh: Ship it. We know we want it. We need to design it. We've settled one of the biggest designs: coming down on the side of declaration rather than implicit inference. So we'll likely land on some declared subsets and you'd use those subsets. The subsets should be named and the types they cover be private so you can change them. Aapo: Nova uses a "singleton" `&mut Agent` which contains everything, partial-borrowing it would be a godsend in some places. Josh: "Partial Borrows" - *extremely* amenable, we all want this very much, we've had design discussions, this is something where help will very likely be welcome. We could talk through the design details we've covered before, and talk about next steps to get an RFC written and accepted. We've talked extensively about the tradeoff between detection and declaration. I think it's pretty clear we want something (we hit the end of the time, some people had to leave) ## Async entry points for WASI 0.3 target Niko: I really want async main. I love the idea that wasm would have a native concept. Josh: *interesting*! We want `async fn main` to work in a variety of other cases too. We should balance how much design we need to do for the general case versus supporting the native WASI case. Niko: Within Amazon we talked about "amazon main" with the goal of setting up the server that you want to (with observability etc.) I feel there's a thing hiding here of different ways of inject different configuration to main. ## Extern “wasm” / repr(wasm) Niko: I don't have a problem with that. Doesn't seem particularly different than random ABIs on other platforms. Josh: very much want to see more "native" support, and how much better we could do with native support than we could do with any kind of bindgen-like tooling. This is true both for exporting and importing functions. You shouldn't need to duplicate or translate interface definitions, ideally. ## Infectiousness of Send with async Josh: something about being generic over `Send`, we don't have good solutions here yet and need them. Tyler: This problem makes me pretty sad. There are different kinds of async environments, some of which care about sending tasks to other threads and some of which do not. I think we will have to accept that most users are just going to want a "mode switch" however you spell it. Maybe something like ```rust use async(Send) as async; async fn foo(callback: impl async Fn()) { callback().await } ``` Niko: I think I agreed with Tyler everywhere up until he said we should be generic over async. TC: When we were doing const impl with trait work, it kept occuring to me there were many places where we wanted to do something like that with send and sync. ## Generic parameters in constant expressions Niko: I think we should do this, I forget why we don't. It's probably a better one for Boxy. TC: I agree with that. Josh: I think this is planned on the const roadmap. ## Wasm proc macros Josh: :) Niko: I want to write the compiler with proc macros all the way down. Targeting webassembly gives you amazing power where you can generate wasm code and then run it within your project -- *safely*. As we seek out more corners of the C universe. If we want to integrate with NVIDIA and their whole CUDA platforms. We have to make a more flexible compiler pipeline to make that work. Or C++ interop. TC: Do you think it could apply to const eval too? Niko: That was my plan. I didn't want to have const eval, I wanted to have wasm. TC: For context, we've talked about a more pluggable compiler. Niko often brought up F#'s type providers as an interesting example. Aapo: The ?? implementation compiler is doing a similar thing by interacting from Rust to JavaScript. And the interface is a big problem on how to make it fast. ## Check-in with wasmtime people Alex: I realise we should have made a smaller list. It's been great. A lot of this has been familiar. I'm looking forward to chatting more about this. Niko: If you could have one/two things. What do we want to do in the next year? Alex: It's difficult because we have our own priorities. I'd probably say linear types and partial borrows. Yosh: This was great. I enjoy hanging out with both groups. Everyne going "yeah I agree with you we should do this". More wasm integration with rustc would be great. Maybe at some point rustc meeting with webassembly group telling them what they sholud change. TC: Where is wasmtime right now? Where is it going? Alex: Wasmtime had most success in edge companies, out of browser. It's been difficult for us: we're trying to make a web standard-like impact thing without having the web standard people. Wasmtime is pretty secure in its development from the number of companies right now. But it requeres a lot of investment and development from the wasmtime folks. TC: How are youfunded? Who invests in this? Alex: Chris, Nick and Pat work at F5. Fastly employ some folks. I work for a startup called Fermion. Yosh works at Microsoft. TC: One of the challenges about `become` and tail calls. It didn't use to suport tail calls natively. Alex: It should be a compiuler errer if it can't produce a tail call. Tail calls are going to be used by everyone within the next six months or so. The browser have it. Wasmtime has it. TC: LLVM limits for the platforms that don't have it. What guarantees we can rely on. What model is rgiht for this. Should we not rely on LLVM and implement lowering on our side? Alex: one of our primary requirements is portability. We need to be able to write some code that compilers everywhere. We don't necessarily care that `extern(C)` supports tail calls. There needs to be some way to make this portable across platforms. fitzgen: I think Alex is saying If LLVM doesn't supportit than Rust shouldn't sythesize it. I don't necessarily agree. It's not the end of the world if we keep special implementations for our portability story. Chris: The most important principle is to avoid surprises. If Rust could see a way to implement tail calls with their own trampolines, we wouldn't use that. We'd rely on our code. Similar to SIMD, we want to expose the capabilities of the underlying platform and then write our won code if it's not there. TC: ?? fitzgen: I think our specific usecase with Pulley wouldn't be affected. All our functions have the same signature. Amanieu: From the library perspective, I don't think there's much interactino from the wasmtime side with teh library. fitzgen: I do have a libs request that we ran into: exposing the btree cursor stuff and stabilizing that. Theer's a lots of cool stuff you can do with btrees but you can't do it in stable rust yet. Amanieu: You're the thing person who's asked me about this over the last year. I'm not too happy with the state of the cursor API so far. Have you tried the unstable API yet? fitzgen: I haven't, but I want the method. Something becoemes `O(n log(n))`. I see the method there and it would allow me to do what I need to but I can't use it. Amanieu: I recently implemented a completely new btree implementation and write a cursor api for it and that's completely different to the one in teh standard library. There's pros and cons to both approaches. We need more feedback on both. Jack: I tihnk the biggest thing that stood out to me was how many things are not new. Basically everything was something I've discussed with Niko or brought up with people. In soem sense it's reassuring. But it's also terrifying because there's so many things we should be doing. It'd be nice to get more feedback on actual solutions. TC: That's a good way to put it. It's terrifying -- a lot of these things are hard, that's why we talked about them for such a long time. TC: Great talking with everyone here. Thanks again. This was a wonderful feedback and wonderful doc. ## "non-overwritable fields" and "read-only / unsafe fields" are entangled nikomatsakis: I'm sure everyone saw [my blog post on non-overwritable fields](https://smallcultfollowing.com/babysteps/blog/2024/09/26/overwrite-trait/), but I want to note that the main challenge is being able to have fields that you know won't change -- i.e., a type `T` where you can't do `std::mem::replace::<T>(p1, p2)` Aapo: Not understanding why fields are eg. `pub(crate)` at all if they have safety invariants. ## I believe there is a good path to linear / nonleakable types nikomatsakis: I've been meaning to sketch this out as a blog post for an awfully long time -- I've got to get the one on ergonomic ref counting out first! -- but I think there is a "not that hard" (from a language POV) way to get linear/non-leakable types (which I then think we would use to also make async drop that is safe). The basic idea is to have a hierarchy ```rust trait Pointee // Root of the hierarcy, exists today trait Move: Pointee // can be moved trait Drop: Move // can be dropped trait Forget // can be forgotten ``` A value of some `impl Move` type can be moved from place-to-place. In order to have a control-flow that takes a place out of scope without moving from it, that place would have to implement `Drop` (so we can run the drop). And in order to be "forgotten", it would have to implement `Forget`. These checks are fairly straightforward to implement in the borrow checker, in my opinion. Just as we plan to do with `Sized`, when you write a `<T>` generic parameter, it is `Forget` by default. But you could *specify* a weaker bound ```rust // Works for any type `T` that can be *moved* fn std::mem::replace<T: Move>(t: &mut T, value: T) -> T { ... } ``` The trickiest bit is going to be bounds on associated types, as usual -- for example, we definitely want closures to not quite their return types to be `Forget`! And of course over time we may, with an edition, wish to make `Drop` the default for generic values. And we'll have to annotate various methods to use the right bounds (many generic things are just `Move`, but not all). Aapo: Yes please! Yosh: This would be great. I worked with lcnr recently to [estimate the perf impact of move](https://lcnr.de/blog/2025/07/28/implicit-auto-trait-bounds.html), and it's likely to be around 5-10%. I would like to expand that check to the other traits too. I know T-Types would really appreciate that. ## Use of setjmp tmandry: Curious what you were using setjmp for (and presumably longjmp) and how you removed it. nikomatsakis: Worth noting that correct use of setjmp is an issue for the Aurora DSQL team, because postgres uses it for exception handling. Amanieu: There's 2 aspects to `setjmp`: 1. It can't be called from Rust because it uses a "returns twice" ABI hack that is barely supported in C and will never be supported in Rust. This can be somewhat worked around by using inline asm to call it instead. Or alternatively it is possible to implement `setjmp` and `longjmp` entirely using inline asm. 2. It's not clear whether using `longjmp` to skip over Rust frames is sound. I believe the last consensus was that it is allowed if you don't skip over any active drops that would have been executed by an unwind, but I don't recall exactly. ## Tail calls and WASM TC: From the document: > but from Wasmtime's perspective the main goal of Pulley is portability, so a solution where tail calls were only supported on some platforms would work but wouldn't be ideal for us. Ironically, the platform that we often worry about the most when it comes to tail call portability is WASM itself. That's the hardest one to support (without the WASM feature that allows natively for tail calls). I'm curious to hear the thoughts of the wasmtime team on how we should think Aapo: very much looking forward to these as well, Nova not necessarily 100% in on portability so tail-calls being "required" would be acceptable. Though hearing that wasm is lacking support is a little worrying. name: Question/comment. ## Alternate atomic models tmandry: Interesting that wasm has its own atomic model that's incompatible with Rust's. My reading of the document is that it's strictly more permissive than Rust's, correct? The Linux kernel also has its own atomic model. As a general comment, it's funny to me how often I'm seeing overlapping use cases between Linux kernel, interop, and wasm. nikomatsakis: This fits with something that I've often talked to Ralf about, which is: one challenge with "UB" is that, while in principle you can take away UB and everything is hunky dory, that's not true if unsafe code or optimizations are relying on something being UB to justify themselves ("this can't happen beacuse that'd be UB"). Which makes the point that we really need two levels, I think, a kind of "lower bound" on UB-- this will ALWAYS be UB in Rust, you can rely on that; and an UPPER bound -- this is currently or sometimes UB in Rust, you should not do it unless you have other reasons to think it is ok. Amanieu: Is this something that would be solved by [`AtomicPerByte`](https://github.com/rust-lang/rfcs/pull/3301). This is intended to allow safely accessing memory that may be concurrently modified by an untrusted process, e.g. a memory-mapped file. It compiles down to roughly the same code as memcpy. Aapo: I'm looking forward to collaborating on `ecmascript_atomics`! The API is pretty rough right now, I'm only now working on actually using the API from Nova and finding additions I need to make, as well as places where removals are then in order in the future. Related note; specialisation comes up here and there. The crate is set up to support `u8`, ..., and `u64` for "storage", and could perhaps be better set up to support signed integers as well as floats in the same sizes as well. Doing this in a generic way leads to specialisation and is currently done by manually checking `if core::any::TypeId::of::<T>() == core::any::TypeId::of::<u8>()` with `transmute_copy` used for T <--> uN conversions. @Amanieu: I don't think `AtomicPerByte` will solve this. It solves the memcpy part of the ECMAScript memory model spec, but the memory model isn't quite weak enough to allow arbitrary bytes to tear. ECMAScript's memcpy and unaligned reads/writes can tear freely, but aligned reads/writes must not. In effect, `AtomicPerByte` + mixed-size Atomics are required to solve the issue completely. ## Working with Pin is hard TC: See: https://github.com/rust-lang/rust/issues/130494 ## Non-overwritable `&mut T` TC: See: https://smallcultfollowing.com/babysteps/blog/2024/09/26/overwrite-trait/ Aapo: Ha, Reborrow! And `!Move`? ## Send bounds on async closures TC: From the doc: > Wasmtime would love to use async closures more often in the codebases, but it currently can’t because it can’t bound the return type (e.g. `where F: AsyncFn(), F(..): Send`). This is unlikely to be news to T-Lang, but we still wanted to list this since it is important for Wasmtime. TC: This is probably mostly waiting on implementation work. Any ideas about who might be able to do this? ## Rustc should have more wasm in it 😈 nikomatsakis: FWIW, I agree =), in a few different ways. First, I see more and more needs for an extensible Rust compiler, and WASM seems great for that (though it just solves a small part of the problem, and often not the most interesting one). But also with Dada I was experimenting with a compiler that had a native WASM backend, and one of the really cool implications of that (not that I ever got this working...) was that in theory you could generate Dada code during compilation and then execute it within that same compilation, which I intended to use for proc macros and all kinds of other things. ## Function pointers may have over-constrained lifetimes TC: From the doc: > It seems weird that `fn(T)` is not `'static` unless `T: 'static`. I'm interested to hear what Niko, in particular, has to say about this and the history here. nikomatsakis: We should change it, but it's tricky. The origin is [RFC 1214](https://rust-lang.github.io/rfcs/1214-projections-lifetimes-and-wf.html?highlight=outlive#) and specifically the desire to have the rule that `<T0 as Trait<T1..Tn>>::Type: 'a` if `Ti: 'a` for all `Ti`. This rule is essential to Rust *but* it is trivially unsound if you have something like this ```rust impl<'a> Switch for fn(&'a u32) { type Output = &'a i32; } ``` because now `<fn(&'x u32) as Switch>::Output` is `&'x i32` and yet `fn(&'x u32): 'static` is true. In retrospect I think my preferred fix would be to disallow `'a` from appearing in the value of an associated type. I don't know if/how we could get away with changing this, but we probably ought to do a crater run and see. It would be hard or impossible to do with an edition. I am a bit curious to dig deeper into the use case though, i've often found this bites me most when what I really want is `unsafe<'a>` binders on types. ## Bitpacking Aapo: Yeees; Nova exclusively uses types like ```rust #[repr(u8)] enum Value { Undefined, Null, String(NonZeroU32), SmallString([u8; 7]), // ... } ``` this works well enough (with newtypes around NonZeroU32s etc) when only 64-bit platforms are supported, but 32-bit support would kind of call for `NonZeroU24`. Also, `NonMaxU32` would be preferable. ## Ownership Aapo: Nova uses lifetime tricks extensively to eg. track GC safepoints. This topic also ties into allocators with context parameters, ie. R4L. * Question: How does Wasmtime perform GC, where applicable?