The Rust project is currently working towards a slate of 41 project goals, with 13 of them designated as Flagship Goals. This post provides selected updates on our progress towards these goals (or, in some cases, lack thereof). The full details for any particular goal are available in its associated tracking issue on the rust-project-goals repository.
Flagship goals
"Beyond the `&`"
| Progress | |
| Point of contact | |
| Champions | compiler (Oliver Scherer), lang (TC) |
| Task owners |
1 detailed update available.
-
Key developments: forbid manual impl of
Unpinfor#[pin_v2]types. -
Blockers: PRs waiting for review:
- impl
Drop::pin_drop(the submodule issue) - coercion of
&pin mut|const T<->&[mut] T
- impl
-
Help wanted: None yet.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
5 detailed updates available.
Since we have chosen virtual places as the new approach, we reviewed what open questions are most pressing for the design. Our discussion resulted in the following five questions:
- Should we have 1-level projections xor multi-level projections?
- What is the semantic meaning of the borrow checker rules (
BorrowKind)? - How should we add "canonical projections" for types such that we have nice and short syntax (like
x~yorx.@y)? - What to do about non-indirected containers (Cell, MaybeUninit, Mutex, etc)?
- How does one inspect/query
Projectiontypes?
We will focus on these questions in December as well as implementing FRTs.
Canonical Projections
We have discussed canonical projections and come up with the following solution:
#![allow(unused)] fn main() { pub trait CanonicalReborrow: HasPlace { type Output<'a, P: Projection<Source = Self::Target>>: HasPlace<Target = P::Target> where Self: PlaceBorrow<'a, P, Self::Output<'a, P>>; } }
Implementing this trait permits using the syntax @$place_expr where the place's origin is of the type Self (for example @x.y where x: Self and y is an identifier or tuple index, or @x.y.z etc). It is desugared to be:
#![allow(unused)] fn main() { @<<Self as CanonicalReborrow>::Output<'_, projection_from_place_expr!($place_expr)>> $place_expr }
(The names of the trait, associated type and syntax are not final, better suggestions welcome.)
Reasoning
- We need the
Outputassociated type to support the@x.ysyntax forArcandArcRef. - We put the FRT and lifetime parameter on
Outputin order to force implementers to always provide a canonical reborrow, so if@x.aworks, then@x.balso works (whenbalso is a field of the struct contained byx).- This (sadly or luckily) also has the effect that making
@x.aand@x.breturn different wrapper types is more difficult to implement and requires a fair bit of trait dancing. We should think about discouraging this in the documentation.
- This (sadly or luckily) also has the effect that making
Non-Indirected Containers
Types like MaybeUninit<T>, Cell<T>, ManuallyDrop<T>, RefCell<T> etc. currently do not fit into our virtual places model, since they don't have an indirection. They contain the place directly inline (and some are even repr(transparent)). For this reason, we currently don't have projections available for &mut MaybeUninit<T>.
Enter our new trait PlaceWrapper which these types implement in order to make projections available for them. We call these types place wrappers. Here is the definition of the trait:
#![allow(unused)] fn main() { pub unsafe trait PlaceWrapper<P: Projection<Source = Self::Target>>: HasPlace { type WrappedProjection: Projection<Source = Self>; fn wrap_projection(p: P) -> Self::WrappedProjection; } }
This trait should only be implemented when Self doesn't contain the place as an indirection (so for example Box must not implement the trait). When this trait is implemented, then Self has "virtual fields" available (actually all kinds of place projections). The name of these virtual fields/projections is the same as the ones of the contained place. But their output type is controlled by this trait.
As an example, here is the implementation for MaybeUninit:
#![allow(unused)] fn main() { impl<T, P: Projection<Source = T>> PlaceWrapper<P> for MaybeUninit<T> { type WrappedProjection = TransparentProjection<P, MaybeUninit<T>, MaybeUninit<P::Target>>; fn wrap_projection(p: P) -> Self::WrappedProjection { TransparentProjection(p, PhantomData, PhantomData) } } }
Where TransparentProjection will be available in the standard library defined as:
#![allow(unused)] fn main() { pub struct TransparentProjection<P, Src, Tgt>(P, PhantomData<Src>, PhantomData<Tgt>); impl<P: Projection, Src, Tgt> Projection for TransparentProjection<P, Src, Tgt> { type Source = Src; type Target = Tgt; fn offset(&self) -> usize { self.0.offset() } } }
When there is ambiguity, because the wrapper and the wrapped types both have the same field, the wrapper's field takes precedence (this is the same as it currently works for Deref). It is still possible to refer to the wrapped field by first dereferencing the container, so x.field refers to the wrapper's field and (*x).field refers to the field of the wrapped type.
Field-by-Field Projections vs One-Shot Projections
We have used several different names for these two ways of implementing projections. The first is also called 1-level projections and the second multi-level projections.
The field-by-field approach uses field representing types (FRTs), which represent a single field of a struct with no indirection. When writing something like @x.y.z, we perform the place operation twice, first using the FRT field_of!(X, y) and then again with field_of!(T, z) where T is the resulting type of the first projection.
The second approach called one-shot projections instead extends FRTs with projections, these are compositions of FRTs, can be empty and dynamic. Using these we desugar @x.y.z to a single place operation.
Field-by-field projections have the advantage that they simplify the implementation for users of the feature, the compiler implementation and the mental model that people will have to keep in mind when interacting with field projections. However, they also have pretty big downsides, which either are fundamental to their design or would require significant complification of the feature:
- They have less expressiveness than one-shot projections. For example, when moving out a subsubfield of
x: &own Structby doinglet a = @x.field.a, we have to move outfield, which prevents us from later writinglet b = @x.field.b. One-shot projections allow us to track individual subsubfields with the borrow checker. - Field-by-field projections also make it difficult to define type-changing projections in an inference friendly way. Projecting through multiple fields could result in several changes of types in between, so we would have to require only canonical projections in certain places. However, this requires certain intermediate types for which defining their safety invariants is very complex.
We additionally note that the single function call desugaring is also a simplification that also lends itself much better when explaining what the @ syntax does.
All of this points in the direction of proceeding with one-shot projections and we will most likely do that. However, we must note that the field-by-field approach might yield easier trait definitions that make implementing the various place operations more manageable. There are several open issues on how to design the field-by-field API in the place variation (the previous proposal did have this mapped out clearly, but it does not translate very well to places), which would require significant effort to solve. So at this point we cannot really give a fair comparison. Our initial scouting of the solutions revealed that they all have some sort of limitation (as we explained above for intermediate projection types for example), which make field-by-field projections less desirable. So for the moment, we are set on one-shot projections, but when the time comes to write the RFC we need to revisit the idea of field-by-field projections.
Wiki Project
We started a wiki project at https://rust-lang.github.io/beyond-refs to map out the solution space. We intend to grow it into the single source of truth for the current state of the field projection proposal as well as unfinished and obsolete ideas and connections between them. Additionally, we will aim to add the same kind of information for the in-place initialization effort, since it has overlap with field projections and, more importantly, has a similarly large solution space.
In the beginning you might find many stub pages in the wiki, which we will work on making more complete. We will also mark pages that contain old or abandoned ideas as such as well as mark the current proposal.
This issue will continue to receive regular detailed updates, which are designed for those keeping reasonably up-to-date with the feature. For anyone out of the loop, the wiki project will be a much better place when it contains more content.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
1 detailed update available.
Purpose
A refresher on what we want to achieve here: the most basic form of reborrowing we want to enable is this:
#![allow(unused)] fn main() { // Note: not Clone or Copy #[derive(Reborrow)] struct MyMutMarker<'a>(...); // ... let marker: MyMarkerMut = MyMutMarker::new(); some_call(marker); some_call(marker); }
ie. make it possible for an owned value to be passed into a call twice and have Rust inject a reborrow at each call site to produce a new bitwise copy of the original value for the passing purposes, and mark the original value as disabled for reads and writes for the duration of the borrow.
A notable complication appears with implementing such reborrowing in userland using explicit cals when dealing with returned values:
#![allow(unused)] fn main() { return some_call(marker.reborrow()); }
If the borrowed lifetime escapes through the return value, then this will not compile as the borrowed lifetime is based on a value local to this function. Alongside convenience, this is the major reason for the Reborrow traits work.
CoerceShared is a secondary trait that enables equivalent reborrowing that only disables the original value for writes, ie. matching the &mut T to &T coercion.
Update
We have the Reborrow trait working, albeit currently with a bug in which the marker must be bound as let mut. We are working towards a working CoerceShared trait in the following form:
#![allow(unused)] fn main() { trait CoerceShared<Target: Copy> {} }
Originally the trait had a type Target ADT but this turned out to be unnecessary, as there is no reason to particularly disallow multiple coercion targets. The original reason for using an ADT to disallow multiple coercion targets was based on the trait also having an unsafe method, at which point unscrupulous users could use the trait as a generic coercion trait. Because the trait method was found to be unnecessary, the fear is also unnecessary.
This means that the trait has better chances of working with multiple coercing lifetimes (think a collection of &muts all coercing to &s, or only some of them). However, we are currently avoiding any support of multiple lifetimes as we want to avoid dealing with rmeta before we have the basic functionality working.
"Flexible, fast(er) compilation"
| Progress | |
| Point of contact | |
| Champions | cargo (Eric Huss), compiler (David Wood), libs (Amanieu d'Antras) |
| Task owners |
1 detailed update available.
rust-lang/rfcs#3873 is waiting on one checkbox before entering the final comment period. We had our sync meeting on the 11th and decided that we would enter FCP on rust-lang/rfcs#3874 and rust-lang/rfcs#3875 after rust-lang/rfcs#3873 is accepted. We've responded to almost all of the feedback on the next two RFCs and expect the FCP to act as a forcing-function so that the relevant teams take a look, they can always register concerns if there are things we need to address, and if we need to make any major changes then we'll restart the FCP.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners | bjorn3, Folkert de Vries, [Trifecta Tech Foundation] |
1 detailed update available.
We did not receive the funding we needed to work on this goal, so no progress has been made.
Overall I think the improvements we felt comfortable promising are on the low side. Overall the amount of time spent in codegen for realistic changes to real code bases was smaller than expected, meaning that the improvements that cranelift can deliver for the end-user experience are smaller.
We still believe larger gains can be made with more effort, but did not feel confident in promising hard numbers.
So for now, let's close this.
| Progress | |
| Point of contact | |
| Task owners |
No detailed updates available.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners | @dropbear32, @osiewicz |
No detailed updates available.
"Higher-level Rust"
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
No detailed updates available.
| Progress | |
| Point of contact | |
| Champions | cargo (Ed Page), lang (Josh Triplett), lang-docs (Josh Triplett) |
| Task owners |
1 detailed update available.
Key developments
- A fence length limit was added in response to T-lang feedback (https://github.com/rust-lang/rust/pull/149358)
- Whether to disallow or lint for CR inside of a frontmatter is under discussion (https://github.com/rust-lang/rust/pull/149823)
Blockers
- https://github.com/rust-lang/rust/pull/146377
- rustdoc deciding on and implementing how they want frontmatter handled in doctests
"Unblocking dormant traits"
| Progress | |
| Point of contact | |
| Champions | |
| Task owners | Taylor Cramer, Taylor Cramer & others |
1 detailed update available.
Current status:
- The RFC for
auto implsupertraits has been updated to address SemVer compatibility issues. - There is a parsing PR kicking off an experimental implementation. The tracking issue for this experimental implementation is here.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners | Benno Lossin, Alice Ryhl, Michael Goulet, Taylor Cramer, Josh Triplett, Gary Guo, Yoshua Wuyts |
No detailed updates available.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
1 detailed update available.
We've continued to fix a bunch of smaller issues over the last month. Tim (Theemathas Chirananthavat) helped uncover a new potential issue due to non-fatal overflow which we'll have to consider before stabilizing the new solver: https://github.com/rust-lang/trait-system-refactor-initiative/issues/258.
I fixed two issues myself in https://github.com/rust-lang/rust/pull/148823 and https://github.com/rust-lang/rust/pull/148865.
tiif with help by Boxy fixed query cycles when evaluating constants in where-clauses: https://github.com/rust-lang/rust/pull/148698.
@adwinwhite fixed a subtle issues involving coroutine witnesses in https://github.com/rust-lang/rust/pull/149167 after having diagnosed the underlying issue there last month. They've also fixed a smaller diagnostics issue in https://github.com/rust-lang/rust/pull/149299. Finally, they've also fixed an edge case of impl well-formedness checking in https://github.com/rust-lang/rust/pull/149345.
Shoyu Vanilla fixed a broken interaction of aliases and fudging in https://github.com/rust-lang/rust/pull/149320. Looking into fudging and HIR typeck Expectation handling also uncovered a bunch of broken edge-cases and I've openedhttps://github.com/rust-lang/rust/issues/149379 to track these separately.
I have recently spent some time thinking about the remaining necessary work and posted a write-up on my personal blog: https://lcnr.de/blog/2025/12/01/next-solver-update.html. I am currently trying to get a clearer perspective on our cycle handling while slowly working towards an RFC for the changes there. This is challenging as we don't have a good theoretical foundation here yet.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
No detailed updates available.
Goals looking for help
Other goal updates
| Progress | |
| Point of contact | |
| Champions |
No detailed updates available.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
4 detailed updates available.
PR https://github.com/rust-lang/a-mir-formality/pull/206 contains a "first draft" for the NLL rules. It checks for loan violations (e.g., mutating borrowed data) as well as some notion of outlives requirements. It does not check for move errors and there aren't a lot of tests yet.
The PR also includes two big improvements to the a-mir-formality framework:
- support for
(for_all)rules that can handle "iteration" - tracking proof trees, making it much easier to tell why something is accepted that should not be
Update: opened https://github.com/rust-lang/a-mir-formality/pull/207 which contains support for &mut, wrote some new tests (including one FIXME), and added a test for NLL Problem Case #3 (which behaved as expected).
One interesting thing (cc Ralf Jung) is that we have diverged from MiniRust in a few minor ways:
- We do not support embedding value expressions in place expressions.
- Where MiniRust has a
AddrOfoperator that uses thePtrTypeto decide what kind of operation it is, we have added aRefMIR operation. This is in part because we need information that is not present in MiniRust, specifically a lifetime. - We have also opted to extend
gotowith the ability to take multiple successors, so thatgoto b1, b2can be seen as "goto either b1 or b2 non-deterministically" (the actual opsem would probably be to always go to b1, making this a way to add "fake edges", but the analysis should not assume that).
Update: opened https://github.com/rust-lang/a-mir-formality/pull/210 with today's work. We are discussing how to move the checker to support polonius-alpha. To that end, we introduced feature gates (so that a-mir-formality can model nightly features) and did some refactoring of the type checker aiming at allowing outlives to become flow-sensitive.
| Progress | |
| Point of contact | |
| Champions | compiler (Oliver Scherer), lang (Tyler Mandry), libs (David Tolnay) |
| Task owners |
No detailed updates available.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
No detailed updates available.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
2 detailed updates available.
Since the last update both of my PRs I mentioned have landed, allowing for constructing ADTs in const arguments while making use of generic parameters. This makes MGCA effectively a "full" prototype where it can now fully demonstrate the core concept of the feature. There's still a lot of work left to do but now we're at the point of finishing out the feature :)
Once again huge thanks to camelid for sticking with me throughout this. Also thanks to errs, oli and lcnr for reviewing some of the work and chatting with me about possible impl decisions.
Some examples of what is possible with MGCA as of the end of this goal cycle:
#![allow(unused)] #![feature(const_default, const_trait_impl, min_generic_const_args)] fn main() { trait Trait { #[type_const] const ASSOC: usize; } fn mk_array<T: const Default + Trait>() -> [T; T::ASSOC] { [const { T::default() }; _] } }
#![allow(unused)] #![feature(adt_const_params, min_generic_const_args)] fn main() { fn foo<const N: Option<u32>>() {} trait Trait { #[type_const] const ASSOC: usize; } fn bar<T: Trait, const N: u32>() { // the initializer of `_0` is a `N` which is a legal const argument // so this is ok. foo::<{ Some::<u32> { 0: N } }>(); // this is allowed as mgca supports uses of assoc consts in the // type system. ie `<T as Trait>::ASSOC` is a legal const argument foo::<{ Some::<u32> { 0: <T as Trait>::ASSOC } }>(); // this on the other hand is not allowed as `N + 1` is not a legal // const argument foo::<{ Some::<u32> { 0: N + 1 } }>(); // ERROR } }
As for adt_const_params we now have a zulip stream specifically for discussion of the upcoming RFC and the drafting of the RFC: #project-const-generics/adt_const_params-rfc. I've gotten part of the way through actually writing the RFC itself though it's gone slower than I had originally hoped as I've also been spending more time thinking through the implications of allowing private data in const generics.
I've debugged the remaining two ICEs making adt_const_params not fully ready for stabilization and written some brief instructions on how to resolve them. One ICE has been incidentally fixed (though more masked) by some work that Kivooeo has been doing on MGCA. The other has been picked up by someone I'm not sure the github handle of so that will also be getting fixed soon.
Ah I forgot to mention, even though MGCA has a tonne of work left to do I expect it should be somewhat approachable for people to help out with. So if people are interested in getting involved now is a good time :)
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
No detailed updates available.
| Progress | |
| Point of contact | |
| Champions | bootstrap (Jakub Beránek), lang (Niko Matsakis), spec (Pete LeVasseur) |
| Task owners | Pete LeVasseur, Contributors from Ferrous Systems and others TBD, |
1 detailed update available.
Meeting notes here: FLS team meeting 2025-12-12
Key developments: We're close to completing the FLS release for 1.91.0, 1.91.1. We've started to operate as a team, merging a PR with the changelog entries, then opening up issues for each change required: ✅ #624(https://github.com/rust-lang/fls/issues/624), ✅ #625(https://github.com/rust-lang/fls/issues/625), ✅ #626(https://github.com/rust-lang/fls/issues/626), ⚠️ #623(https://github.com/rust-lang/fls/issues/623). #623(https://github.com/rust-lang/fls/issues/623) is still pending, as it requires a bit of alignment with the Reference on definitions and creation of a new example. Blockers: None currently Help wanted: We'd love more folks from the safety-critical community to contribute to picking up issues or opening an issue if you notice something is missing.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
1 detailed update available.
Here's our December status update!
-
We have revised our prototype of the pre-RFC based on Ralf Jung's feedback. Now, instead of having two different retag functions for operands and places, we emit a single
__rust_retagintrinsic in every situation. We also track interior mutability precisely. At this point, the implementation is mostly stable and seems to be ready for an MCP. -
There's been some discussion here and in the pre-RFC about whether or not Rust will still have explicit MIR retag statements. We plan on revising our implementation so that we no longer rely on MIR retags to determine where to insert our lower-level retag calls. This should be a relatively straightforward change to the current prototype. If anything, it should make these changes easier to merge upstream, since they will no longer affect Miri.
-
BorrowSanitizer continues to gain new features, and we've started testing it on our first real crate (lru) (which has uncovered a few new bugs in our implementation). The two core Tree Borrows features that we have left to support are error reporting and garbage collection. Once these are finished, we will be able to expand our testing to more real-world libraries and confirm that we are passing each of Miri's test cases (and likely find more bugs lurking in our implementation). Our instrumentation pass ignores global and thread-local state for now, and it does not support atomic memory accesses outside of atomic
loadandstoreinstructions. These operations should be relatively straightforward to add once we've finished higher-priority items. -
Performance is slow. We do not know exactly how slow yet, since we've been focusing on feature support over benchmarking and optimization. This is at least partially due to the lack of garbage collection, based on what we're seeing from profiling. We will have a better sense of what our performance is like once we can compare against Miri on more real-world test cases.
As for what's next, we plan on posting an MCP soon, now that it's clear that we will be able to do without MIR retags. You can expect a more detailed status update on BorrowSanitizer by the end of January. This will discuss our implementation and plans for 2026. We will post that here and on our project website.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners | Amanieu d'Antras, Guillaume Gomez, Jack Huey, Josh Triplett, lcnr, Mara Bos, Vadim Petrochenkov, Jane Lusby |
1 detailed update available.
In addition to further ongoing work on reference material (some of which is on track to be merged), we've had some extensive discussions about reference processes, maintenance, and stability markers. Niko Matsakis is putting together a summary and proposal for next steps.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
No detailed updates available.
| Progress | |
| Point of contact | |
| Champions | compiler (Manuel Drehwald), lang (TC) |
| Task owners | Manuel Drehwald, LLVM offload/GPU contributors |
2 detailed updates available.
It's only been two weeks, but we got a good number of updates, so I already wanted to share them.
autodiff
- On the autodiff side, we landed the support for rlib and better docs. This means that our autodiff frontend is "almost" complete, since there are almost no cases left where you can't apply autodiff. There are a few features like custom-derivatives or support for
dynarguments that I'd like to add, but they are currently waiting for better docs on the Enzyme side. There is also a long-term goal off replacing the fat-lto requirement with the less invasive embed-bc requirement, but this proved to be tricky in the past and only affects compile times. - @sgasho picked up my old PR to dlopen enzyme, and found the culprit of it failing after my last rebase. A proper fix might take a bit longer, but it might be worth waiting for. As a reminder, using dlopen in the future allows us to ship autodiff on nightly without increasing the size of rustc and therefore without making our infra team sad.
All in all, we have landed most of the hard work here, so that's a very comfortable position to be in before enabling it on nightly.
offload
- We have landed the intrinsic implementation of Marcelo Domínguez, so now you can offload functions with almost arbitrary arguments. In my first prototype, I had limited it to pointers to 256 f64 values. The updated usage example continues to live here in our docs. As you can see, we still require
#[cfg(target_os=X)]annotations. Under the hood, the LLVM-IR which we generate is also still a bit convoluted. In his next PRs, he'll clean up the generated IR, and introduce an offload macro that users shall call instead of the internal offload intrinsic. - I spend more time on enabling offload in our CI, to enable
std::offloadin nightly. After multiple iterations and support from LLVM offload devs, we found a cmake config that does not run into bugs, should not increase Rust CI time too much, and works with both in-tree llvm/clang builds, as well as external clang's (the current case in our Rust CI). - I spend more time on simplifying the usage instructions in the dev guide. We started with two cargo calls, one rustc call, two clang calls, and two clang-helper binary calls. I was able to remove the rustc and one of the clang-offload-packager calls, by directly calling the underlying LLVM APIs. I also have an unmerged PR which removes the two clang calls. Once I cleaned it up and landed it, we would be down to only two cargo calls and one binary call to
clang-linker-wrapper. Once I automated this last wrapper (and enabled offload in CI), nightly users should be able to experiment withstd::offload.
Time for the next round of updates. Again, most of the updates were on the GPU side, but with some notable autodiff improvements too.
autodiff:
-
@sgasho finished his work on using dlopen to load enzyme and the pr landed. This allowed Jakub Beránek and me to start working on distributing Enzyme via a standalone component.
-
As a first step, I added a nicer error if we fail to find or dlopen our Enzyme backend. I also removed most of our autodiff fallbacks, we now unconditionally enable our macro frontend on nightly: https://github.com/rust-lang/rust/pull/150133 You may notice that
cargo expandnow works on autodiff code. This also allowed the first bug reports about ICE (internal compiler error) in our macro parser logic. -
Kobzol opened a PR to build Enzyme in CI. In theory, I should have been able to download that artifact, put it into my sysroot, and use the latest nightly to automatically load it. If that had worked, we could have just merged his PR, and everyone could have started using AD on nightly. Of course, things are never that easy. Even though both Enzyme, LLVM, and rustc were built in CI, the LLVM version shipped along with rustc does not seem compatible with the LLVM version Enzyme was built against. We assume some slight cmake mismatch during our CI builds, which we will have to debug.
offload:
-
On the gpu side, Marcelo Domínguez finished his cleanup PR, and along the way also fixed using multiple kernels within a single codebase. When developing the offload MVP I had taken a lot of inspiration from the LLVM-IR generated by clang - and it looks like I had gotten one of the (way too many) LLVM attributes wrong. That caused some metadata to be fused when multiple kernels are present, confusing our offload backend. We started to find more bugs when working on benchmarks, more about the fixes for those in the next update.
-
I finished cleaning up my offload build PR, and Oliver Scherer reviewed and approved it. Once the dev-guide gets synced, you should see much simpler usage instructions. Now it's just up to me to automate the last part, then you can compile offload code purely with cargo or rustc. I also improved how we build offload, which allows us to build it both in CI and locally. CI had some very specific requirements to not increase build times, since our x86-64-dist runner is already quite slow.
-
Our first benchmarks directly linked against NVIDIA and AMD intrinsics on llvm-ir level. However, we already had an nvptx Rust module for a while, and since recently also an amdgpu module which nicely wraps those intrinsics. I just synced the stdarch repository into rustc a few minutes ago, so from now on, we can replace both with the corresponding Rust functions. In the near future we should get a higher level GPU module, which abstracts away naming differences between vendors.
-
Most of my past rustc contributions were related to LLVM projects or plugins (Offload and Enzyme), and I increasingly encountered myself asking other people for updates or backports of our LLVM submodule, since upstream LLVM has fixes which were not yet merged into our LLVM submodule. Our llvm working group is quite small and I didn't want to burden them too much with my requests, so I recently asked them to join it, which also got approved. In the future I intend to help a little with the maintenance here.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners | (depending on the flag) |
1 detailed update available.
Update from the 2025-12-03 meeting:
-Zharden-sls
Wesley reviewed it again, provided a qualification, more changes requested.
| Progress | |
| Point of contact | |
| Champions | lang (Josh Triplett), lang-docs (TC) |
| Task owners |
2 detailed updates available.
Update from the 2025-12-03 meeting.
Deref / Receiver
Ding keeps working on the Reference draft. The idea is still not well-proliferated and people are not convinced this is a good way to go. We hope the method-probing section in Reference PR could clear thins up.
We're keeping the supertrait auto-impl experiment as an alternative.
RFC #3851: Supertrait Auto-impl
Ding addressed Predrag's requests on SemVer compatibility. He's also opened an implementation PR: https://github.com/rust-lang/rust/pull/149335. Here's the tracking issue: https://github.com/rust-lang/rust/issues/149556.
derive(CoercePointee)
Ding opened a PR to require additional checks for DispatchFromDyn: https://github.com/rust-lang/rust/pull/149068
In-place initialization
Ding will prepare material for a discussion at the LPC (Linux Plumbers Conference). We're looking to hear feedback on the end-user syntax for it.
The feature is going quite large, Ding will check with Tyler on the whether this might need a series of RFCs.
The various proposals on the table continue being discussed and there are signs (albeit slow) of convergence. The placing function and guaranteed return ones are superseded by outpointer. The more ergonomic ideas can be built on top. The guaranteed value placement one would be valuable in the compiler regardless and we're waiting for Olivier to refine it.
The feeling is that we've now clarified the constraints that the proposals must operate under.
Field projections
Nadri's Custom places proposal is looking good at least for the user-facing bits, but the whole thing is growing into a large undertaking. Benno's been focused on academic work that's getting wrapped up soon. The two will sync afterwards.
Quick bit of great news: Rust in the Linux kernel is no longer treated as an experiment, it's here to stay 🎉
https://lwn.net/SubscriberLink/1050174/63aa7da43214c3ce/
| Progress | |
| Point of contact | |
| Champions | cargo (Ed Page), compiler (b-naber), crates-io (Carol Nichols) |
| Task owners |
3 detailed updates available.
Ed Page hey i would like to contribute to this I reached out on zulip. Bumping up the post in case it might have gone under the radar
Hi @sladyn98 - feel free to ping me on Zulip about this.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
1 detailed update available.
The RFC draft was reviewed in detail and Ralf Jung pointed out that the proposed semantics introduce issues because they rely on "no-behavior" (NB) with regards to choosing an address for a local. This can lead to surprising "time-traveling" behavior where the set of possible addresses that a local may have (and whether 2 locals can have the same address) depends on information from the future. For example:
#![allow(unused)] fn main() { // This program has DB let x = String::new(); let xaddr = &raw const x; let y = x; // Move out of x and de-initialize it. let yaddr = &raw const y; x = String::new(); // assuming this does not change the address of x // x and y are both live here. Therefore, they can't have the same address. assume(xaddr != yaddr); drop(x); drop(y); }
#![allow(unused)] fn main() { // This program has UB let x = String::new(); let xaddr = &raw const x; let y = x; // Move out of x and de-initialize it. let yaddr = &raw const y; // So far, there has been no constraint that would force the addresses to be different. // Therefore we can demonically choose them to be the same. Therefore, this is UB. assume(xaddr != yaddr); // If the addresses are the same, this next line triggers NB. But actually this next // line is unreachable in that case because we already got UB above... x = String::new(); // x and y are both live here. drop(x); drop(y); }
With that said, there is still a possibility of achieving the optimization, but the scope will need to be scaled down a bit. Specifically, we would need to:
- no longer perform a "partial free"/"partial allocation" when initializing or moving out of a single field of a struct. The lifetime of a local starts when any part of it is initialized and ends when it is fully moved out.
- allow a local's address to change when it is re-initialized after having been fully moved out, which eliminates the need for NB.
This reduces the optimization opportunities since we can't merge arbitrary sub-field moves, but it still allows for eliminating moves when constructing a struct from multiple values.
The next step is for me to rework the RFC draft to reflect this.
| Progress | |
| Point of contact | |
| Task owners |
|
No detailed updates available.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
2 detailed updates available.
Key developments: HTML replay logic has merge. Once it gets into nightly cargo report timings can open the timing report you have previously logged.
- https://github.com/rust-lang/cargo/pull/16377
- https://github.com/rust-lang/cargo/pull/16378
- https://github.com/rust-lang/cargo/pull/16382
Blockers: No, except my own availability
Help wanted: Same as https://github.com/rust-lang/rust-project-goals/issues/398#issuecomment-3571897575
Key developments:
Headline: You should always enable build analysis locally, if you are using nightly and want the timing info data always available.
[unstable]
build-analysis = true
[build.analysis]
enabled = true
- More log events are emitted: https://github.com/rust-lang/cargo/pull/16390
- dependency resolution time
- unit-graph construction
- unit-registration (which contain unit metadata)
- Timing replay from
cargo report timingsnow has almost the same feature parity ascargo build --timings, except CPU usage: https://github.com/rust-lang/cargo/pull/16414 - Rename
rebuildevent tounit-fingerprint, and is emitted also for fresh unit: https://github.com/rust-lang/cargo/pull/16408. - Proposed a new
cargo report sessionscommand so that people can retrieve previous sessions IDs not use the latest one: https://github.com/rust-lang/cargo/pull/16428 - Proposed to remove
--timings=jsonwhich timing info in log files should be a great replacement: https://github.com/rust-lang/cargo/pull/16420 - Documenting efforts for having man pages for nested commands `cargo report : https://github.com/rust-lang/cargo/pull/16430 and https://github.com/rust-lang/cargo/pull/16432
Besides implementations, we also discussed about:
- The interaction of
--message-formatand structured logging system, as well as log event schemas and formats: https://rust-lang.zulipchat.com/#narrow/channel/246057-t-cargo/topic/build.20analysis.20log.20format/with/558294271 - A better name for
RunId. We may lean towardsSessionIdwhich is a common name for logging/tracing ecosystem. - Nested Cargo calls to have a sticky session ID. At least a way to show they were invoked from the same top-level Cargo call.
Blockers: No, except my own availability
Help wanted: Same as https://github.com/rust-lang/rust-project-goals/issues/398#issuecomment-3571897575
| Progress | |
| Point of contact | |
| Champions | compiler (Oliver Scherer), lang (Scott McMurray), libs (Josh Triplett) |
| Task owners | oli-obk |
1 detailed update available.
Updates
- https://github.com/rust-lang/rust/pull/148820 adds a way to mark functions and intrinsics as only callable during CTFE
- https://github.com/rust-lang/rust/pull/144363 has been unblocked and just needs some minor cosmetic work
Blockers
- https://github.com/rust-lang/rust/pull/146923 (reflection MVP) has not been reviewed yet
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
1 detailed update available.
Status update December 23, 2025
The majority of December was spent iterating on https://github.com/rust-lang/cargo/pull/16155 . As mentioned in the previous update, the original locking design was not correct and we have been working through other solutions.
As locking is tricky to get right and there are many scenarios Cargo needs to support, we are trying to descope the initial implementation to an MVP, even if that means we lose some of the concurrency. Once we have an MVP on nightly, we can start gathering feedback on the scenarios that need improvement and iterate.
I'm hopeful that we get an unstable -Zfine-grain-locking on nightly in January for folks to try out in their workflows.
Also we are considering adding an opt-in for the new build-dir layout using an env var (CARGO_BUILD_DIR_LAYOUT_V2=true) to allow tool authors to begin migrating to the new layout. https://github.com/rust-lang/cargo/pull/16336
Before stabilizing this, we are doing crater run to test the impact of the changes and proactively reaching out to projects to minimize breakage as much as possible. https://github.com/rust-lang/rust/pull/149852
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
No detailed updates available.
| Progress | |
| Point of contact | |
| Task owners | [Bastian Kersting](https://github.com/1c3t3a), [Jakob Koschel](https://github.com/jakos-sec) |
1 detailed update available.
Based on the gathered feedback I opened a new MCP for the proposed new Tier 2 targets with sanitizers enabled. (https://github.com/rust-lang/compiler-team/issues/951)
| Progress | |
| Point of contact | |
| Task owners | vision team |
No detailed updates available.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
1 detailed update available.
We have enabled the second x64 machine, so we now have benchmarks running in parallel 🎉 There are some smaller things to improve, but next year we can move onto running benchmarks on Arm collectors.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
|
No detailed updates available.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
1 detailed update available.
Opened stabilization PR but we have blockers I didn't hear of, so stabilization will be postponed until then.
| Progress | |
| Point of contact | |
| Champions | compiler (David Wood), lang (Niko Matsakis), libs (Amanieu d'Antras) |
| Task owners |
3 detailed updates available.
I haven't made any progress on Deref::Target yet, but I have been focusing on landing rust-lang/rust#143924 which has went through two rounds of review and will hopefully be approved soon.
Update: David and I chatted on Zulip. Key points:
David has made "progress on the non-Sized Hierarchy part of the goal, the infrastructure for defining scalable vector types has been merged (with them being Sized in the interim) and that'll make it easier to iterate on those and find issues that need solving".
On the Sized hierarchy part of the goal, no progress. We discussed options for migrating. There seem to be three big options:
(A) The conservative-but-obvious route where the T: Derefin the old edition is expanded to T: Deref<Target: SizeOfVal> (but in the new edition it means T: Deref<Target: Pointee>, i.e., no additional bounds). The main downside is that new Edition code using T: Deref can't call old Edition code using T: Deref as the old edition code has stronger bounds. Therefore new edition code must either use stronger bounds than it needs or wait until that old edition code has been updated.
(B) You do something smart with Edition.Old code where you figure out if the bound can be loose or strict by bottom-up computation. So T: Deref in the old could mean either T: Deref<Target: Pointee> or T: Deref<Target: SizeOfVal>, depending on what the function actually does.
(C) You make Edition.Old code always mean T: Deref<Target: Pointee> and you still allow calls to size_of_val but have them cause post-monomorphization errors if used inappropriately. In Edition.New you use stricter checking.
Options (B) and (C) have the downside that changes to the function body (adding a call to size_of_val, specifically) in the old edition can stop callers from compiling. In the case of Option (B), that breakage is at type-check time, because it can change the where-clauses. In Option (C), the breakage is post-monomorphization.
Option (A) has the disadvantage that it takes longer for the new bounds to roll out.
Given this, (A) seems the preferred path. We discussed options for how to encourage that roll-out. We discussed the idea of a lint that would warn Edition.Old code that its bounds are stronger than needed and suggest rewriting to T: Deref<Target: Pointee> to explicitly disable the stronger Edition.Old default. This lint could be implemented in one of two ways
- at type-check time, by tracking what parts of the environment are used by the trait solver. This may be feasible in the new trait solver, someone from @rust-lang/types would have to say.
- at post-mono time, by tracking which functions actually call
size_of_valand propagating that information back to callers. You could then compare against the generic bounds declared on the caller.
The former is more useful (knowing what parts of the environment are necessary could be useful for more things, e.g., better caching); the latter may be easier or more precise.
Update to the previous post.
Tyler Mandry pointed me at this thread, where lcnr posted this nice blog post that he wrote detailing more about (C).
Key insights:
- Because the use of
size_of_valwould still cause post-mono errors when invoked on types that are notSizeOfVal, you know that addingSizeOfValinto the function's where-clause bounds is not a breaking change, even though adding a where clause is a breaking change more generally. - But, to David Wood's point, it does mean that there is a change to Rust's semver rules: adding
size_of_valwould become a breaking change, where it is not today.
This may well be the best option though, particularly as it allows us to make changes to the defaults across-the-board. A change to Rust's semver rules is not a breaking change in the usual sense. It is a notable shift.
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |
1 detailed update available.
This month I've written some documentation for how Const Generics is implemented in the compiler. This mostly covers the implementation of the stable functionality as the unstable features are quite in flux right now. These docs can be found here: https://rustc-dev-guide.rust-lang.org/const-generics.html
| Progress | |
| Point of contact | |
| Champions | |
| Task owners |