Nightly: v0.26.3
This version is still a work in progress.
✨ Highlights
Documentation
Language enhancements
-
The ternary
if/elseexpression now coerces each element to its contextual type when it is obvious. For example, this works instead of producing an error about incompatible metatypes:comptime some_type: Movable = Int if cond else String -
Unified closures now accept default capturing conventions when there are explicit captures already.
def captures_with_default_convention(): var a, b, c, d = ("a", "b", "c", "d") def my_fn() unified {mut a, b, c^, read}: # capture: # `a` by mut reference # `b` by immut reference # `c` by moving # `d` by immut reference (the default 'read' convention) use(a, b, c, d) -
Added
abi("C")as a function effect for declaring C calling convention on function definitions and function pointer types. Functions marked withabi("C")use the platform C ABI (System V x86-64 / ARM64 AAPCS) for struct arguments and return values, enabling safe interop with C libraries:# C-ABI function definition (safe as a callback into C code) def add(a: Int32, b: Int32) abi("C") -> Int32: return a + b # C-ABI function pointer type (safe for use with DLHandle.get_function) var f = handle.get_function[def(Float64) abi("C") -> Float64]("sqrt")DLHandle.get_function[]now enforces that the type parameter carriesabi("C"), preventing silent ABI mismatches when loading C symbols. -
String literals now support
\uXXXXand\UXXXXXXXXunicode escape sequences, matching Python. The resulting code point is stored as UTF-8. Invalid code points and surrogates are rejected at parse time. -
Added support for conditional
RegisterPassableconformance. -
Variadic lists and packs can be forwarded through runtime calls with
*packwhen the callee takes a compatible variadic list/pack.def callee[*Ts: Writable](*args: *Ts): comptime for i in range(args.__len__()): print(args[i]) def forwarder[*Ts: Writable](*args: *Ts): callee(*args) forwarder(1, "hello", 3.14) # prints each value on a separate line -
Heterogenous variadic packs can now be specified with a
SomeTypehelper function. These two are equivalent:def foo[*arg_types: Copyable](*args: *arg_types) -> Int: ... def foo(*args: *SomeTypeList[Copyable]) -> Int: ...
Language changes
-
Variadic parameters lists are now passed instead of
ParameterListandTypeListinstead of!kgen.param_list. This makes it much more ergonomic to work with these types, e.g. simple logic just works:def callee[*values: Int](): var v = 0 for i in range(len(values)): v += values[i] for elt in values: v += eltSimilarly, the
ParameterList/TypeListstructs have other methods for transforming the value list. As such, a variety of values from theVariadicstruct have started moving over to being members of these types. -
All Mojo functions now has a unique "function literal type". In practice, it means that:
# type_of(foo) != type_of(bar) def foo(): pass def bar(): pass -
Mojo now warns on uses of the legacy
fnkeyword. Please move todefas this will upgrade to an error in the future. -
Import statements of the form
from pkg import ...no longer makepkgavailable to the module.
Library changes
-
Atomic operations have moved to a dedicated
std.atomicmodule. TheConsistencytype has been renamed toOrderingand itsMONOTONICmember has been renamed toRELAXEDto align with conventions used by other languages. Update existing code as follows:# Before from std.os import Atomic from std.os.atomic import Atomic, Consistency, fence _ = atom.load[ordering=Consistency.MONOTONIC]() # After from std.atomic import Atomic, Ordering, fence _ = atom.load[ordering=Ordering.RELAXED]() -
assert_raisesnow catches customWritableerror types, not justError. -
Variadics of types have been moved to the
TypeListstruct. One can write operations such as:comptime assert TypeList[Trait=AnyType, Int, String]().contains[Bool] -
abort(message)now includes the call site location in its output. The location is automatically captured and printed alongside the message. You can also pass an explicitSourceLocationto override it:abort("something went wrong") # prints: ABORT: path/to/file.mojo:42:5: something went wrong var loc = current_location() abort("something went wrong", location=loc) -
abort(message)now prints its message on Nvidia and AMDGPU, including block and thread IDs. Previously, the message was silently suppressed on these GPUs. On Apple GPU, the message is silently suppressed for now. -
SourceLocationfields (line,col,file_name) are now private. Use the new accessor methodsline(),column(), andfile_name()instead. -
Fixed default alignment in
TileTensor.load()andTileTensor.store()to use the caller-specifiedwidthparameter instead ofSelf.element_size. -
Added uninitialized memory read detection for float loads. When compiled with
-D MOJO_STDLIB_SIMD_UNINIT_CHECK=true, every float load is checked against the debug allocator's poison patterns (0xFF host fill and canonical qNaN device fill). A match triggersabort()with a descriptive message. When disabled (the default), zero runtime overhead. For MAX pipelines, setMODULAR_MAX_UNINITIALIZED_READ_CHECK=trueto enable both the debug allocator and the load-time checks automatically. -
Added
CompilationTarget.is_apple_m5()tostd.sysfor detecting Apple M5 targets at compile time.is_apple_silicon()now includes M5 in its check. -
Added Apple M5 MMA intrinsics (
apple_mma_load,apple_mma_store,_mma_apple) instd.gpu.compute.arch.mma_apple, enabling hardware matrix multiply-accumulate on Apple GPU. -
Standard library types now use conditional conformances, replacing previous
_constrained_conforms_tochecks:Span:Writable,HashableTuple,Optional,Variant, andUnsafeMaybeUninit:RegisterPassableVariant:Copyable,ImplicitlyCopyable
-
Tuplenow conditionally conforms toDefaultable, so genericT: Defaultablecode can default-construct tuples when all element types areDefaultable. -
OwnedDLHandle.get_symbol()now returnsOptional[UnsafePointer[...]]instead of aborting when a symbol is not found. This allows callers to handle missing symbols gracefully. -
GPU primitive id accessors (e.g.
thread_idx) have migrated fromUInttoInt.This is part of a broader migration to standardize on the
Inttype for all sizes and offsets in Mojo.To provide a gradual migration path, explicitly typed aliases are available temporarily.
Base UIntAccessorIntAccessorthread_idxthread_idx_uintthread_idx_intthread_dimthread_dim_uintthread_dim_intblock_dimblock_dim_uintblock_dim_intgrid_dimgrid_dim_uintgrid_dim_intglobal_idxglobal_idx_uintglobal_idx_intlane_idlane_id_uintlane_id_intwarp_idwarp_id_uintwarp_id_intCode can preserve its prior behavior by using a renaming import of the
thread_idx_uintalias:- from std.gpu import thread_idx + from std.gpu import thread_idx_uint as thread_idxNote that
thread_idx_uintand the other_*uintaliases will eventually be deprecated and removed as well.After a temporary deprecation acting as a "speed bump" in the 2026-03-29 nightly release,
thread_idxetc. have changed fromUInttoInt.Code built with a version where
thread_idxis stillUInt, can proactively migrate to the eventualIntbehavior using thethread_idx_intalias:- from std.gpu import thread_idx + from std.gpu import thread_idx_int as thread_idx # ... update file to reflect change from `UInt` to `Int` ... -
Added
IterableOwnedtrait to the iteration module. Types conforming toIterableOwnedimplement__iter__(var self), which consumes the collection and returns an iterator that owns the underlying elements.Listnow conforms toIterableOwned.Optionalnow conforms toIterableOwned.Dequenow conforms toIterableOwned.LinkedListnow conforms toIterableOwned.Dictnow conforms toIterableOwned.Setnow conforms toIterableOwned.Counternow conforms toIterableOwned.InlineArraynow conforms toIterableOwned.Spannow conforms toIterableOwned(conditional onT: Copyable). The owned iterator yields copies of elements by value.- Iterator adaptors (
enumerate,zip,map,peekable,take_while,drop_while,product,cycle,count,repeat) now conform toIterableOwned. - Added owned overloads of
enumerate(),zip(),map(),peekable(),take_while(),drop_while(),product(), andcycle()that consume the input iterable.
-
CStringSlicecan no longer represent a null pointer. To represent nullability useOptional[CStringSlice]which is guaranteed to have the same size and layout asconst char*, whereNULLis the emptyOptional. -
external_call'sreturn_type's requirements has been relaxed fromTrivialRegisterPassabletoRegisterPassable. -
Negative indexing on all stdlib collections has been removed to enable cheap CPU bounds checks by default:
ListSpanInlineArrayStringStringSliceLinkedListDequeIntTuple
Using a negative
IntLiteralfor indexing will now trigger a compile-time error, for example:/tmp/main.mojo:3:12: note: call expansion failed with parameter value(s): (..., ...) print(x[-1]) ^ constraint failed: negative indexing is not supported, use e.g. `x[len(x) - 1]` insteadUpdate any
x[-1]tox[len(x) - 1], following the compiler errors to your call sites as above.This does not affect any MAX ops that support negative indexing.
-
Bounds checking is now on by default for all collections on CPU, and will show you the call site in your code where you triggered the out of bounds access:
def main(): var x = [1, 2, 3] print(x[3])At: /tmp/main.mojo:3:12: Assert Error: index 3 is out of bounds, valid range is 0 to 2Bounds checking is still off by default for GPU to avoid performance penalties. To enable it for tests:
mojo build -D ASSERT=all main.mojoTo turn off all asserts, including CPU bounds checking:
mojo build -D ASSERT=none main.mojo -
alloc[T](count, alignment)will nowabortif the underlying allocation failed. -
Added
Variadic.contains_valuecomptime alias to check whether a variadic sequence contains a specific value at compile time. -
ArcPointernow conditionally conforms toHashableandEquatablewhen its inner typeTdoes. Both__eq__and__hash__delegate to the managed value, matching C++shared_ptrand RustArcsemantics. This makesArcPointerusable as aDictkey orSetelement with value-based equality. Pointer identity is still available via theisoperator. -
Pathnow conforms toComparable, enabling lexicographic ordering and use withsort(). -
range()overloads that took differently-typed arguments or arguments that wereIntable/IntableRaisingbut notIndexerhave been removed. Callers should ensure they're passing consistent integral argument types when callingrange(). -
Consistencynow has a default constructor that selectsRELEASEordering on Apple GPU andSEQUENTIALon all other targets. AllAtomicmethods andfenceuse this platform-aware default instead of hard-codingSEQUENTIAL. -
NDBufferhas been fully removed. Please migrate toTileTensor. -
Added a generic
__contains__method toSpanfor any element type conforming toEquatable, not justScalartypes. -
Fixed
blocked_productintile_layoutto zip block and tiler dimensions per mode, matching the legacyblocked_productbehavior. -
Added
Span-based overloads forenqueue_copy,enqueue_copy_from, andenqueue_copy_toonDeviceContext,DeviceBuffer, andHostBuffer, providing a safer alternative to rawUnsafePointerfor host-device memory transfers. -
String.__len__()has been deprecated. Prefer to useString.byte_length()orString.count_codepoints(). -
Added
map()andand_then()methods toOptional.map()transforms the contained value by applying a function, returningOptional[To].and_then()chains operations that themselves return anOptional, enabling flat-mapping over fallible computations.var o = Optional[Int](42) def closure(n: Int) unified {} -> String: return String(n + 1) var mapped: Optional[String] = o.map[To=String](closure) print(mapped) # Optional("43") -
parallelize,parallelize_over_rows(instd.algorithm.backend.cpu.parallelize) and theelementwiseoverloads instd.algorithm.functionalnow accept an optional trailingctx: Optional[DeviceContext] = Noneparameter. When supplied, the provided CPUDeviceContextis forwarded tosync_parallelizeso that parallel work runs on that context; when omitted, the previous behavior is preserved. This is a step toward running CPU ops on specific NUMA nodes.
Tooling changes
-
The Mojo debugger now displays scalar types (e.g.
UInt8,Float32) as plain values instead of([0] = value), and elides internal_mlir_valuewrapper fields from struct display. -
mojo formatno longer supports the deprecatedfnkeyword, nor the removedownedargument convention. -
Comptime function calls now print more nicely in error messages and generated documentation, not including
VariadicList/VariadicPackand including keyword argument labels when required.
GPU programming
- Added support for AMD MI250X accelerators.
❌ Removed
-
The
escapingfunction effect is no longer supported. Migratedef(...) escaping -> Tclosures tounifiedclosures. -
The deprecated
@doc_privatedecorator has been removed. Use@doc_hiddeninstead. -
Removed the
store_release,store_relaxed,load_acquire, andload_relaxedhelpers fromstd.gpu.intrinsics. UseAtomic[dtype, scope=...].storeandAtomic[dtype, scope=...].loadwith the desiredOrderinginstead:# Before from std.gpu.intrinsics import store_release, load_acquire store_release[scope=Scope.GPU](ptr, value) var v = load_acquire[scope=Scope.GPU](ptr) # After from std.atomic import Atomic, Ordering Atomic[dtype, scope="device"].store[ordering=Ordering.RELEASE](ptr, value) var v = Atomic[dtype, scope="device"].load[ordering=Ordering.ACQUIRE](ptr)
🛠️ Fixed
-
Fixed
mojoaborting at startup withstd::filesystem::filesystem_errorwhen$HOMEis not traversable by the running UID (common in containerized CI where the image's build-time UID differs from the runtime UID). The config search now treats permission errors as "not found" and falls through to the next candidate. (Issue #6412) -
Fixed
libpythonauto-discovery failing for Python 3.14 free-threaded builds. The discovery script constructed the library filename without the ABI flags suffix (e.g. looked forlibpython3.14.dylibinstead oflibpython3.14t.dylib). (Issue #6366) -
Fixed
RTLD.LOCALhaving the wrong value on Linux. It was set to4(RTLD_NOLOAD) instead of0, causingdlopenwithRTLD.NOW | RTLD.LOCALto fail. (Issue #6410) -
Fixed
mojo formatcrashing after upgrading Mojo versions due to a stale grammar cache. (Issue #6144) -
Fixed
atofproducing incorrect results for floats near the normal/subnormal boundary (e.g.,Float64("4.4501363245856945e-308")returned half the correct value). (#6196) -
Issue #5872: Fixed a compiler crash ("'get_type_name' requires a concrete type") when using default
Writable,Equatable, orHashableimplementations on structs with MLIR-type fields (e.g.__mlir_type.index). The compiler now correctly reports that the field does not implement the required trait. -
Fixed
Atomic.storesilently dropping the requestedscope. The previous implementation lowered toatomicrmw xchgwithout forwardingsyncscope, soAtomic[..., scope="device"].store(...)was emitting a system-scope store on NVPTX (extra L2/NVLink fences) and an over-synchronized store on AMDGPU.Atomic.storenow lowers viapop.store atomic syncscope(...), emittingst.release.<scope>on NVPTX and a properly-scoped LLVM atomic store on AMDGPU. The Mojo API surface is unchanged. -
Fixed
Process.run()not inheriting the parent's environment variables. Child processes spawned viaProcess.run()now correctly receive the parent's environment.
Was this page helpful?
Thank you! We'll create more content like this.
Thank you for helping us improve!