This version is still a work in progress.
Language enhancements
-
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]
# OR
struct 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.
Language changes
- Relative imports must now use
from(from . import foo); theimport .fooform is no longer accepted.
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]):
pass
Affected 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 (
-
The
IterableOwnedconformance onListandInlineArray(consuming iteration viafor x in collection^) is now conditional, requiring the element type to beMovable & ImplicitlyDeletable. Consuming iteration moves elements out of the collection rather than copying them, so it no longer requiresCopyable. Generic code bounded onIterableOwnednow 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. -
The traits
ImplicitlyDeletable,Movable,Copyable, andImplicitlyCopyableare now stable. -
Added
raise_python_exception()tostd.python.bindings, which translates a MojoErrorinto a Python exception viaPyErr_SetStringand returns a nullPyObjectPtr.
Tooling changes
- Added a
--lld-pathCLI flag. This overrides the LLD path that Mojo uses.
GPU programming
-
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]
)