Mojo nightly
This version is still a work in progress.
Language enhancements
-
Mojo now support
==and!=for type equality check, and_type_is_eqis removed. -
Mojo now infers
TraitforTypeList.ofsuch thatcomptime TL = TypeList.of[Int, Bool]# works withoutcomptime TL = TypeList.of[Trait = AnyType, Int, Bool] -
Mojo now warns about redundant trait composition
# Warning: Redundant trait composition: 'Copyable' already implies 'AnyType'comptime T : AnyType & Copyable = xxx -
Keyword variadic arguments can now be forwarded to another function that takes keyword variadics, using Python style
**syntax:def takes_them(**kwargs: Int): ...def pass_them(**kwargs: Int):takes_them(**kwargs^) -
Struct fields are no longer allowed to hide
UnsafeAnyOriginwithin a struct, e.g. this is no longer accepted:struct Example:# error: cannot use UnsafeAnyOrigin in a struct field.var ptr: UnsafePointer[Int, MutUnsafeAnyOrigin]This is because Mojo doesn't know that uses of
Examplecontain anUnsafeAnyOriginand therefore doesn't do lifetime extension for values in its context. The typical solution for this is to add anOriginparameter but you can also useUntrackedOriginif you explicitly manage the lifetime of the underlying data:struct Example[origin: Origin]:var ptr: UnsafePointer[Int, Self.origin]# ORstruct Example:var ptr: UnsafePointer[Int, MutUntrackedOrigin]As a temporary workaround, you can decorate fields with
@__allow_legacy_any_origin_fieldsto ignore the compiler error, however this decorator is not stable and will eventually be removed. -
Import resolution behavior has been made consistent. When resolving an import of a module or package, in any given directory the resolution in order of preference is: source packages; precompiled
.mojocfiles; source modules; legacy precompiled.mojopkgfiles.Previously the behavior was unspecified and would pick whichever matching name it found in the directory first.
Language changes
-
Relative imports must now use
from(from . import foo); theimport .fooform is no longer accepted. -
Absolute imports
import a.b.cnow bind all ofa,a.b, anda.b.cinto the scope, where previously onlya.b.cwas made available. -
A bug in import handling has been fixed where absolute imports of a package followed by an import of one of its submodules no longer result in a compiler error.
import aimport a.b # fixed; was: "invalid redefinition of 'a'" -
A bug in function-scoped imports has been fixed, allowing dotted imports:
def foo():import a.ba.b.foo() # fixed; was: "use of unknown declaration 'a'"Note that this was already working correctly for other forms of import (
import a,from a import b,from a.b import c, etc). -
An imported package's submodules are now only accessible when the package's
__init__.mojore-exports those submodules.import pkg# only ok if pkg/__init__.mojo re-exports 'sub'.# Re-export submodules with, e.g.,# from . import sub# Use relative imports to avoid importing system packages.pkg.sub.foo()Note that absolute imports can always bring in that submodule, bypassing the
__init__.mojo:# always ok, regardless of the package's __init__.mojoimport pkg.submodulepkg.submodule.foo() -
whereclauses inside a parameter list (for example,[x: Int where x > 0]) are no longer supported, following a period of deprecation. Use a trailingwhereclause after the signature instead:# Old (no longer supported):# fn foo[x: Int where x > 0]():# New:fn foo[x: Int]() where x > 0:pass
Library changes
-
Intis now an alias forScalar[DType.int]and integer literals materialize to thisScalartype. Because of this some conversions have become more strict.A new
SIMDSizetype has been added for the width ofSIMDitself and must be used when inferring a parameter based on a SIMD argument like so:def frob[w: SIMDSize](v: SIMD[DType.int, w]): ...Alternatively the width can be unbound if you simply want to be parametric over any
SIMDtype:def frob(v: SIMD[DType.int, _])The new
Intshould still be used in all other situations. -
ImplicitlyDestructiblehas been renamed toImplicitlyDeletable, for better name consistency with its required__del__()"delete" special method. -
The
Reflected.field_type[name]reflection member has been renamed toReflected.field[name], because it returns a chainableReflectedhandle for the named field rather than the field's bare type, so the old name was not accurate. Retrieve the field's type from the handle's.Tmember, as inreflect[T].field["x"].T. Update call sites such asreflect[T].field_type["x"]toreflect[T].field["x"]. -
Several collection types now conditionally conform to
ImplicitlyDeletable, conforming only when their element type does. This lets a collection hold non-ImplicitlyDeletableelements at all (previously such a collection failed to compile); a collection of non-deletable elements is itself linear and must be drained explicitly with the newdestroy_with()method, which calls a closure on each element:collection^.destroy_with(my_destroy_closure)Generic code that takes one of these collections by value may now need
& ImplicitlyDeletableadded to its element bound so the collection can be dropped:def foo[T: Movable & ImplicitlyDeletable, //](var arr: InlineArray[T, 3]):passAffected types:
InlineArray[ElementType, size].Deque[ElementType]- Element-destroying operations (
append,appendleft,extend,extendleft,insert,clear,remove, etc.) still requireElementTypeto beImplicitlyDeletable. - Consuming iteration (
for x in deque^, theIterableOwnedconformance) is likewise conditional, requiringElementTypeto beImplicitlyDeletable; generic code bounded onIterableOwnednow rejects a non-conforming element type at the bound rather than failing later inside__iter__(). For deletable element types (the common case) this is transparent.
- Element-destroying operations (
-
Is is now possible to iterate over owned elements in
List,Dict,InlineArray,LinkedList, andSetwhen the element type is notCopyable:def iterate[T: Movable](var list: List[T]):# Consume elementsfor var x in list^:passThe
IterableOwnedconformance on several collections is now conditional on the element type conforming toMovable & ImplicitlyDeletable, droppingCopyable.Additionally, generic code bounded on
IterableOwnednow rejects a collection of non-conforming elements at the bound, rather than failing later inside__iter__(). -
The implicit conversion constructors that cast an
UnsafePointertoMutUnsafeAnyOriginorImmutUnsafeAnyOriginare now deprecated and emit a deprecation warning when used.UnsafeAnyOriginis an unsafe escape hatch that silently extends unrelated lifetimes and disables exclusivity checking, so it should never be applied implicitly. Prefer keeping a concrete origin; if you must discard it, make the cast explicit with theas_unsafe_any_origin()method. -
Added
reflect[T].field_at[idx]to the reflection API, the by-index dual ofreflect[T].field[name]. It returns the reflection handle for the type of the field atidx, so a field's concrete type can be recovered while iterating fields by index (where the name is not available as a literal):comptime y_type = reflect[Point].field_at[1]var v: y_type.T = 3.14 # y_type.T is the concrete field type -
Removed the implicit constructors that converted an
UnsafePointerinto anOptional[UnsafePointer[..., UnsafeAnyOrigin]]. Constructing anOptional[UnsafePointer]now preserves the pointer's real origin instead of silently widening it toUnsafeAnyOrigin. Two call-site updates may be needed:-
Passing a concrete pointer where the parameter's origin is a genuinely fixed
MutAnyOrigin/ImmutAnyOrigin(typically C-FFI signatures) now requires an explicitas_unsafe_any_origin(). -
Because origins are now preserved, exclusivity checking applies to
memcpy()(and similar) calls whosedestandsrcderive from the same buffer. An intra-buffer copy that previously compiled now errors with "argument of 'memcpy' call allows writing a memory location previously writable through another aliased argument". Opt out by making one argument an unsafe any-origin (the non-overlap ofdestandsrcis already amemcpy()precondition):memcpy(dest=buf + dst_off,src=(buf + src_off).as_unsafe_any_origin(),count=n,)
-
-
The traits
ImplicitlyDeletable,Movable,Copyable, andImplicitlyCopyableare now stable. -
Removed
trait_downcast_var(). Improvements to type refinement based onwhere conforms_to(..)andcomptime assert conforms_to(..)make explicit value trait downcasting no longer necessary. -
Added
raise_python_exception()tostd.python.bindings, which translates a MojoErrorinto a Python exception viaPyErr_SetStringand returns a nullPyObjectPtr. -
Iterating over a
String,StringSlice, orStringLiteralnow yields grapheme clusters by default. Their__iter__()and__reversed__()methods return aGraphemeSliceIter, sofor c in my_string:produces what a user perceives as a single "character" on screen. The lower-level views remain available when you want them:codepoints()orcodepoint_slices()for Unicode scalars, andbytes()for raw UTF-8 bytes.
Tooling changes
- Added a
--lld-pathCLI flag. This overrides the LLD path that Mojo uses.
GPU programming
-
DeviceContext.load_functionnow keys its runtime cache on the requested entry-point name as well as the blob. Loading two different entry points (for examplekernel_aandkernel_b) from a single PTX/cubin blob no longer collides — previously the second load silently returned the function resolved by the first. The cache also no longer keys on the entire blob when no module name is supplied: it keys on a short hash of the blob instead, so each call avoids copying, hashing, and byte-comparing the whole blob (and retaining a duplicate of it). The win scales with blob size and matters most for large multi-entry blobs loaded on the per-execution path. -
The
DeviceStreamtype is now included in the API reference documentation. Returned byDeviceContext.create_stream()andDeviceContext.create_external_stream(), it provides methods for synchronizing and sequencing asynchronous GPU work (for example,synchronize(),record_event(), andenqueue_wait_for()). The type was already public but was previously hidden from the generated docs. -
Added an 8x8
simdgroup_matrixmatrix multiply-accumulate primitive (_mma_apple_8x8()) withapple_mma_load_8x8()/apple_mma_store_8x8()fragment helpers for Apple Silicon GPUs instd.gpu.compute.arch. Unlike the 16x16 path (Apple M5 only), the 8x8 primitive is available on all Apple GPU generations (M1-M5). It acceptsFloat16,BFloat16, andFloat32inputs with aFloat32accumulator. -
Apple M5
simdgroup_matrixMMA now accepts FP8 (float8_e4m3fn,float8_e5m2) inputs with an F32 accumulator, alongside the existing F16/BF16/F32 and 8-bit integer types. -
Added
warp.match_any(), which returns, for each warp lane, the mask of lanes whose value has the same bits. It uses NVIDIA'smatch.any.syncinstruction, areadfirstlaneballot fold on AMD, and a shuffle-based emulation on Apple Silicon GPUs. -
Added
warp.match_all(), which returns the warp's active-lane mask if every lane holds the same bits and 0 otherwise. It uses NVIDIA'smatch.all.syncinstruction, areadfirstlaneballot fold on AMD, and a shuffle-based check on Apple Silicon GPUs. -
DeviceGraphBuilder.collect_dependenciesnow accepts an optionaldependenciesargument. The named predecessor handles are injected as ambient predecessors of every node theworkclosure adds, so the scope's nodes run after those predecessors without the closure threading the handles through to eachadd_*call. With the default (empty)dependenciesthe behavior is unchanged. Whenworkadds no nodes, the returned join node falls back to depending ondependenciesso it still chains correctly.var producers = builder.collect_dependencies(add_producers)# Every node added by `add_consumers` depends on `producers`:var consumers = builder.collect_dependencies(add_consumers, dependencies=[producers]) -
Added a
DeviceGraphBuilder.add_functionoverload that takes the kernel as a compile-time parameter and compiles it automatically, mirroring the parameter-basedDeviceContext.enqueue_function. Callers no longer need a separateDeviceContext.compile_functionstep to add a kernel node:def build(mut builder: DeviceGraphBuilder) raises {read}:_ = builder.add_function[kernel](42, grid_dim=1, block_dim=1, dependencies=[]) -
AddressSpaceis now target-extensible rather than a fixed, portable enum. The built-in GPU spaces (GENERIC,GLOBAL,SHARED,CONSTANT,LOCAL,SHARED_CLUSTER,BUFFER_RESOURCE) are unchanged, but accessing any other name — for example an accelerator-specificAddressSpace.SCRATCHPAD— now resolves through the active hardware backend instead of being a hard-coded compile error. The set of valid address-space names is the union of the built-in GPU spaces and whatever the active backend defines, so accelerator backends can provide their own named spaces (with their own values) only where they exist. A name that no backend defines remains a compile-time error. -
Added support for the Steam Deck's RDNA2 Van Gogh APU.
Removed
-
Removed the
store_volatile()andload_volatile()intrinsics fromstd.gpu.intrinsics. UseUnsafePointer.store[volatile=True]()andUnsafePointer.load[volatile=True]()instead, which work across all supported GPU targets rather than NVIDIA only. -
Removed the deprecated
GPUAddressSpacealias forAddressSpace. UseAddressSpacedirectly.
Fixed
-
A
comptimemember with a trailingwhereclause is now accepted as a witness for a conditional trait conformance when the conformance constraint implies the member's constraint, for example:trait StaticSize:comptime SIZE: Intstruct Foo[size: Int = -1](StaticSize where size >= 0):comptime SIZE: Int where Self.size >= 0 = Self.size