Mojo nightly
This version is still a work in progress.
Language enhancements
-
Added experimental
__match/casepattern matching for early testing, includingcomptime __matchfor compile-time subjects. Patterns include literals, contextual initializer lists such as{}and{1, offset=2}, or-patterns (|), guards (if),as/ bare name bindings, tuples, structs, andEnumLiketypes such asOptional. An initializer-list pattern constructs a value of the subject type and compares it to the subject. Dynamic match also supportsvar/refbindings; comptime match binds names as parameter values and rejectsvar/ref. Nested patterns can dig through several layers in one case—for example, matching an optional point at runtime or specializing a kernel path on a compile-time tile size:def describe(p: Optional[Point]) -> String:__match p:case .Some(Point(x=0, y=0)):return "origin"case .Some(Point(x=var x, y=0)) | .Some(Point(x=0, y=var x)):return String("axis:", x)case .Some(Point(x=x, y=y)) if x == y:return "diagonal"case .None:return "missing"case _:return "unreachable"# comptime match is guaranteed evaluated at compile time. The subject must be# a parameter and the bound names are also parameters. var and ref specifiers# are not allowed in comptime match, because they are not meaningful.def matmul_tile[m: Int, n: Int, k: Int](...):comptime __match (m, n, k):case (16, 16, 16):# Hand-tuned 16³ path....case (m_tile, n_tile, k_tile) if m_tile * n_tile <= 256:# Small-tile path; bound sizes stay available as parameters....case _:# Generic fallback....Both forms warn on non-exhaustive
EnumLikesubjects (includingBooland tuples/structs of such types) and on unreachable or duplicate cases. Open subjects such asIntandStringdiagnose duplicate literal cases but are not required to be exhaustive.EnumLiketypes can now set_enum_is_exhaustivetoFalsewhen their known cases do not cover every possible value. This is useful for extensible domains such as error codes, where an operating system or library can add values that the type does not know about:struct ErrorCode(EnumLike):comptime FileNotFound = Self(2)comptime PermissionDenied = Self(13)comptime OutOfMemory = Self(12)comptime TimedOut = Self(110)comptime Unavailable = Self(503)comptime _enum_is_exhaustive = False# EnumLike metadata and methods omitted....def describe(error: ErrorCode) -> String:__match error:case .FileNotFound:return "file not found"case .PermissionDenied:return "permission denied"case .OutOfMemory:return "out of memory"case .TimedOut:return "operation timed out"case .Unavailable:return "service unavailable"case _:return "unknown error"Open enums preserve separate compilation between libraries and their clients. A library can add a known case without breaking previously compiled client matches when they compile against a new version of the package that adds a case they didn't know about.
-
The message on a
whereclause can now be writtenwhere <condition> else "<message>", as the preferred alternative to the existingwhere (<condition>, "<message>"), which will be deprecated over time.def foo[sc: Int]() where sc > 1 else "scaling factor must be greater than 1":...struct Box[T: Deinitable](Marker where conforms_to(T, Marker) else "Box[T] is a Marker only when T is",):... -
A struct can now opt out of a trait by writing
not <Trait>in its conformance list. It is the preferred alternative to the existing<Trait> where False, which will continue to work:struct Handle(not Movable, Writable):...An opt-out can record why, with the same
else "<message>"spelling awhereclause uses:struct Handle(not Movable else "a Handle is pinned to the port it opened"):...
Language changes
- The
asyncandawaitkeywords are renamed to__asyncand__awaitto mark async support as unstable. Write__async defand__await expr. The unprefixed spellings still work but emit a warning that async is unstable, with a fix-it to the prefixed spelling. The prefixed spelling suppresses the warning but doesn't make async stable.
Library changes
-
The enum-behaving structs across the standard library, MAX kernels, and MAX runtime now conform to
EnumLike, so they support enum-style pattern matching (__matchwithcase .NAME) and reflective case metadata:Scope,FastMathFlag, andTestResultin the standard library;AllReduceAlgorithm,RasterOrder,MatmulSchedule,ReductionMode,GEMMKind,ThreadScope,FP6Format,MXFormat,Backend,AMDIGLPStrategy,MHASchedule,ScatterOobIndexStrategy,SchedulingStrategy,Phase,PhaseAction, andBlockPhasein the kernels; andTraceCategory,TraceLevel,LogLevel,CacheEviction,Fill, andConsistencyin the runtime and driver APIs.FlashAttentionAlgorithmconforms non-exhaustively: its-1"unspecified" value selects no named case, matching its runtime resolution semantics.__match AllReduceAlgorithm.ONE_STAGE:case .ONE_STAGE:launch_1stage()case .TWO_STAGE | .LAMPORT:launch_2stage() -
Tuplegainedfirst_of[T](), which returns a reference to the first element of typeT. The lookup is resolved at compile time, and it is a compile-time error if the tuple has no element of that type:var t = (1, String("two"), 3.0)t.first_of[String]() += "!" -
SIMD.from_bytes()andSIMD.as_bytes()now take anendianparameter of the newEndiantype (Endian.bigorEndian.little) instead of thebig_endianBool:var port = UInt16.from_bytes[endian=.big](bytes) # was: big_endian=TrueThe default is still the target's byte order, which
Endian.native()returns.is_big_endian()andis_little_endian()are deprecated; compareEndian.native()with.bigor.littleinstead. -
Hashing a byte sequence is now spelled
hash_bytes(), which takes anImmSpan[Byte]. The pointer-and-lengthhash()overload is deprecated:var digest = hash_bytes(text.as_bytes()) # was: hash(text.ptr(), text.byte_length())hash()keeps itsHashablemeaning, sohash(my_list)still hashes a list element-by-element whilehash_bytes(my_list)hashes its bytes. -
Coord.product()has a new parameterized overload,product[T: DType](), which accumulates and returns the product atTrather than atCoord.DTYPE:var c = Coord(Idx[4], Int(8), Int32(3))var n = c.product[DType.uint32]() # UInt32(96)The unparameterized
product()is unchanged and still returnsScalar[Coord.DTYPE]. ATtoo narrow to hold the result wraps rather than widening, so a caller that picks one owns the overflow. -
StringSpannow only provides immutable byte access.MutStringSpanandMutStringSlicehave been removed, and themutparameter onStringSpanhas been removed. -
Introduced a new
CompilationTargetpublic type, which is now used in most APIs that are parameterized on compilation target, such assize_of(),align_of(),compile_info(), and more.Convenience accessors are provided for querying the current host and accelerator targets:
-
CompilationTarget.current()— either the current host target, or accelerator target in an offload compilation. -
CompilationTarget.default_accelerator()— the default accelerator target, either determined automatically or as specified by--target-accelerator.
Additionally,
CompilationTargetnow provides predicate methods for checking the vendor identity of a given target. (The preexisting free functions with the same name are now implemented in terms of these new methods.) These include:.is_nvidia_gpu()for checking whether a compilation target is an NVIDIA GPU..is_amd_gpu()and.is_apple_gpu()provide the equivalent checks for AMD and Apple GPUs respectively.
-
-
Added new
TargetAcceleratortype for representing accelerator metadata at compile time, combiningGPUInfoandCompilationTargetvalues.Target constants like
B200orMI355Xhave changed from beingGPUInfoinstances toTargetAccelerator.Use
.gpu_infoto access the prior device metadata, or.targetfor the compilation target. -
Layoutnow carries its alignment as a keyword-only parameter of the newAlignmenttype, instead of storing it as a runtime field. It defaults to the element type's natural alignment, soLayout[T](count=n)is unchanged. Build anAlignmentwithAlignment.of[T]()for a type's natural alignment orAlignment.of_bytes[n]()for an explicit byte count.AllocationandManagedAllocationcarry the same parameter.ThinAllocationdeliberately does not: it records only the element type, so the alignment travels with theLayoutyou supply tounsafe_with_layout.var layout = Layout[Int32, alignment = .of_bytes[64]()](count=8)var thin = alloc(layout).into_thin()dealloc(thin^.unsafe_with_layout(layout))Only compile-time alignments are supported for now. This makes the common case (natural type alignment) simple - so the
LayoutandAllocationtype don't pay the cost of holding an extraIntfield. Dynamic (runtime) alignment will eventually be supported after some more design considerations. -
Coordhas a newreplace_dynamic[at](value)method, the in-place counterpart toreplace. It keeps the coordinate's type, so the element atatmust already be dynamic, andvaluetakes that element's own dtype.var c = Coord(ComptimeInt[3](), Int64(0))c.replace_dynamic[1](Int64(7)) # Coord(ComptimeInt[3](), Int64(7)) -
Spanhas a newunsafe_deinit_elements()method, which destroys every element in place and leaves the memory uninitialized. -
Spanhas new methods for initializing a span ofMaybeUninit[T]elements and viewing the result as a span ofT:unsafe_init_with()initializes each element with the result of calling a function with that element's index.unsafe_init_copy_from()copies from another span.unsafe_init_move_from()moves out of another span.unsafe_assume_init()reinterprets the span as initialized, for memory that was initialized some other way.
-
The default
Writableimplementation now formatsEnumLiketypes by their active case rather than their fields. A case prints asTypeName.case(payload), or asTypeName.casewhen it has no payload (NoneType):print(Shape.circle(3)) # Shape.circle(3)print(repr(Shape.circle(3))) # Shape.circle(Int(3))print(Shape.empty()) # Shape.emptyTypes that implement their own
write_to()orwrite_repr_to(), such asOptionalandBool, are unaffected. -
Tuple.consume_elements(),Tuple.deinit_with(), andVariadicPack.consume_elements()now take the element handler as a runtime closure argument instead of a compile-timecapturingparameter, matchingList.deinit_with()andVariadicList.consume_elements():def handler[idx: Int](var elt: t.Ts[idx]) {mut collected}:collected[idx] = elt.datat^.consume_elements(handler) # was: t^.consume_elements[handler]() -
SIMD.reduce()now takes a closure reduction function as a runtime argument instead of a compile-timecapturingparameter. Thethinfunction-pointer form is unchanged:def add[width: SIMDLength](lhs: SIMD[DType.int32, width], rhs: SIMD[DType.int32, width]) -> SIMD[DType.int32, width]:return lhs + rhsv.reduce(add) # was: v.reduce[add]()v.reduce[2](add) # was: v.reduce[add, 2]() -
The TMA descriptor types
TMATensorTile,TMATensorTileArray,TMATensorTileIm2col, andSplitLastDimTMATensorTileinmax.kernels.layout.tma_asyncnow taketile_shapeanddesc_shapeas flatCoordvalues instead ofIndexList[rank], and the separaterankparameter is gone (rank is now derived astile_shape.rank). Shape helpers_default_desc_shape,_padded_shape,_ragged_shape, and_im2col_desc_shapeare now out-param functions returning concreteCoordtypes. Migrate callers by passingcoord[...]values directly and reading shape dimensions via.element_types[i].static_value:# was: TMATensorTile[dtype, rank=3, tile_shape=Index(BM,1,depth),# desc_shape=Index(BM,1,depth)]# now:var tile = TMATensorTile[dtype, coord[BM, 1, depth], coord[BM, 1, depth]]()comptime BM_i = type_of(tile).tile_shape.element_types[0].static_valueQTMATilesplit intoQTMATilePrefill(rank-3, prefill/non-fused) andQTMATileFused(rank-4, decoding or fused-GQA); the rank choice must now be spelled at each use site. The companion factoriesq_tma_prefillandq_tma_fusedreplace the deletedq_tma. TheQTMATilealias itself was removed outright after all in-tree consumers migrated; spell the rank choice explicitly withQTMATilePrefillorQTMATileFused.
Tooling changes
-
The
mojocompiler now uses jemalloc as its allocator on Linux. Compiling large GPU kernel files is roughly 10 to 13 percent faster, because the compiler spends far less system time in glibc's arena management. -
The Mojo language server now reports a value's address space as a semantic token modifier (
addressSpace0throughaddressSpace15, matching theAddressSpaceindex) so editors can color shared, local, and constant memory differently. The address space is found in the value's type or in a type nested in its parameters, so anArrayof shared pointers is marked too.
Removed
- Removed
sum()from theCoordLiketrait and from its implementations (Coord,ComptimeIntand theAllmarker). Nothing called it: a coordinate's elements are extents and indices, so adding them together has no meaning the wayproduct()does, where the result is the number of elements a shape describes. Useproduct()for that, or iterate the elements and add them yourself if you really want a sum.
Fixed
-
c_charnow reflects the target's actualcharsignedness: unsigned by default on Linux running on ARM (AAPCS64), signed everywhere else Mojo currently targets (x86-64 Linux, macOS on Apple silicon). Previouslyc_charwas always signed, which could bind an FFI declaration to the wrong C type on Linux/ARM. -
atof()now rounds large mantissas correctly. Values whose significant digits exceed 53 bits, such asatof("123456789012345678"), could round to the wrong neighbouring double, and exact ties such asatof("4503599627370497.5")did not round to even. -
Splitting on an empty separator no longer puts the trailing empty slice out of bounds.
-
atof()accepts numbers with more than 19 significant digits, which previously raised "The number is too long". Malformed inputs such as"1..2","1e5.0", or"1e--5", which the old parser silently misread by skipping the offending characters, now raise. -
Struct layouts that recurse only through a parametric indirection now compile. A field such as
Optional[List[Pointer[Node, origin]]]orTuple[Pointer[Node, origin], Int]insidestruct Nodewas spuriously rejected withstruct has recursive reference to itself, despite the recursion being behind a pointer.