The redesign of Firefox Settings is now enabled by default in Nightly! With this change, we are making it easier for people to customize Firefox and discover the controls that matter most to them. The redesign improves navigation and organization, updates labels and descriptions, and uses a new underlying architecture that will make Settings easier to update over time.
Firefox Settings have grown a lot over the years. Our research showed that some areas had become crowded and more difficult to navigate, and that important controls could be hard to discover. Some terminology and layouts had also become inconsistent over time. Internally, the existing structure also made Settings harder to maintain and evolve over time.
Here are some of the changes you'll notice:
More pages within Settings, with less content per page
Clearer grouping and better flow between related controls
Improved labels and helper text
Consistent use of our design system throughout
The new Settings navigation and layout
While the organization of Settings has changed, all your existing choices and preferences remain in place. You can always use the search bar to jump straight to what you're looking for.
We plan to ship the redesign to the Release channel in Firefox 152. You will continue to see changes in Nightly, and we welcome feedback, especially in Mozilla Connect.
Customization has long been a core part of Firefox, and this redesign is intended to continue improving how people discover and manage their browser settings. We appreciate all of the feedback from the community so far on this project and look forward to refining the experience with your input!
Axe-time, sword-time, shields are sundered,
Wind-time, wolf-time, ere the world falls.
- Völuspá, Poetic Edda
As of Firefox 148, SpiderMonkey's asm.js optimizations are disabled by default, and we plan to remove the code entirely in a future release.
If you maintain a site that uses asm.js, nothing will break. asm.js is just a subset of plain JavaScript, so the code keeps running through our regular JIT just like any other script. That said, recompiling to WebAssembly will get you faster execution and smaller binaries.
History
asm.js was Mozilla's response to the question posed by NaCl and PNaCl: how can the web run code at native speeds?
The idea was clever: pick a strict, statically-typed subset of JavaScript that an engine could recognize on the fly and compile down to native code. We could get performance similar to NaCl/PNaCl and still have code live inside web content and use web API's (no separate sandbox, IPC, or alternative API's).
asm.js shipped in Firefox 22 back in 2013 and was a success. It let projects like Unity and Unreal ship C/C++ codebases to the web for the first time, using just standard web technologies. The Epic Citadel demo was ported to the web in just four days. It was a landmark achievement, and a fond memory for the original asm.js team.
asm.js proved that we could run code at near-native speed on the web using just web technologies. This opened the door to WebAssembly, which shipped several years later in Firefox 52. Without asm.js, we likely wouldn't have WebAssembly.
Why now?
So why turn it off? WebAssembly has succeeded, and asm.js usage has mostly migrated over. Keeping the asm.js path alongside WebAssembly costs us maintenance time and gives us extra attack surface in the VM.
If you are shipping asm.js content, please consider recompiling to WebAssembly! Our WebAssembly pipeline is significantly more advanced than the asm.js one ever was. You should see faster execution and smaller binaries.
Ragnarök
The asm.js compiler is called OdinMonkey. As was foretold long ago, OdinMonkey must meet his fated doom. The bug Ragnarök tracks the "Twilight of OdinMonkey".
All is not lost however, for born of OdinMonkey is BaldrMonkey, our WebAssembly optimizing compiler. OdinMonkey may be swallowed whole by the wolf, Fenrir, but BaldrMonkey will rule over the reborn world alongside RabaldrMonkey ("commotion"), our WebAssembly baseline compiler.
On this Odin's day (Wednesday) we thank OdinMonkey for thirteen years of service. Skål!
Then fields unsowed bear ripened fruit,
all ills grow better, and Baldr comes back;
Baldr and Hoth dwell in Hropt's battle-hall. - Völuspá, Poetic Edda
The Firefox for Android app has always had a complicated build process - we're cramping a complex cross-platform browser engine and all the related components that make it work on Android into one package. In its current form, it lives in the Firefox mono-repo at mozilla-central (now mozilla-firefox using the git repository).
I wanted to document my "artifact-mode" environment here since it's worked quite successfully for me for many years with minor changes.
NOTE: After a fresh clone of the mono-repo, don't forget to first run and follow the prompts of ./mach bootstrap .
mozconfig
My mozconfig below is enabled for artifact mode, but occasionally I switch between various configurations. You can see those commented out, with these few extra notes:
I like to separate out my objdirs to avoid cache pollution between the different build types. I think you can get away without needing to specify this and an objdir for your build type and arch will be generated.
sccache speeds up the native portion of full builds after the first slow one, but it's a hit or miss if you fetch from the remote repository but don't need to rebuild as often.
I don't care to manually run the clobber step, and I don't truly appreciate why that isn't always automatically done.
# Build GeckoView/Firefox for Android:
ac_add_options --enable-application=mobile/android
# Targeting the following architecture.
# For regular phones, no --target is needed.
# For x86 emulators (and x86 devices, which are uncommon):
# ac_add_options --target=i686
# For newer phones or Apple silicon
ac_add_options --target=aarch64
# For x86_64 emulators (and x86_64 devices, which are even less common):
# ac_add_options --target=x86_64
# sccache will significantly speed up your builds by caching
# compilation results. The Firefox build system will download
# sccache automatically.
# This only works for non-artifact builds.
#ac_add_options --with-ccache=sccache
# Enable artifact builds; manager-mode.
ac_add_options --enable-artifact-builds
# Write build artifacts to..
## Full build dir
#mk_add_options MOZ_OBJDIR=./objdir-droid
#mk_add_options MOZ_OBJDIR=./objdir-desktop
## Artifact builds
mk_add_options MOZ_OBJDIR=./objdir-frontend
# Automatic clobbering; don't ask me.
mk_add_options AUTOCLOBBER=1
JAVA_HOME
Sometimes you might find yourself needing to run a (non-mach) command in the terminal. Those typically will need to invoke some parts of gradle for an Android build, so it's best to make sure those are using the same JDK as the bootstrapped one in the mono-repo. This avoids weird build errors where something that compiles in one place isn't working in another (like Android Studio).
The location for the JDKs are typically in ~/.mozbuild/jdk/, and if you've between around for ~6 months you end up with multiple versions after every JDK bump:
$ ls -l ~/.mozbuild/jdk/
drwxr-xr-x@ - jalmeida 15 Apr 2025 jdk-17.0.15+6
drwxr-xr-x@ - jalmeida 15 Jul 2025 jdk-17.0.16+8
drwxr-xr-x@ - jalmeida 21 Oct 2025 jdk-17.0.17+10
drwxr-xr-x@ - jalmeida 20 Jan 09:00 jdk-17.0.18+8
drwxr-xr-x@ - jalmeida 26 Feb 15:04 mozboot
You can find some way to point your latest JDK to one location or you can be lazy like me and pick the latest version to assign as your JAVA_HOME property by adding this to your shell's RC file:
export JAVA_HOME="$(ls -1dr -- $HOME/.mozbuild/jdk/jdk-* | head -n 1)/Contents/Home"
Android Studio
UPDATE: With D286123 landed, this should no longer be necessary! 🎉
Similarly for Android Studio, let's do the same so that environment is identical. Head to, Settings | Build, Execution, Deployment | Build Tools | Gradle, and ensure that "Gradle JDK" path is set to JAVA_HOME.
Lately, the default seems to be for it to follow GRADLE_LOCAL_JAVA_HOME which is a property we can't easily override, so we have to manually set this ourselves.
Using the same Android SDK also helps speed things up and avoids source confusion. You can typically find it in ~/.mozbuild/android-sdk-macosx and update it at Settings | Languages & Frameworks | Android SDK.
Debugging
This section is for miscellaneous build error situations that come-up, but assuming mach build work and there are no known Android build changes, my solution has typically always been the same.
For example, the other day I fetched another engineers patch to test out locally1 as part of reviewing it where I faced the error message below:
Execution failed for task ':components:feature-pwa:compileDebugKotlin'.
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':components:feature-pwa:compileDebugKotlin'.
> A failure occurred while executing org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction
> Internal compiler error. See log for more details
* Try:
> Run with --info or --debug option to get more log output.
> Run with --scan to generate a Build Scan (powered by Develocity).
> Get more help at https://help.gradle.org.
* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':components:feature-pwa:compileDebugKotlin'.
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:135)
at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:288)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:133)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:121)
at org.gradle.api.internal.tasks.execution.ProblemsTaskPathTrackingTaskExecuter.execute(ProblemsTaskPathTrackingTaskExecuter.java:41)
at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:74)
at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
at org.gradle.execution.plan.DefaultNodeExecutor.executeLocalTaskNode(DefaultNodeExecutor.java:55)
at org.gradle.execution.plan.DefaultNodeExecutor.execute(DefaultNodeExecutor.java:34)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:355)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:343)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.lambda$execute$0(DefaultTaskExecutionGraph.java:339)
at org.gradle.internal.operations.CurrentBuildOperationRef.with(CurrentBuildOperationRef.java:84)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:339)
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:328)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:459)
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:376)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
Caused by: org.gradle.workers.internal.DefaultWorkerExecutor$WorkExecutionException: A failure occurred while executing org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction
at org.gradle.workers.internal.DefaultWorkerExecutor$WorkItemExecution.waitForCompletion(DefaultWorkerExecutor.java:289)
at org.gradle.internal.work.DefaultAsyncWorkTracker.lambda$waitForItemsAndGatherFailures$2(DefaultAsyncWorkTracker.java:130)
at org.gradle.internal.Factories$1.create(Factories.java:33)
at org.gradle.internal.work.DefaultWorkerLeaseService.lambda$withoutLocks$2(DefaultWorkerLeaseService.java:344)
at org.gradle.internal.work.ResourceLockStatistics$1.measure(ResourceLockStatistics.java:42)
at org.gradle.internal.work.DefaultWorkerLeaseService.withoutLocks(DefaultWorkerLeaseService.java:342)
at org.gradle.internal.work.DefaultWorkerLeaseService.withoutLocks(DefaultWorkerLeaseService.java:326)
at org.gradle.internal.work.DefaultWorkerLeaseService.withoutLock(DefaultWorkerLeaseService.java:331)
at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForItemsAndGatherFailures(DefaultAsyncWorkTracker.java:126)
at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForItemsAndGatherFailures(DefaultAsyncWorkTracker.java:92)
at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForAll(DefaultAsyncWorkTracker.java:78)
at org.gradle.internal.work.DefaultAsyncWorkTracker.waitForCompletion(DefaultAsyncWorkTracker.java:66)
at org.gradle.api.internal.tasks.execution.TaskExecution$3.run(TaskExecution.java:260)
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:47)
at org.gradle.api.internal.tasks.execution.TaskExecution.executeAction(TaskExecution.java:237)
at org.gradle.api.internal.tasks.execution.TaskExecution.executeActions(TaskExecution.java:220)
at org.gradle.api.internal.tasks.execution.TaskExecution.executeWithPreviousOutputFiles(TaskExecution.java:203)
at org.gradle.api.internal.tasks.execution.TaskExecution.execute(TaskExecution.java:170)
at org.gradle.internal.execution.steps.ExecuteStep.executeInternal(ExecuteStep.java:105)
at org.gradle.internal.execution.steps.ExecuteStep.access$000(ExecuteStep.java:44)
at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:59)
at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:56)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:56)
at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:44)
at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:42)
at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:75)
at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:55)
at org.gradle.internal.execution.steps.PreCreateOutputParentsStep.execute(PreCreateOutputParentsStep.java:50)
at org.gradle.internal.execution.steps.PreCreateOutputParentsStep.execute(PreCreateOutputParentsStep.java:28)
at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:68)
at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:38)
at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:61)
at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:26)
at org.gradle.internal.execution.steps.CaptureOutputsAfterExecutionStep.execute(CaptureOutputsAfterExecutionStep.java:69)
at org.gradle.internal.execution.steps.CaptureOutputsAfterExecutionStep.execute(CaptureOutputsAfterExecutionStep.java:46)
at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:39)
at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:28)
at org.gradle.internal.execution.steps.BuildCacheStep.executeWithoutCache(BuildCacheStep.java:189)
at org.gradle.internal.execution.steps.BuildCacheStep.lambda$execute$1(BuildCacheStep.java:75)
at org.gradle.internal.Either$Right.fold(Either.java:176)
at org.gradle.internal.execution.caching.CachingState.fold(CachingState.java:62)
at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:73)
at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:48)
at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:46)
at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:35)
at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:75)
at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$2(SkipUpToDateStep.java:53)
at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:53)
at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:35)
at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:37)
at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:27)
at org.gradle.internal.execution.steps.ResolveIncrementalCachingStateStep.executeDelegate(ResolveIncrementalCachingStateStep.java:49)
at org.gradle.internal.execution.steps.ResolveIncrementalCachingStateStep.executeDelegate(ResolveIncrementalCachingStateStep.java:27)
at org.gradle.internal.execution.steps.AbstractResolveCachingStateStep.execute(AbstractResolveCachingStateStep.java:71)
at org.gradle.internal.execution.steps.AbstractResolveCachingStateStep.execute(AbstractResolveCachingStateStep.java:39)
at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:64)
at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:35)
at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:62)
at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:40)
at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.execute(AbstractCaptureStateBeforeExecutionStep.java:76)
at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.execute(AbstractCaptureStateBeforeExecutionStep.java:45)
at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.executeWithNonEmptySources(AbstractSkipEmptyWorkStep.java:136)
at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.execute(AbstractSkipEmptyWorkStep.java:66)
at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.execute(AbstractSkipEmptyWorkStep.java:38)
at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:38)
at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:36)
at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:23)
at org.gradle.internal.execution.steps.HandleStaleOutputsStep.execute(HandleStaleOutputsStep.java:75)
at org.gradle.internal.execution.steps.HandleStaleOutputsStep.execute(HandleStaleOutputsStep.java:41)
at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.lambda$execute$0(AssignMutableWorkspaceStep.java:35)
at org.gradle.api.internal.tasks.execution.TaskExecution$4.withWorkspace(TaskExecution.java:297)
at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.execute(AssignMutableWorkspaceStep.java:31)
at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.execute(AssignMutableWorkspaceStep.java:22)
at org.gradle.internal.execution.steps.ChoosePipelineStep.execute(ChoosePipelineStep.java:40)
at org.gradle.internal.execution.steps.ChoosePipelineStep.execute(ChoosePipelineStep.java:23)
at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.lambda$execute$2(ExecuteWorkBuildOperationFiringStep.java:67)
at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.execute(ExecuteWorkBuildOperationFiringStep.java:67)
at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.execute(ExecuteWorkBuildOperationFiringStep.java:39)
at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:46)
at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:34)
at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:44)
at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:31)
at org.gradle.internal.execution.impl.DefaultExecutionEngine$1.execute(DefaultExecutionEngine.java:64)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:132)
... 30 more
Caused by: org.jetbrains.kotlin.gradle.tasks.FailedCompilationException: Internal compiler error. See log for more details
at org.jetbrains.kotlin.gradle.tasks.TasksUtilsKt.throwExceptionIfCompilationFailed(tasksUtils.kt:22)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:112)
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:75)
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:68)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:64)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:61)
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:61)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:58)
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:176)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
at org.gradle.internal.Factories$1.create(Factories.java:33)
at org.gradle.internal.work.DefaultWorkerLeaseService.lambda$withLocksAcquired$0(DefaultWorkerLeaseService.java:269)
at org.gradle.internal.work.ResourceLockStatistics$1.measure(ResourceLockStatistics.java:42)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocksAcquired(DefaultWorkerLeaseService.java:267)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:259)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
... 2 more
The full trace was long and didn't seem related to a code failure in the module itself. So I employed the solution, which is always the same:
./mach build
In Android Studio, File > Sync Project with Gradle Files.
Yup, that's all. Very simple and boring.
1
With Jujutsu, this is the moz-phab command I use which has made it easier to manage review patches: moz-phab patch <patch-id> --no-branch --apply-to main@origin
Comments
With an account on the Fediverse or Mastodon, you can respond to this post. Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one. Known non-private replies are displayed below.
Learn how this was implemented from the original source here.
<noscript><p>Loading comments relies on JavaScript. Try enabling JavaScript and reloading, or visit <a href="https://mindly.social/@jonalmeida/116197244320129422">the original post</a> on Mastodon.</p></noscript>
<noscript>You need JavaScript to view the comments.</noscript> &>"'
Update on May 19, 2026: Firefox's free built-in VPN now supports location selection, giving people access to a fully comprehensive VPN experience directly within the browser. Starting today, Firefox users in the U.S., U.K., France, Germany, and Canada can choose to browse from any of the countries where we've launched VPN support. Browsing is subject to the local laws and content restrictions of the selected region.
Original post from March 2026:
Today we're introducing a free built-in VPN in Firefox, a new IP-protection feature designed to keep you even more private while you browse. We're starting by offering an industry-leading 50 gigabytes of free VPN-browsing each month.
Firefox has long focused on building privacy tools directly into the browser to protect you online. Over the years, we've introduced world-class protections that block known trackers, reduce fingerprinting and limit how companies can follow people across the web. Our goal has been consistent: make meaningful privacy protections accessible to Firefox users every day.
Firefox is the only major browser to include a built-in VPN like this for free - giving you more control over your privacy, right where you browse.
Privacy built into the browser
Every time you visit a website, your IP address is shared automatically. IP addresses help websites know where to send information back to your device, but they can also be used to approximate your location, link your browsing activity across sites and keep logs about your online behavior, meaning websites can track your behavior. It's one of many ways companies track activity across the internet.
Additionally, when you're using public Wi-Fi while at a coffee shop, in a hotel, or in your dorm, people can spy on your network traffic and see which websites you might be visiting.
At Mozilla, we believe people should have stronger protections against this kind of tracking and spying, and that those protections should be easy to use.
Introducing built-in VPN
Our free built-in VPN is designed to make IP protection simple to use in Firefox.
The built-in VPN includes an unprecedented 50 GB per month of free VPN browsing, enough to cover everyday activities like shopping, banking, and reading.
Turn it on in Firefox with a single click. No extra apps. No downloads. Once it's on, Firefox routes your browsing traffic through a proxy network that replaces your IP address before it reaches a website. The sites you visit see the proxy's IP address rather than your own. Firefox already encrypts your traffic with HTTPS, but masking your IP adds another layer of privacy. You can mask the URLs you're visiting from anyone trying to spy on your network traffic on public Wi-Fi, like while you're enjoying a latte at your favorite coffee shop.
If you reach the monthly limit, IP protection is paused until the next cycle. Firefox will require you to confirm before proceeding without the VPN so your browsing doesn't unintentionally continue without IP protection.
Browser-level protection and full-device protection
The free built-in VPN helps secure your traffic while browsing in Firefox, making it a simple way to protect your IP address from being tracked by big tech. However, it does not offer full device protection.
For those looking for broader coverage, you can also choose protection that extends across your entire device, including other apps. The standalone Mozilla VPN subscription offers this capability with unlimited data across multiple devices. Depending on your needs, you can pick the level of privacy and protection that suits you.
We've heard concerns about so-called "free VPNs," which often rely on advertising or selling user data to generate revenue. Firefox's built-in VPN is designed differently. It does not sell your browsing data and does not inject advertising into your traffic. Instead, we offer a limited amount of browser-level protection for free, alongside Mozilla VPN, our paid, unlimited, full-device VPN service.
The free built-in VPN is currently rolling out as a beta to Firefox desktop users in the United States, the United Kingdom, Germany and France, with plans to expand to additional countries coming soon over the next several releases.
As with many Firefox features, we're introducing it gradually starting in Firefox 149 so we can learn from user feedback and continue improving the experience.
Building a more private web
Protecting privacy online is an ongoing effort. As the web evolves, new technologies create both opportunities and challenges for keeping personal information safe.
Mozilla has spent years building privacy protections - from Total Cookie Protection to Private browsing mode to anti-fingerprinting - directly into Firefox so people have more control over how they experience the web. This built-in VPN is one more way Firefox helps you browse with less exposure and more peace of mind.
By continuing to build these protections into Firefox, we aim to make the web safer, more transparent and more respectful of the people who use it.
Today, Firefox is rolling out updates across desktop and mobile that give you more choice over how you browse.
Here's a look at what's new.
Adding location selection to Firefox's free VPN
Firefox now offers a fully featured VPN experience directly in the browser - for free. In just two months, over 1 million users have already signed up to try it.
With the addition of location selection, one of the most requested features from the Firefox community, Firefox's free built-in VPN now delivers all the flexibility and functionality people expect from a modern VPN.
Starting today, Firefox users in the U.S., U.K., France, Germany, and Canada can choose to browse from any of the countries where we've launched VPN support. Whether you want to view local news, shop regionally, or see how content appears in another country, location selection gives you more control over where your connection appears from online.
Firefox will recommend the location closest to you by default, but people can switch locations at any time. Additional locations are planned for future releases. Browsing will still be subject to the local laws and content restrictions of the selected region.
Available in Firefox 151.
More controls over AI features on mobile
You can now decide how AI shows up in your Firefox mobile browsing experience, with more control over which AI features to activate and more ways to use them across devices and languages.
After launching AI controls on desktop earlier this year, Firefox is bringing them to iOS and Android. Depending on your device and region, available controls include features like translations, voice search and Shake to Summarize. Preferences can be updated at any time, making it easy to keep only the tools you want to use as new AI-powered features arrive in Firefox.
Shake to Summarize, recognized by TIME as one of the Best Inventions of 2025, helps you cut to the chase on mobile webpages by generating a simple summary with a flick of your wrist. The feature is now expanding to Android devices, with English rolling out first and additional languages coming soon. Shake to Summarize is now available on iOS in English, German, French, Spanish, Portuguese, Italian, and Japanese, giving more people the option to use it in the language that works best for them.
AI controls are available on iOS and Android. Shake to Summarize is available on Android in English and on iOS in English, German, French, Spanish, Portuguese, Italian, and Japanese.
A one-click reset for Private Browsing
Finished shopping for a surprise gift or logging into a temporary account? A new flame-shaped "Clear Private Session" button, located to the right of the address bar, instantly clears your current private session and starts a new one without requiring you to close and reopen the browser. Cookies, browsing history, logins, and other session data from your private windows are deleted automatically. Read more in this week's release notes.
Try it out in Firefox 151.
Coming soon in Nightly: A refreshed settings page
Firefox is previewing a redesigned settings page in Nightly that makes it easier to find the controls you're looking for and move through settings more intuitively. The redesign introduces improved navigation, clearer organization, and updated labels and descriptions designed to make customization easier.
Existing settings and preferences will remain unchanged, and the updated search experience helps people quickly find settings that may appear in new sections.
This will be enabled by default for Nightly users starting later this week.
Download the latest version of Firefox to explore what's new and share feedback with us on Mozilla Connect.
Mobile browsing is personal. It's the link you open from a group chat because someone said, "Wait, is this real?" It's the article you read in the few quiet minutes you have to yourself. It's the review you skim before buying something you've been thinking about all week.
On a phone, browsing follows you through the day, from the first thing you look up in the morning to the last tab you close at night. So when AI features are part of the experience, you should have a clear way to choose what helps and what stays off.
That's why we're bringing AI controls - a popular desktop feature on Firefox - to iOS and Android users. AI controls let you turn off Firefox's AI features entirely, enable only the ones you want or adjust your choices later. The feature rolls out on mobile on May 19.
AI features you can choose on Firefox mobile
What you see may vary by device and location, but AI controls in Firefox mobile let you turn on or off features, including website translations and voice search on Android, and translations and Shake to Summarize on iOS, where available.
As Firefox adds new features, you can come back to your settings and choose what works for you.
Mobile browsing on your terms
As AI becomes a bigger part of the web, the question isn't just what it can do. It's how much control people have over it.
In Firefox mobile, AI can help make browsing easier, from finding what you need to understanding pages faster. But Firefox isn't just adding AI features. We're adding the controls, too.
Decide how AI fits into your experience. Try the features you want. Turn off the ones you don't. Keep browsing on your terms.
WebDriver is a remote control interface that enables introspection and control of user agents. As such, it can help developers to verify that their websites are working and performing well with all major browsers. The protocol is standardized by the W3C and consists of two separate specifications: WebDriver classic (HTTP) and the new WebDriver BiDi (Bi-Directional).
This newsletter gives an overview of the work we've done as part of the Firefox 151 release cycle.
Contributions
Firefox is an open source project, and we are always happy to receive external code contributions to our WebDriver implementation. We want to give special thanks to everyone who filed issues, bugs and submitted patches.
In Firefox 151, Armin Ulrich contributed a fix to WebDriver BiDi:
Implemented the browser.setClientWindowState command. This command allows clients to change the OS-level window state of a browser window, such as maximized, minimized, fullscreen, or normal. It also allows repositioning and resizing the window.
Don't let bloated websites slow you down. When you just need the gist, scrolling through ads and filler content can turn a quick check into an endless scroll.
Firefox's Shake to Summarize feature solves that. We first launched it on iOS in English last September, earning a special mention in TIME's Best Inventions of 2025 and a strong response from users. Since then, we've expanded it to iOS users in German, French, Spanish, Portuguese, Italian and Japanese. Starting today, we're bringing it to Android users in English, with more languages coming soon.
Get the gist in seconds
On any web page under 5,000 words, just shake your phone and a clean summary will instantly appear.
If you prefer tapping, you can select Summarize Page under More in the three-dot menu.
Powered by AI, protected by Firefox
To keep your data secure, Shake to Summarize uses different technology depending on your device:
On iPhone 15 Pro or later (running iOS 26+): Your summary is generated right on your device using Apple Intelligence.
On all other devices: Your text is sent securely to Mozilla's cloud-based AI. We power this with Mistral-Small, an AI model carefully selected for its speed, efficiency, and alignment with an open internet
Your phone's default browser leaves you stuck scrolling through cluttered pages and content overload.
The Firefox mobile team is taking a purposeful approach - with smart tools that respect your time, privacy and choices. Download Firefox today to try Shake to Summarize, and get ready for even more features designed to help you move faster and protect your focus.
The 2025H2 Project Goal period has now concluded. Over these months, the Rust Project pursued 41 Project Goals, 13 of which were designated as Flagship Goals. This post contains curated updates on our progress since the last post and the final status for each of the goals (many of which continue as part of the 2026 period). Full details for any particular goal are available in its tracking issue.
Self-reviewed Implement borrowck for &pin mut|const $place. Found that the current approach of handling pinned borrows may be incorrect, as it failed to distinguish a pinned borrow from a coercion of a normal-to-pinned reference. The latter doesn't prevent a T: Unpin type from being moved, but the former does, which breaks the pin coercion test.
At the beginning of December, we set out to answer five important questions regarding the virtual places approach. We discussed four questions and arrived at answers for three.
Earlier this month, NadrierilDing Xiang Fei and I held a meeting on autoref and method resolution in a world with field projections. This meeting resulted in a new page for the wiki on autoref.
The first pull request of the lang experiment has just been merged: rust-lang/rust#152730
This PR enables the use of the field_of! macro to obtain a unique type for each field of a struct, enum variant, tuple, or union. We call these types field representing types (FRTs). When the base type is a struct that is not repr(packed), only contains Sized fields, this type automatically implements the Field trait that exposes some information about the field to the type system. The offset in bytes from the start of the struct, the type of the field and the type of the base type.
The feature is still incomplete and highly experimental. We also want to tackle the limitations in future PRs. For the moment this is enough to give us the ability to experiment with library versions of field projections and write functions that are generic over the fields of structs. For example one can write code like this:
#![feature(field_projections)]use std::field::{Field, field_of};use std::ptr;fn project_ref<'a, T, F: Field<Base = T>>(r: &'a T) -> &'a F::Type { // SAFETY: the `Field` trait guarantees that this is sound. unsafe { &*ptr::from_ref(r).byte_add(F::OFFSET).cast() }}struct Struct { field: i32, other: u32,}fn main() { let s = Struct { field: 42, other: 24 }; let r = &s; let field = project_ref::<_, field_of!(Struct, field)>(r); let other = project_ref::<_, field_of!(Struct, other)>(r); println!("field: {field}"); // prints 42 println!("other: {other}"); // prints 24}
A very important feature of the types returned by field_of! is that you can implement traits for them if you own the base type. This allows anointing fields with information by extending the Field trait. For example, this allows encoding the property of being a structurally pinned field:
use std::pin::Pin;unsafe trait PinnableField: Field { type StructuralRefMut<'a> where Self::Type: 'a, Self::Base: 'a; fn project_mut<'a>(base: Pin<&'a mut Self::Base>) -> Self::StructuralRefMut<'a> where Self::Type: 'a, Self::Base: 'a;}fn project_pinned<'a, T, F>(r: Pin<&'a mut T>) -> <F as PinnableField>::StructuralRefMut<'a>where F: PinnableField<Base = T>,{ F::project_mut(r)}
We can then implement this extra trait for all of the fields of our struct (and automate that with a proc-macro):
unsafe impl PinnableField for field_of!(Struct, field) { type StructuralRefMut<'a> = &'a mut i32; fn project_mut<'a>(base: Pin<&'a mut Self::Base>) -> Self::StructuralRefMut<'a> where Self::Type: 'a, Self::Base: 'a, { let base = unsafe { Pin::into_inner_unchecked(base) }; &mut base.field }}unsafe impl PinnableField for field_of!(Struct, other) { type StructuralRefMut<'a> = Pin<&'a mut u32>; // u32 is `Unpin`, so this isn't doing anything special, but it highlights the pattern. fn project_mut<'a>(base: Pin<&'a mut Self::Base>) -> Self::StructuralRefMut<'a> where Self::Type: 'a, Self::Base: 'a, { let base = unsafe { Pin::into_inner_unchecked(base) }; unsafe { Pin::new_unchecked(&mut base.other) } }}
Now you can safely obtain a pinned mutable reference to other and a normal mutable reference to field by calling the project_pinned function and supplying the correct FRT.
We have an updated plan for this goal in 2026 consisting of three major steps:
a-mir-formality,
Implementation,
Experimentation.
Some of their subtasks depend on other subtasks for other steps. You can find the details in the updated tracking issue. Here is a short rundown of each:
a-mir-formality: we want to create a formal model of the borrow checker changes we're proposing to ensure correctness. We also want to create a document explaining our model in a more human-friendly language. To really get started with this, we're blocked on the new expression based syntax in development by Niko.
Implementation: at the same time, we can start implementing more parts in the compiler. We will continue to improve FRTs, while keeping in mind that we might remove them if they end up being unnecessary. They still pose for a useful feature, but they might be orthogonal to field projections. We plan to make small and incremental changes, starting with library additions. We also want to begin exploring potential desugarings, for which we will add some manual and low level macros. When we have that figured out, we can fast-track syntax changes. When we have a sufficiently mature formal model of the borrow checker integration, we will port it to the compiler. After further evaluation, we can think about removing the incomplete_feature flag.
Experimentation: after each compiler or standard library change, we look to several projects to stress-test our ideas in real code. I will take care of experimentation in the Linux kernel, while Tyler Mandry will be taking a look at testing field projections with crubit. Josh Triplett also has expressed eagerness of introducing them in the standard library; I will coordinate with him and the rest of t-libs-api to experiment there.
Yesterday, we held a t-lang design meeting on our current approach. Nadrieril and I authored a design document with the feedback of Tyler Mandry, Ding Xiang Fei, Alice Ryhl, and Gary Guo. In this document, we provided the motivation for this feature, what the look and feel of a solution fitting into the existing features of Rust is, and a comprehensive + compact introduction to our current approach based on virtual places.
The general reception was extremely positive. To give some concrete quotes from the meeting:
Josh:
I adore this! I love how orthogonal it is, and how impactful and universal it is. I anticipate this becoming a beloved, pervasive feature of Rust.
Places and projection seem important enough to me that they're worth giving one of our precious remaining ASCII sigils to, and @ is nicely evocative of a place (something is at a place). So to the extent the final syntax benefits from a sigil, :+1: for giving this @. (See some feedback below on the details, though.)
TC:
Love it. High concept. As I said in the last meeting:
I particularly like language features that reduce the need for library surface area, and this is one of those.
There are, of course, many details to resolve and understand further, e.g., with respect to migration issues, interaction with const, async, and other effect-like things, etc. I'm looking forward to seeing the formalization work.
tmandry:
What I love about this direction is how effectively it builds on what Rust already has. I love to see designs that reinforce our existing concepts while pushing them in directions that make them more expressive.
Jack:
Whoo boy. This is great. There's so much here that I'm not exactly sure where to begin and what to comment on. I think this is the type of thing that we will only really be able to figure out the nitty gritty details and ergonomics only after some amount of experimentation.
There are a few takeaways from this meeting:
Mark raised the concern that t-libs should be more involved in reviewing the experimental traits that we intend to add. Ensuring that we don't accidentally stabilize or expose some behavior, have sufficient documentation on our experimental traits, and that t-libs is in the loop of this feature in general.
Mark offered to review PRs and I will be tagging him in those.
Jack raised the concern that increasing the cognitive load for the 95% use-case should be avoided. Making the right choice between @ and & might be challenging for users.
We discussed this point more in the meeting and concluded with that we need to do some experimentation, possibly utilizing the user research team. We will of course keep this in mind and revisit it later when we have a partially working implementation.
TC requested that we publish our fine-grained design axioms, essentially the list of things we go through when considering a modification of our proposal.
I will write an update on this issue explaining exactly those.
Aside from the concerns and directly actionable items, the meeting also covered design questions/comments that we want to take a look at in the coming weeks/months:
PR open to get the first working version of the Reborrow and CoerceShared traits merged.
Blockers
Currently "blocked" on PR review, and of course my (and Ding's) work to fix all review issues.
The review has brought up an opportunity to replace Rvalue::Ref / ExprKind::Ref with a more generalised variant that could encompass both references and user-defined references. This would be powerful, but it would be a very big and scary change. If this turns out to be a blocking issue for reviewers, then this will block the goal for the foreseeable future as the PR then starts on a massive refactoring.
Help wanted
The PR currently does not include derive traits, but we'd really want them. Instead of these:
impl<'a> Reborrow for CustomMarker<'a> {}impl<'a> CoerceShared<CustomMarkerRef<'a>> for CustomMarker<a'> {}impl<'a, T> Reborrow for CustomMut<'a, T> {}impl<'a, T> CoerceShared<CustomRef<'a, T>> for CustomMut<'a, T> {}
If anyone feels like picking up this thread, that'd be awesome: the derive macros do not need to really perform any validity checking, as the trait itself will do that.
If the PR merges soon, then public testing and exploration of the traits will be the next big thing. Likely concurrently with that the massive refactoring to generalise Rvalue::Ref / ExprKind::Ref.
No major updates this cycle - we're still working through feedback on rust-lang/rfcs#3874 and rust-lang/rfcs#3875 and prototyping the implementation to be prepared.
Update this cycle is the same as last time - rust-lang/rfcs#3874 and rust-lang/rfcs#3875 are progressing, with feedback being addressed and checkboxes checked, and we're still working out what the implementation would look like.
rust-lang/rfcs#3874 has finished FCP and is due to be merged any day now. I'm working on resolving the remaining open comments on rust-lang/rfcs#3875 and then intend to nudge the reviewers to have a look and check their boxes or leave concerns.
Adam Gemmell has opened rust-lang/cargo#16675 with an early sketch of some of the core changes that build-std would require and is working with the Cargo team to address feedback and work out how to proceed with the implementation.
There hasn't been too much progress over the last few weeks and I've been mostly taking a Christmas break. Nicholas Nethercote has been looking into the performance of the new trait solver, cleaning up canonicalization and slightly improving its performance: PR 1 and PR 2.
Shoyu Vanilla looked into ICE from mir validation on unsizing in opendal and uncovered the underlying bug there. While this issue also affects the old solver and the proper fix for it requires where-bounds on binders, we can work around this bug in the trait solver for now and intend to do so.
We've started another crater run with all our recent changes and adwin has started to triage it, uncovering one new issue up until now. Intend to continue going through that over the next few weeks.
we discussed how to investigate and fix the remaining correctness issues in Tage's work, to be able to evaluate it more accurately: in particular around variance and bidirectional edges, and without the reliance on NLL (having computed region values / errors)
we've tried to see if it'd be possible to remove the cfg region elements
Amanda is still working on her two papers, one about the current borrow checker and one about the work on Polonius. Her major PR for the restructuring of placeholder handling during region inference is stalled due to a conflict with further trait solver developments and may have to be abandoned. Work with the larger types team is ongoing and smaller patches/refactorings/improvements are being landed in the meantime.
I've also fixed more small inefficiencies (computing boring/relevant locals on-demand in diagnostics, removed conversions between locations and points, etc) building on top of the previous PRs (so they need to be reviewed first)
I've looked at crates.io again with the alpha, to find functions that are slower than with NLLs. AFAICT the worst case there is 60% for a 5KLOC function with 42K loans, 255K statements, and 125K outlives constraints. I'll see what we can do with this. Small composable functions is still good advice.
there seem to be optimization opportunities to 1. limit propagation to the smaller number of blocks that could be affected by bidirectional edges, 2. for unifying invariant lifetimes of live locals that are assigned at most once (à la use-def chains), 3. for invalidations that are just the activation of a reservation
we discussed possible plans to gather actual statistics, using the infrastructure that was created for the Metrics project
we're also preparing the new project goal for this year, where we'll want to stabilize the alpha 🤞
We had a bit less time this month, the update will be shorter, but still meaningful I hope:
#150551 has landed, and it feels stabilizable. To me, this part of the goal is achieved.
still, "stabilizable" is not stable, and there is more work to do. We plan to stabilize this year, and the project goal proposal for 2026 tracks how.
tiif is still deep in #152051, and a-mir-formality work with Niko and I.
Amanda has opened a few cleanup PRs (#152438, and #152579), and #151863 has landed already. She also has started looking into Tage's old PR to see if we can fix it, benchmark it more accurately, and see the cool parts there that we could be using.
Jack is possibly going to have some time to work with us this year! His help will be very welcome, especially as I will have less time available myself.
we'll be tracking the opaque type region liveness soundness issue in #153215, and I've added a couple tests, in case tiif's PR or anything that impacts them lands.
some of the tiny cleanups I mentioned last time have also landed in #152587.
The Rust Foundation is opening up a short-term, approximately 3-month, contracting role to assist in our Rust/C++ Interop initiative. The primary work and deliverables for the role will be to make substantial progress on the Problem Space Mapping Rust Project Goal by collecting discrete problem statements and offering up recommendations on the work that should follow based upon the problems that you found.
If you are interested in how programming languages interoperate, are curious in understanding the problems therein, and are have a passion to think about how those problems may be resolved for the betterment of interop, then this work may be for you.
An ideal candidate will have experience with Rust programming. Having experience in C++ is strongly preferred as well. If you have direct experience with actual engineering that required interoperating between Rust and C++ codebases, that's even better.
If you are interested, please email me (email address found in my GitHub profile) or contact me directly on Zulip by Tuesday, January 27 and we can take it from there to see if there may be a potential fit for further discussion.
The effort to fill the contracting role to support this project goal is in the process winding down. The interview and discussion process is nearly complete. We expect to make a final decision for the role in early February.
Next step is prioritising a few of the use cases, then working on related problem statements in more detail.
Blockers
Nothing at the moment, still working through the high level mapping of the problem space.
Help wanted
Suggestions for more interop use cases would be very welcome, just open a discussion in t-lang/interop and I'll turn it into a ticket. Or go ahead and open a use case ticket directly.
Next step is continuing to work on overloading and build systems in more detail. If you have specific Rust/C/C++ build system blockers, please open a chat or ticket.
Blockers
Nothing at the moment, everyone has been extremely helpful, and I'm getting good feedback on use cases, problems, priorities, and Rust language experiments.
these interop user experiences are waiting for analysis, so they can be summarised in the build system and overloading problem statements
this will likely happen after RustWeek and the All Hands
Next step is continuing to work on the overloading experiment, along with RustWeek/All Hands preparation, and collecting feedback during the conference.
Blockers
Nothing at the moment. There is a steady stream of new use cases, problems, code examples and Rust language experiment feedback.
Boxy and I have established a regular time to check-in on formalizing this within a-mir-formality. Today we mostly worked on the "model" of const values, starting with this
i.e., a const value can be a scalar value (as today) or a struct literal like Foo { ... } (which would also cover tuples and things). We got the various tests passing. Huzzah!
In addition to what niko posted previously there's been a lot of other stuff happening. A lot of people have opened PRs to improve mGCA this month: León Orell Valerian LiehrNoah Lev @enthropy7 Kivooeomu001999 @Human9000-bit Redddy @Keith-Cancel @AprilNEA
A rough list of things that have been improved for mGCA:
Lots of new expressions now supported by mGCA: const constructors, tuple constructor calls, array expressions, tuple expression, literals
associated_const_equality has been merged into min_generic_const_args. the former was effectively dependent on the latter already so this just makes it nicer to use the former :)
traits can now be dyn compatible if all associated constants are type consts and are specified in the trait object (e.g. dyn Trait<ASSOC = 10>)
type consts are enforced to be non-generic
a bunch of ICEs have been fixed
camelid has been working on "non-min" version of mGCA which will allow arbitrary expressions to be used in the type system (a blog post with more detail will be published once this actually lands)
In non-mGCA updates, as niko says, we've been meeting regularly to make progress on modelling const generics in a-mir-formality. I've also been spending time thinking about the interactions between adt_const_params and ADTs with privacy/safety invariants and I think I know how to structure the RFC in this area so can make progress on that again
There's been a lot of miscellaneous fixes for mGCA this month. I've also started drafting some blog posts to explain what's going on with mGCA/oGCA as well as soliciting use cases/experience reports for them and adt_const_params. I also talked with some folks at Rust Nation this month about const generics and what features would be useful for them and why.
Late on the update :') niko and i continue to meet to discuss const generics. we've made some progress on figuring out problems around privacy/safety in const generics. we've also been discussing the big picture stuff for const generics and where we're "heading".
started running weekly meetings about const generics to make it easier to keep up to date with all the people who are working on const generics stuff. i think min_adt_const_params is now at the point of what the RFC is going to specify.
GCA is making good progress thanks to ashley's work. i also met with lcnr where we talked about whether there was some version of mGCA that is stabilizeable in the near future or not (maybe!)
since we completed the 1.94.0 release of the FLS a bit early this time, we checked into the stretch part of our goal this year to look at 1.95.0 early
we learned a bit more of the release notes process thanks to tips from Eric Huss and TC
Tshepang Mbambo and I attended the t-release meeting last week where we chatted about working a little "upstream" with them on generating the release notes a bit earlier
tomorrow in our t-fls meeting we'll discuss our interest with engaging over there; at a minimum I'll get engaged with t-release
Glossary and main-body text harmonization:
the first PR landed from Tshepang Mbambo removing IDs from the glossary
further steps planned, we have a tracking issue for it
Developer guide:
akin to how the Reference now has a developer's guide now for contributing we'll do the same in the FLS
Yesterday, we posted an MCP for our retag intrinsics. While that's in progress, we'll start adapting our current prototype to remove our dependence on MIR-level retags. Once that's finished, we'll be ready to submit a PR.
We published our first monthly blog post about BorrowSanitizer.
Our overall goal for 2026 is to transition from a research prototype to a functional tool. Three key features have yet to be implemented: garbage collection, error reporting, and support for atomic memory accesses. Once these are complete, we'll be able to start testing real-world libraries and auditing our results against Miri.
We provide detailed error messages for aliasing violations, which look almost like Miri's do!
We have two forms of retag intrinsic: __rust_retag_mem and __rust_retag_reg. We no longer require a compiler plugin to determine the permission associated with a retag, which will make it possible to use BorrowSanitizer by providing a single -Zsanitizer=borrow flag to rustc. You can check out our MCP for more detailed design updates.
We are starting to have a better understanding of how BorrowSanitizer performs in practice, but we do not have enough data yet to be certain. From one test case, it seems like we are somewhat faster but still in the same category of performance as Miri when we compare against other sanitizers. Expect more detailed results to come as we scale up our benchmarking pipeline.
We have a tentative plan for upstreaming BorrowSanitizer in 2026, starting with its LLVM components. We intend to start the RFC process on the LLVM side this spring, once our API is stable.
We added hundreds more relevant tests from Miri's test suite. At the moment, 80% pass.
We improved our cargo plugin (cargo-bsan) to better support multilanguage libraries. This will let us start to recreate the bugs from our earlier evaluation.
Our goal for April is to continue expanding our test suite, finish an initial version of the LLVM components of BorrowSanitizer, and hopefully start the RFC process on the LLVM side.
We have some exciting news: our talk on BorrowSanitizer was accepted at RustConf this year! We're grateful for the opportunity and looking forward to sharing our results with the broader community this September.
BorrowSanitizer now uses a shadow stack to track metadata at runtime - this is a significantly different strategy than other LLVM sanitizers, and it will help us support garbage collection.
We are now ready to start sending in PRs for our retag intrinsics. It will take a little time to split our changes up into meaningful, reviewable chunks-you can expect to see these throughout the next week.
The RFC for our LLVM components is taking a little longer than expected, but it was worth taking the extra time to test out compiler changes and make sure that we had the core parts of the instrumentation pass settled. We'll be drafting the RFC throughout the next few weeks.
std::autodiff is moving closer to nightly, and std::offload is gaining various performance, feature, and hardware support improvements.
autodiff
Jakub Beránek, sgasho, and I continued working on enabling autodiff in nightly. We have a PR up that builds autodiff in CI, and verified that the artifacts can be installed and work on Linux. For apple however, we noticed that any autodiff usage hangs. After some investigation, it turns out that we ended up embedding two LLVM copies, one in rustc, and one in Enzyme. It should be comparably easy to get rid of the second one. Once we verified that this fixes the build, we'll merge the PR to enable autodiff on both targets in nightly.
offload
A lot of interesting updates on the performance, feature, and hardware support side.
Marcelo Domínguez, @kevinsala, @jdoerfert, and I started implementing the first benchmarks, since that's generally the best way to find missing features or performance issues. We were positively surprised by how good the out-of-the-box performance was. We will implement a few more benchmarks and post the results once we have verified them. We also implemented multiple PRs which implement bugfixes, cleanups, and needed features like support for scalars. We also started working on LLVM optimizations which make sure that we can achieve even better performance.
I noticed that our offload intrinsic allowed running Rust code on the GPU, but it wasn't of much help when calling gpu vendor libraries like cuBLAS. I implemented a new helper intrinsic which allows calling those functions conveniently, without having to manually move data to or from the device. It will benefit from the same LLVM optimizations as our full offload intrinsic. It also a bit simpler to set up on the compiler and linker side, so it already works with std and mangled kernel names, something that we still have to improve for our main offload intrinsic.
A lot of work happened on the LLVM offload side for SPIRV and Intel GPU support. At the moment, our Rust frontend is tested on NVIDIA and AMD server and consumer GPUs, as well as AMD HPC and Lapotop APUs. Karol Zwolak reached out since he wants to help with with also running Rust on Intel GPUs. Offload relies on LLVM which started gaining Intel support, so hopefully we won't need much work beyond a new intel-gpu target and a new stdarch module. There is also work on a new spirv target for rustc, which we could also support if it goes through LLVM. Due to some open questions around typed pointers it does not seem clear yet whether it will, so we will have to wait.
Nikita started working on updating our submodule to LLVM 22. This hopefully does not only brings some compile and runtime performance improvements, but also greatly simplifies how we can build and use offload. Once it landed I'll refactor our bootstrapping logic, and as part of that start building offload in CI.
std::autodiff is now partly in CI, and std::offload got tested on a lot more benchmarks.
autodiff
Work continued on enabling autodiff in nightly. Since the last update, we have enabled autodiff in some Mingw and Linux runners. Users can now download libEnzyme artifacts, place them locally in the right spot for their toolchain, and then use autodiff on their nightly compiler. Once macOS is added, we will enable a new rustup component that will handle the download for users. Before enabling autodiff on macOS, however, we want to change how we distribute LLVM on this target (from static to dynamic linking). There are a lot of workflows and users of this target, not all of which can be modelled in the Rust CI. Our last two attempts sadly broke such downstream users and local contributors, so both attempts had to be reverted. Since testing here is tricky, progress here might be on the slower side; we will see.
offload
Most of the work on the offload side lately has been invisible, since we were working on implementing more benchmarks and LLVM optimizations, as well as missing features, discovered by those benchmarks. We achieved excellent performance on those benchmarks; more details will soon be presented by Marcelo Domínguez at the EuroLLVM conference in two weeks!
Beyond benchmarks, there was a lot of tinkering on smaller PRs, reviewing, and housekeeping. LLVM-22 landed, so we updated our bootrstrap code to make use of new APIs, and tried to move a few smaller PRs forward, mainly around a better user experience and for making more Rust features available. Since the focus is still on benchmarks, not many of those PRs landed. They are in a mostly ready state, so it's a good time to pick them up if you're considering contributing. Please ping me on Zulip or in any PR with the offload label if you are interested!
It seems that figuring out which functions are noreturn is at a level too low for rustc. Function signatures are not sufficient and there are cases where rustc doesn't know whether to emit noreturn. It is something we should ask the LLVM to give us that information.
Alice Ryhl opened a new issue to introduce the -Zsanitizer=kernel-hwaddress sanitizer for aarch64 targets: https://github.com/rust-lang/compiler-team/issues/975
The corresponding RFC has been discussed by the Lang team on 2026-03-11. The overall vibe was positive and TC is going to read through it and hopefully check a box on the proposed FCP.
Ding's arbitrary_self_types: Split the Autoderef chain rust#146095 is waiting on reviews. It updates the method resolution to essentially: deref_chain(T).flat_map(|U| receiver_chain(U)).
The perf run was a wash and a carter has completed yesterday. Analysis pending.
We've discovered the #[feature(arbitrary_self_types_pointer)] feature gate. As the Lang consensus is to not support the Receiver trait on raw pointer types we're probably going to remove it (but this needs further discussion). This was a remnant from the original proposal, but the Lang has changed direction since.
Ding is writing a post to describe all the open proposals including Alice's new one that she brouhght up during the LPC 2025. He'll merge it in the Beyond References wiki.
Macros, attributes, derives, etc.
Josh brought up his work on adding more capable declarative macros for writing attributes and derives. He's asked the Rust for Linux team for what they need to stop using proc macros.
Miguel noted they've just added dependency on syn, but they would like to remove it some day if their could.
Benno provided a few cases of large macros that he thought were unlikely to be replaceable by declarative-style ones. Josh suggested there may be a way and suggested an asynchronous discussion.
The team discussed the impact of this. These hints are used in C but not yet in Rust. cold_path would be sufficient, but likely/unlikely would still be more convenient in cases where there isn't an else branch. Tyler Mandry mentioned that these can be implemented in terms of cold_path.
Niche optimizations
We discussed the feasibility of embedding data in lower bits of a pointer -- something the kernel is doing in C. This could also enable setting the top bit in the integers (which is otherwise never set) and make it represent an error in that case (and a regular pointer otherwise).
Ideally, this would be done in safe Rust, as the idea is to improve the safety of the C code in question.
Extending the niches is something Rust wants to see, but it's waiting on pattern types. There are short/medium-term options by using unsafe and wrapping it in a safe macro, but the long-term hope is to have this supported in the language.
Vendoring zerocopy
The project has interest in vendoring zerocopy. We had its maintainers Jack Wrenn and Joshua Liebow-Feeser join us to discuss this and answer our questions. The main question was about whether to vendor at all, how often should we (or will have to) upgrade, and how much of it is expected to end up in the standard library.
The project follows semver with the extended promise to not break minor versions even before 1.0.0. We could vendor the current 0.8 and we should be upgrade on our own terms (e.g. when we bring in new features) rather than being forced to.
Right now, the project is able to experiment with various approaches and capabilities. Any stdlib integration a long way away, but there is interest in integrating these to the language and libraries where appropriate.
New trait solver
There's been a long-term effort to finish the new trait solver, which will unblock a lot of things. Niko Matsakis asked about things it's blocking for Rust for Linux.
This is the list: unmovable types, guaranteed destructors, Type Alias Impl Trait (TAIT), Return Type Notation (RTN), const traits, const generics (over integer types), extern type.
2026 Project goals
This year brings in the concept of roadmaps. We now have a Rust for Linux and a few more granular Goals. We'll be adding more goals over time, but the one merged cover what we've been focusing on for now.
We spent most of the meeting going over the open Project goals, the Rust for Linux roadmap and other things we'd like to see that aren't the right shape for a goal.
Miguel Ojeda brought up the upcoming Debian 14 release (coming out probably somewhere around Q2 of 2027) and we went over each item and decided whether it's something we need to make sure is in that release or not.
Debian stable is an important milestone and the Rust version in it serves as a baseline for Rust for Linux development.
The dma_read! / dma_write! macros switched over to it. This also fixes a soundness issue 1.
Note: this is done entirely via macros and doesn't use any Field projections language features. The Field projection syntax and traits should make this more ergonomic and integrate the borrow checker so we can accept more code.
We're planning to have a design meeting with the Lang team in the last week of March.
rustfmt imports formatting and trailing slashes
We talked about the rustfmt formatting of the use statements again. While the trailing empty comment // workaround (see this update) is acceptable as a temporary measure, we need to find a long-term solution where you can configure rustfmt to accept this style.
We don't have a issue for this specific formatting yet, though it was discussed in #3361.
The next step are to create such an issue. We were hesitant to add burden to a team that's already at limit, but having the issue would let us track it from the Rust for Linux side.
Boxy asked the team for features that are most important under the const generics umbrella. This might help with prioritisation and just understanding of practical uses.
Ability to do arithmetic on const generic types: e.g. the kernel has a type Bounded which has a value and a maximum size (in bits). Both the bit width and value are const values. They want to be able to do arithmetics on these types (starting with bit shifts) that will guarantee the the result will fit within the specified size at compile time.
Argument-position const generics: right now, the const generic types must be specified in the type bound section (within the angle brackets). So for example you have to write: Bounded::<u8, 4>::new::<7>() instead of the more natural Bounded::<u8, 4>::new(7). This gets more complicated when there's const-time calculation happening rather than just a numerical constant -- in which case this also needs to be wrapped in curly brackets: { ... }.
Being generic over types other than numbers: pointers would be useful for asm_const_ptr. String literals too -- even if they're just passed through without being processed / operated on. And if going from a passthrough string makes it possible to pass through any type, that would help the team replace some typestate patterns they're using with an enum.
statx
Alice Ryhlproposed being able to create std::fs::Metadata from Linux statx syscall.
This was discussed in the Libs-API meeting and they had questions about possible evolutions of the statx ABI -- if/how it can grow in the future and how they could handle that if they wanted some of the new data available. So we discussed it in the Rust for Linux meeting.
In the end, it seems prudent to be reasonably defensive rather than relying on the syscall pre-filling default values.
Alice Ryhlproposed an opaque statx struct that would give the stdlib a way to decide on the struct's size, pre-filled contents and mask.
Miguel Ojeda suggested contacting Christian Brauner and Alexander Viro (i.e. the VFS maintainers); Josh Triplett agreed that it would be good if we can get a thread with the right people in linux-fsdevel.
zerocopy uses two traits that are both polyfills for unstable traits : KnownLayout (for ptr_metadata) and Immutable (for Freeze). It would help maintenance of zerocopy (which Rust for Linux plans to start using) if these were stabilised.
ptr_metadata is something the team wants in the kernel independently. It's possibly blocked on (or at least might have interactions with) the Sized Hierarchy work.
We also discussed the dependence/independence of the Deref and Receiver implementations, in particular whether it ever makes sense to implement Deref but notReceiver. Josh Triplett suggested gathering examples for cases like that (where you can't use the type as a Self type in the function declaration, but allow calling methods on it).
The current plan for the experiment is to have these traits separate, but have the compiler enforce that if they implement the same type, their targets are identical. This will let us open the door for any future possibilities (a supertrait / subtrait relation, or having diverging targets in the future).
We want to experiment to see where and how these traits and their possible evolution might be helpful.
null-ptr-deref
The team would like to have a (an optional) compiler guarantee, that the compiler never removes null checks on raw pointers. What can currently happen in C is that if you deref a null pointer, the compiler can do optimisations including removing any subsequent checks whether that pointer is null, because dereferencing a null pointer is undefined behaviour.
But the null check can still help prevent further bugs and in C, the kernel now disables the optimisation that would remove it.
The RFC has just been published. It has been significantly reworked since the last draft.
Notable changes:
Removed the concept of activation/de-activation. Now the semantics don't need to deal with partially allocated locals. This is less powerful optimization-wise but should still cover most cases.
Added byref/byval to call arguments to clarify how they are passed.
Added a separate section for the surface language changes to separate it from the MIR changes.
Added more details on the MIR optimization which eliminates moves.
Changed the MIR operand evaluation order to be left-to-right, except for destination places which are always evaluated last.
Added StorageLive back: we need it to mark the location where llvm.lifetime.start should be inserted, which is not the same as the location where a local is initialized. In the opsem, StorageLive doesn't actually allocate the local, that's still done when it is initialized by a write.
The prototype of this project goal is basically complete.
Current state
This project goal introduces build analysis support in Cargo, with the aim of making build behavior understandable across multiple invocations, not just a single run.
At a high level, the prototype:
Records build metadata over time, including:
rebuild reasons
timing information
relevant invocation context
Stores this data locally in a structured log format suitable for later analysis
Exposes the data via unstable cargo report subcommands, such as:
cargo report sessions - list session IDs
cargo report timings - HTML timing report
cargo report rebuilds - Why things rebuilt
See the Reference for a more thorough usage documentation
Path towards stabilization
Before this feature can be stabilized, the following unresolved questions must be answered. They might not block stabilization, but need to be evaluated if it is fine to leave for future.
cargo report commands
This is a stabilization blocker.
Currently all three report commands (sessions, rebuilds, timings) implicitly inspect global log files when if not in a workspace.
Should this be explicit with a flag?
Should this be an error if not in a workspace?
Bikeshed on command names
Currently we have all nouns
For sessions
runs simple but ambiguous
Just log like git log
history user-friendly (docker history, shell history, though not alike)
For timings:
Not controversial, as we have --timings flag already
There is ongoing work for Adts and function pointers, both of which will land as MVPs and will need some work to make them respect semver or generally become useful in practice
Removing the 'static bound from try_as_dyn turned out to have many warts, so I'm limiting it to a much smaller subset and will have borrowck emit the 'static requirement if the other rules do not apply (instead of having an unconditional 'static requirement)
There are some known issues we'd like to address before doing a formal call for testing. Notably, improving blocking messages, fixing potential thread starvation in Cargo's job queue when locks block, and investigate increasing rlimits to reduce risk of hitting max file descriptors for large projects.
I am hopeful that these issues will be resolved over the coming month and we can do a call for testing to start gathering feedback from the community on whether the new locking strategy improves workflows.
After the initial PR from the last update was merged, we shifted our focus to resolving some of the known issues. Notably, locking blocks the Cargo job queue slowly causing thread starvation if many build units are held by another Cargo instance.
We investigated adding the ability for Cargo to "suspend" a job internally while waiting for a lock, but we felt this change was a bit invasive and did not fit well with how the job queue was designed. Instead we plan to change our design to acquire all build unit locks prior to running the job queue (see #16657).
At the same time, we have continued to refine the new build-dir to prepare it for a call for testing and eventual stabilization. (#16542, #16502, #16515, #16514)
Finally we decided to split .cargo-lock into 2 locks to allow cargo check and cargo build to run in parallel when artifact-dir == build-dir (and -Zfine-grain-locking is enabled)
I suspect this may be the last update on this goal, as the 2026 slate of goals is coming up. While I did not renew this goal for 2026, I do plan to continue work on this and eventually stabilize this within this year.
Both the MCP and the PR for the AddressSanitizer target have been merged. Next up I should prepare the MCP for the Memory- and ThreadSanitizer targets, hopefully sending out soon.
rust-lang/rust#143924 has been merged, enabling scalable vector types to be defined on nightly, and I'm working on a patch to introduce unstable intrinsics/scalable vector types to std::arch
Progress has been slow since the last update because I've been busy, but I've been working on a rebase of rust-lang/stdarch#1509, which has bitrot quite a bit. Rémy Rakic is joining me to work on the Sized Hierarchy parts of the goal.
On the scalable vector half of the goal, I've got a branch with rust-lang/stdarch#1509 rebased, though without the intrinsic-test tool having been updated - that ended up being tricky and we've agreed to do it as a follow-up. We've opened rust-lang/rust#153286 with the compiler fixes that the stdarch patch requires, which should land soon (rust-lang/rust#153653 was opened and landed in the interim).
On the sized hierarchy half of the goal, Rémy Rakic has been updating our RFC such that we can discuss it in design meetings with the language team on the 18th and 25th - we'll update rust-lang/rfcs#3729 later today. We've split out the const Sized parts as a future possibility (though one we are committed to pursuing) as that has more open design questions, and we've discussed the proposed syntax and approach to migration - which are what we intend to focus on in the design meetings. He's also been working out how we can start implementing our migration strategy and help resolve blockers in other areas.
For the sized hierarchy half of the goal, Rémy Rakic and I had two design meetings with the language team (2026/03/18 and 2026/03/25) discussing the syntax/naming and migration strategy respectively.
On syntax, the language team preferred introducing an "only bounds" syntax to control opting-out of default bounds and opting-in to alternative bounds in a family of traits (described in an alternative in the RFC), but there was an open question of whether that syntax should apply to an individual bound or all of the bounds - Niko Matsakis is investigating that.
On naming, the language team also preferred the name SizeOfVal over MetaSized, and didn't like Pointee but had no better alternatives. Rémy Rakic prepared rust-lang/rust#154374 to do that renaming and started a discussion with the library team to confirm they were happy with the name, because changing it involves an amount of churn. The library team wanted to know what other traits in the hierarchy might later be introduced, as that would help inform the naming of the currently proposed traits, so Rémy Rakicwrote up a document with that information. We're holding off on doing any name changes until we find some consensus between libs and lang - who is responsible for these traits' names is a bit unclear.
On migration, the language team were largely happy with our proposed approach, and we realised that the approach proposed by lcnr for associated types might also work for our other migrations. Rémy Rakic has had meetings with lcnr to better understand that approach and to work out the next steps for implementing it.
In the context of concerns around young people's interactions with digital technologies, the UK's Department for Science, Innovation and Technology is consulting on additional measures to prepare young people for growing up in a digital world. Before the backdrop of users circumventing age assurance systems mandated under the UK's Online Safety Act, the consultation considers age-gating virtual private networks (VPNs).
Mozilla's mission is grounded in the belief that the internet must remain open and accessible to all, and that privacy and security online are fundamental human rights. We recognize that the protection of young people online is one of the most pressing and challenging questions of our time, and we are committed to supporting policy proposals that address the root causes of online harms. We are concerned, however, that blunt interventions like mandatory age assurance and restricting access to tools like VPNs are not effective in improving the protection afforded to young people online, while undermining the fundamental rights of all users.
VPNs serve as critical privacy and security tools for users across all ages. By hiding users' IP addresses, VPNs help protect users' location, reduce tracking and avoid IP-based profiling. People use VPNs for lots of different reasons: to connect to their school's or employer's network remotely, to avoid censorship or to simply protect their privacy and security online. While being able to access VPNs is especially important for vulnerable groups like activists, dissidents or journalists, VPNs improve everyone's baseline protection online.
Young people are particularly vulnerable to online tracking, targeted advertising, and the risks that flow from personal data being collected and processed for commercial purposes without adequate consent or transparency. In a world in which young people are interacting with digital technologies as part of their realities from young ages onward, restricting young people's access to privacy-protecting technologies is in tension with the goal of equipping them to navigate the internet safely and competently. In order to be able to develop agency and responsible habits in engaging with digital technologies, it is crucial for young people to be introduced to best practices and key safety and privacy tools as they engage with the online world.
Rather than age-gating technologies like VPNs, we believe that regulators should address the root causes of online harm by holding platforms to account, encouraging the responsible use of parental controls and investing in digital skills and a whole of society approach to digital wellbeing.
Read our full submission to the Department for Science, Innovation and Technology.
Hello and welcome to another issue of This Week in Rust! Rust is a programming language empowering everyone to build reliable and efficient software. This is a weekly summary of its progress and community. Want something mentioned? Tag us at @thisweekinrust.bsky.social on Bluesky or @ThisWeekinRust on mastodon.social, or send us a pull request. Want to get involved? We love contributions.
An important step for RFC implementation is for people to experiment with the implementation and give feedback, especially before stabilization.
If you are a feature implementer and would like your RFC to appear in this list, add a call-for-testing label to your RFC along with a comment providing testing instructions and/or guidance on which aspect(s) of the feature need testing.
Always wanted to contribute to open-source projects but did not know where to start? Every week we highlight some tasks from the Rust community for you to pick and get started!
Some of these tasks may also have mentors available, visit the task page for more information.
No Calls for participation were submitted this week.
If you are a Rust project owner and are looking for contributors, please submit tasks here or through a PR to TWiR or by reaching out on Bluesky or Mastodon!
Are you a new or experienced speaker looking for a place to share something cool? This section highlights events that are being planned and are accepting submissions to join their event as a speaker.
If you are an event organizer hoping to expand the reach of your event, please submit a link to the website through a PR to TWiR or by reaching out on Bluesky or Mastodon!
This week saw a couple of PRs affecting the new trait solver, which is steadily moving forward, in particular #156139 was a massive perf. win. #156185 optimized visibility computation, resulting in up to a 8% win on the typenum crate.
If you are running a Rust event please add it to the calendar to get it mentioned here. Please remember to add a link to the event too. Email the Rust Community Team for access.
Of the last 150 merged PRs to Bun, 108 are memory-safety-adjacent - missed cleanup on an error path, use-after-free, uninitialized reads, out-of-bounds access, reentrancy. 75 of those would not compile in a language with destructors, move semantics, and a borrow checker. One in three PRs we ship is "forgot to free something on an error path."
Of the 108, ~88 are in Zig. The ~14 in C++ are mostly ref-cycles and GC-concurrency races - the residual class that survives any language. So the Zig→Rust delta is real: the Zig bugs are exactly the destructor/ownership-fixable kind, and the C++ side is already near the floor.
Without stronger compile-time guarantees, this stays a cat-and-mouse game. The proposal is to remove the largest bug class structurally rather than fix instances of it indefinitely.
At Mozilla, we've long believed in giving people choice and agency over their experiences online. As power in digital markets has concentrated in a small number of large companies, there have been efforts in the US, Japan, UK, India, Korea, Brazil and elsewhere to restore competition and put choice back in people's hands.
These efforts are at various stages, but first among them was the EU's Digital Markets Act. Over two years since obligations came into effect, the DMA is delivering for people in some key areas.
Not everywhere. Not perfectly. And not without enforcement. But browser choice is the clearest example.
Every 10 seconds, someone picks Firefox through a DMA choice screen
Operating systems like iOS, Android, Windows, and MacOS lean on pre-installed browsers, tricky default settings, and deceptive design to make it hard for people to exercise choice and to keep independent browsers from competing on a level playing field. But where the DMA has created opportunities for genuine browser choice, people are taking it.
New Mozilla data is clear: since the rules took effect, Firefox is selected through a DMA browser choice screen every 10 seconds. That adds up to more than six million Firefox selections. And people are sticking with us: retention is five times higher when people choose Firefox through a choice screen.
Academic analysis points the same way. Independent researchers compared Firefox daily active users in the EU with 43 non-EU countries. Comparing the 15 months before and after browser choice screens rolled out on iOS, they found that Firefox daily active users (DAU) were 113% higher in the EU than it would have been without the DMA. On Android, it was 12% higher. The smaller Android effect is due to the fact that Firefox usage there started from a much higher base, and the Android rollout has been more uneven than on iOS. The research also shows that the DMA's effect is growing over time.
Browser choice on mobile is moving, but desktop is left behind
The DMA's work isn't done. There remains room for improvement on mobile (including making it easier to import your data to a new browser and switch with one click). However, desktop remains largely untouched - leaving roughly 310 million desktops and laptops in the EU without equivalent browser choice. For example, Windows users are subject to deceptive design tactics and are not given an active choice. Even where choice screens exist, they are not a silver bullet; ecosystem lock-in and interoperability barriers still hold back competition and innovation.
Still, the signal is clear: when people get real browser choice, they take it and select alternatives. It's easy for gatekeepers to dismiss this as a couple of competitors benefiting. This ignores the range of challenger browsers also reporting huge growth in the EU. What's more, it ignores the benefit to people. DMA browser choice screens are reaching different audiences. Mozilla analysis shows that women make up a significantly higher share of Firefox selections on iOS via a choice screen than organic downloads, suggesting that choice screens may successfully reach a demographic that reports lower confidence in manually changing browser defaults. The DMA's effects are only starting to be felt and understood.
The road ahead
Effective enforcement of the obligations is the way forward. Gatekeepers continue to test and, in many instances, openly push back against the intent of the DMA provisions. This can take the form of implementation choices that limit real user uptake, delays in rolling out effective solutions, or sustained efforts to reinterpret, weaken, or roll back key provisions.
Most evidently, privacy and security arguments are often elevated in ways that risk diverting attention from whether compliance is delivering genuine choice and competition in practice. In reality, privacy, security, and effective competition can and should be designed to work hand-in-hand. They do not always have to be traded off.
Policymakers and enforcers should remain focused on outcomes: ensuring that the DMA delivers real-world competition and user choice, and resisting efforts to dilute its impact through partial compliance or narrative reframing.
Browser choice is just the start
Mozilla's hope is that real browser choice will become the rule, rather than the exception. And that the lessons of browser choice screens will be applied to other areas of the DMA, including data portability and interoperability. Only with full compliance - including applying the existing DMA text to AI - can the full benefits of competition and innovation be brought to people in the EU.
If you have ever used Thunderbird in Bulgarian, the subject of this month's office hours is one of the contributors who made that possible! Office Hours hosts Heather and Monica have been lucky enough to chat with long-time localizer Bogamil Shopov at conferences like FOSDEM. Now, they're sitting down to talk to him about how his contributor story started, and to hear the advice he has for anyone curious about being part of Thunderbird.
We'll be back next time, checking in on Thunderbird Pro! It's been almost a year since we sat down with members of our team making this possible. As we've slowly started opening up the service to our Early Birds from the waitlist, it seemed a great opportunity to learn what users can expect, now and in the future!
A Contributor Origin Story
Bogomil has been a contributor to Thunderbird from the start! Even if he didn't like the UI in the early days, he loved our commitment to software freedom. He wanted people to have the software in their own language, and so he started localizing Thunderbird into Bulgarian. This was in the days before Pontoon, Mozilla's localization platform, and so this wasn't easy work! Another hurdle, which still happens, is that small language locales tend to have smaller numbers of contributors doing localizing work.
From Mentee to Mentor
Thankfully, Bogomil had unofficial mentors to help them find their way. On the localization side, he had a Bulgarian mentor who he, in turn, introduced to Mozilla and other open source projects. More experienced Mozilla contributors answered his questions as he expressed his interest in getting more involved. Bogomil points out that this need for both good onboarding docs and genuine human connection is still something all open source projects needs - and something we can keep improving!
Now Bogomil is the experienced mentor, and he's had some key steps to helping grow the Thunderbird localizer community. He sends thank you notes to new translators and sends resources for specific computer and email terminology to new Bulgarian volunteers. He gives his mentees specific tasks to help focus their energy and excitement, and maybe even more importantly, he gives them encouragement.
This Speaker goes to 11
While Bogomil had given several public talks in Bulgarian before COVID, after 2020 he decided to try giving conference talks in English. His first talk was at Halfstack, sponsored by Mozilla, in Vienna, on what made him happy as a developer and IT person in general. He even brought a violin player on stage with him! His talk got a warm reception, and it encouraged him to keep talking about what brought him joy, whether it was using heavy metal to help him focus or his involvement with Thunderbird and open source. If you've ever considered giving a talk, especially about one of your passions, and hesitated, Bogomil would tell you to do it! People are more welcoming than you might think.
Tools and Advice
Bogomil still uses Pontoon for Thunderbird desktop translations, in addition to Weblate, where Thunderbird for Android and many other open source projects live. In addition to this localization platforms, he uses CoEdIt along with web-based tools that allow him to see how certain words were previously translated. And of course, he has three release channels of Thunderbird on his computer at all times, something most of the Thunderbird team understands!
From newer contributors, Bogomil has learned how language is changing and how to keep terms, like the Bulgarian word for "browser," up to date, in addition to other feedback. His advice to new and potential volunteers is to be open to critique. This is especially true joining a project with a long history like Thunderbird, especially before wanting to change something. And he says don't be afraid to do things and experiment, even if things go wrong.
Two weeks ago we announced that we had identified and fixed an unprecedented number of latent security bugs in Firefox with the help of Claude Mythos Preview and other AI models. In this post, we'll go into more detail about how we approached this work, what we found, and advice for other projects on making good use of emerging capabilities to harden themselves against attack.
Suddenly, the bugs are very good
Just a few months ago, AI-generated security bug reports to open source projects were mostly known for being unwanted slop. Dealing with reports that look plausibly correct but are wrong imposes an asymmetric cost on project maintainers: it's cheap and easy to prompt an LLM to find a "problem" in code, but slow and expensive to respond to it.
It is difficult to overstate how much this dynamic changed for us over a few short months. This was due to a combination of two main factors. First, the models got a lot more capable. Second, we dramatically improved our techniques for harnessing these models - steering them, scaling them, and stacking them to generate large amounts of signal and filter out the noise.
Ordinarily we keep detailed bug reports private for several months after shipping fixes and issuing security advisories, largely as a precaution to protect any users who, for whatever reason, were slow to update to the latest version of Firefox. Given the extraordinary level of interest in this topic and the urgency of action needed throughout the software ecosystem, we've made the calculated decision to unhide a small sample of the reports behind the fixes we recently shipped. We've attempted to draw them from a range of browser subsystems, but the selection process was still somewhat arbitrary. Nevertheless, we hope that the depth and diversity of these reports lends credence to our assessment of the capabilities and our calls for defenders to begin applying these techniques:
An incorrect equality check can cause the JIT to optimize away the initialization of a live WebAssembly GC struct, creating a fake-object primitive with potential arbitrary read/write in code that had undergone extensive fuzzing by internal and external researchers.
A 15-year-old bug in the <legend> element triggered by meticulous orchestration of edge cases across distant parts of the browser, including recursion stack depth limits, expando properties, and cycle collection.
Reliably exploits a race condition over IPC, allowing a compromised content process to manipulate IndexedDB refcounts in the parent to trigger a UAF and potential sandbox escape.
A raw NaN crossing an IPC boundary can masquerade as a tagged JS object pointer, turning double deserialization into a parent-process fake-object primitive for a sandbox escape.
An intricate testcase weaving through nested event loops, pagehide listeners, and garbage collection to trigger a UAF in the attribute setter for <object> elements.
Triggers a parent UAF by flooding WebTransport with thousands of certificate hashes to stretch a race condition in a refcount-heavy copy loop, and exploits that race condition over IPC from a compromised content process.
Simulates a malicious DNS server by intercepting glibc DNS function calls in order to reproduce a UDP->TCP fallback edge case, triggering a buffer over-read and parent-process stack memory leak during HTTPS RR & ECH parsing.
20-year-old XSLT bug in which reentrant key() calls cause a hash table rehash that frees its backing store while a raw entry pointer is still in use (one of several sec-high issues we fixed involving XSLT).
Patches the color picker to simulate otherwise non-automatable user selection, then uses a synchronous input event to spin a nested event loop that re-enters actor teardown and frees the callback while it is still unwinding, triggering a content process UAF.
A compromised content process could send an arbitrary wallpaper image to be decoded in the parent process, which could be paired with a hypothetical vulnerability in an image decoder to escape the sandbox. This entailed difficult-to-automate reasoning about the trust-level of inputs in the parent process.
Escapes our in-process sandboxing technology for third-party libraries (RLBox) by leveraging a gap in the verification logic used to copy values from the untrusted to the trusted side of the sandbox boundary.
Extremely small testcase that exploits the special rowspan=0 semantics in HTML tables by appending >65535 rows to bypass clamping and overflow a 16-bit layout bitfield, which went undetected for years by fuzzers.
Note that a number of these bugs are sandbox escapes, which would need to be combined with other exploits to achieve a full-chain Firefox compromise. These reports presume that the sandboxed process that renders site content has already been compromised with some separate bug, and is now running attacker-controlled machine code attempting to escalate control into the privileged parent process. When crafting a sandbox escape, the model is permitted to patch the Firefox source code, so long as the modified code is restricted to run only in the sandboxed process[1]. Such bugs are notoriously difficult to find with fuzzing, and while we've had some success developing new techniques to close this gap, AI analysis provides much more comprehensive coverage of this critical surface.
Just as interesting as what the models found is what they didn't find - not because they didn't try, but because they were unable to circumvent Firefox's layered defenses. For example, in recent years we received several clever reports from security researchers that managed to escape the process sandbox by triggering prototype pollution in the privileged parent process. Rather than fixing these problems one-by-one, we made an architectural change to freeze these prototypes by default. While auditing logs from the harness, we saw many attempts to pursue this line of escape that were thwarted by this design. Observing such direct payoff from previous hardening work was even more rewarding than finding and fixing more bugs.
Harnessing Models to Build a Hardening Pipeline
We've experimented internally with LLM code audits over the past few years, with early attempts using models like GPT 4 or Sonnet 3.5 to statically analyze high risk code for vulnerabilities. These experiments showed some promise, but the high rate of false positives made them impractical to scale.
The introduction of agentic harnesses that can reliably detect security issues has completely changed this. These can find real bugs and dismiss unreproducible speculation. The key feature of such a harness is that, given the right interfaces and instructions, it can create and run reproducible test cases to dynamically test hypotheses about bugs in code. After fixing the initial set of issues that Anthropic sent to us in February, we built our own harness atop our existing fuzzing infrastructure.
We began with small-scale experiments prompting the harness to look for sandbox escapes with Claude Opus 4.6. Even with this model, we identified an impressive amount of previously-unknown vulnerabilities which required complex reasoning over multiprocess browser engine code. At first, we supervised the process in the terminal to observe the process in real-time and tune the prompts and logic. Once this was working well, we parallelized the jobs across multiple ephemeral VMs, each tasked to hunt for bugs within a specific target file and write its findings back to a bucket.
A discovery subsystem is necessary but not sufficient. In order to scale the effort, we needed to integrate it with our full security bug lifecycle: determining what to look for, where to look, and how to handle what it produces. This last part includes deduplicating against known issues, tracking bugs, triaging them, and getting fixes shipped. While the model is the core primitive powering the harness, this full pipeline is necessary to make it useful at scale.
While harnesses may be reusable across projects, this pipeline is inherently project-specific, reflecting each codebase's semantics, tooling, and processes. Standing this up required significant iteration, with a tight feedback loop alongside the Firefox engineers who were fielding the incoming bugs.
Upgrading the Models
Once the end-to-end pipeline is in place, it's trivial to swap in different models when they become available. Building this pipeline early helped us find a number of serious bugs using publicly-available models, and it also helped us hit the ground running when we had the opportunity to evaluate Claude Mythos Preview. In our experience, model upgrades increase the effectiveness of the entire pipeline: the system gets simultaneously better at finding potential bugs, creating proof-of-concept test cases to demonstrate them, and articulating their pathology and impact.
In addition to fixing the 271 bugs identified by Claude Mythos Preview in the 150 release, we've shipped more of these fixes in 149.0.2, 150.0.1, and 150.0.2. We also continue to find bugs with other means internally, and, similar to other projects, we've seen a significant uptick in external reports in the last few months.
Ultimately, every bug requires care and attention to properly fix. Staying on top of this unprecedented volume has led to a lot of work and long days over the last few months, and we're extremely proud of how the team has stepped up to meet this challenge. Over 100 people contributed code to this effort to ship the most secure Firefox yet. In addition to writing and reviewing patches, others have been building and scaling this pipeline, triaging, testing the fixes, and managing the release process for each bug.
Takeaways
Anyone building software can start using a harness with a modern model to find bugs and harden their code today. We recommend getting started now. You will find bugs, and you will set yourself up to take advantage of new models as soon as they become available.
You can start with very simple prompting, then observe and iterate. Our initial prompts were not dissimilar from those described here. Through iteration we've built out a lot of orchestration and tooling to optimize and scale the pipeline, but the essence of the inner loop remains the same: there is a bug in this part of the code, please find it and build a testcase.
We haven't bottomed on all the latent bugs in Firefox, but are quite pleased with the trajectory. Today, our scanning is largely focused on specific areas of the code (files, functions) where we instruct the system to look, based on a mix of human judgement and automated signals. In the near future, we intend to integrate this analysis into our continuous integration system to scan patches as they land in the tree. Models are quite flexible with the form of context provided, and we expect patch-based scanning to work as well or even better than file-based scanning.
The current moment is a perilous one, but also full of opportunity. Let's work together to secure the internet.
FAQ
The announcement said "271 bugs", but I count something different. What's going on?
On the advisories web page we group all internally-reported bugs as "rollup" CVEs with multiple bugs underneath them. The web page is built from yaml in the foundation-security-advisories repo, the canonical location for our CVE assignments. While some browsers do not create CVE identifiers for internally-discovered issues at all, we provide this information in order to be as transparent as possible.
In Firefox 150, there were three internal rollups: CVE-2026-6784 (154 bugs), CVE-2026-6785 (55 bugs), and CVE-2026-6786 (107 bugs).
Astute readers will notice the number of bugs in those internal rollups adds up to 316, which is more than the 271 we announced finding with Claude Mythos Preview. That's because our security team hunts for new bugs every day by attacking Firefox with a combination of (a) fuzzing systems (b) manual inspection and (c) this new agentic pipeline across a variety of models.
We fixed a total of 423 security bugs in releases in April. In addition to the 271 bugs announced two weeks ago, there were 41 externally reported bugs, with the remaining 111 discovered internally and split roughly in third between:
Bugs found using this pipeline with Claude Mythos Preview but fixed in releases other than Firefox 150
Bugs found using this pipeline with other models
Bugs found with other techniques like fuzzing
Note that we also directly credited 3 CVEs to Anthropic separate from this latest effort (CVE-2026-6746, CVE-2026-6757, CVE-2026-6758). These were fixes for bugs sent to us by the outstanding Anthropic Frontier Red team a couple months ago and we assigned unique CVEs for each as per our normal process.
What do security ratings mean?
As additional context, we apply security severity ratings from critical to low to indicate the urgency of a bug:
sec-critical and sec-high are assigned to vulnerabilities that can be triggered with normal user behavior, like browsing to a web page. We make no technical difference between these, but sec-critical bugs are reserved for issues that are publicly disclosed or known to be exploited in the wild.
sec-moderate is assigned to vulnerabilities that would otherwise be rated sec-high but require unusual and complex steps from the victim.
sec-low is assigned to bugs that are annoying but far from causing user harm (e.g, a safe crash).
Of the 271 bugs we announced for Firefox 150: 180 were sec-high, 80 were sec-moderate, and 11 were sec-low.
While we care most about critical/high bugs, it's normal for us to prioritize moderate and low security bugs in order to fix correctness issues and as a defense-in-depth mechanism.
Is a sec-high or sec-critical bug the same as a practical exploit?
Not necessarily.
In most cases, a single critical/high bug is not actually enough to compromise Firefox. This is because Firefox has a defense-in-depth architecture, so for example exploiting a JIT bug only achieves remote code execution in a sandboxed and site-specific process. Real-world attackers generally need to chain multiple exploits together to escalate privileges through one or more layers of sandboxing along with OS-level mitigations like ASLR.
We also generally don't build exploits to see whether a bug could be used by an attacker in the real world. We classify sec-high based on predictable crash symptoms such as use-after-free or out-of-bounds memory issues being reported by AddressSanitizer, and our threat model assumes that any of them could be exploitable with sufficient effort. This reduces the risk of a false negative during exploitability analysis, and more importantly it allows us to focus our resources on finding and fixing more vulnerabilities.