v0.26.2 (2026-03-19)
✨ Highlights
-
Official Modular AI agent skills. Modular has released official AI agent skills for working with MAX and Mojo. The four skills—
new-modular-project,mojo-syntax,mojo-gpu-fundamentals, andmojo-python-interop—help AI coding agents create new projects with the right tooling, generate correct modern Mojo syntax, follow GPU programming best practices, and handle Python-Mojo interop. The skills follow the open Agent Skills Standard and work with Claude Code and other compatible agents. Install them withnpx skills add modular/skills. -
Conditional trait conformances. Structs can now declare trait conformances that apply only when type parameters satisfy certain conditions, using
whereclauses in the conformance list. Nine standard library types—includingList,Dict,Set, andOptional—now use this feature to enforce trait requirements at compile time. See Language enhancements and Conditional conformances. -
def/fnunification.defis now Mojo's standard function declaration keyword.deffunctions no longer implicitly raise and now have the same semantics asfn: non-raising by default, with explicitraisesfor functions that can throw.fnis deprecated and will be removed in a future release. See Language enhancements. -
T-strings. Mojo now supports template strings with the
t"..."prefix. T-strings produce aTStringvalue that captures both the static format string and runtime arguments, enabling structured string processing without immediate allocation. See Language enhancements. -
comptime ifandcomptime for. The newcomptime ifandcomptime forsyntax replaces the legacy@parameter ifand@parameter fordecorator forms for compile-time conditionals and loops. Both syntaxes are accepted in this release; the@parameterforms will be deprecated soon. See Language enhancements. -
assertstatement. Mojo now supports a standaloneassertstatement, similar to Python'sassert. It checks a condition at runtime and aborts if the condition is false, with an optional message. Under the hood,assertdesugars todebug_assert()and respects the-D ASSERTflag. See Language enhancements. -
@align(N)decorator. Structs can now specify minimum alignment with@align(N), similar to C++alignasand Rust#[repr(align(N))]. The alignment value can be a struct parameter, enabling generic cache-aligned and hardware-aligned types. See Language enhancements. -
Implicit
InttoSIMDconversions deprecated. Implicit conversions fromInttoSIMDscalar types likeInt8orFloat32are now deprecated. Code relying on these conversions should use explicit constructors instead. Themojo build --experimental-fixitcommand can assist with migration. See Math and numeric types. -
Init unification. The
__moveinit__()and__copyinit__()methods are now renamed to__init__()with keyword-onlytakeandcopyarguments, respectively. The legacy names are still accepted but should be migrated to the modern form. See Init unification. -
StringableandRepresentabledeprecated. These traits are replaced by the unifiedWritabletrait, which provides default implementations. Most standard library types have already had theirStringableandRepresentableconformances removed. See String and text. -
UTF-8 safety improvements.
StringandStringSlicesubscripting now panics when an index falls in the middle of a multi-byte codepoint, andString.resize()panics on operations that would produce invalid UTF-8. Byte-position subscripting now returns an entire Unicode codepoint instead of a single byte. See String and text. -
traitno longer inheritsImplicitlyDestructible. Trait declarations no longer automatically conform toImplicitlyDestructible; traits that need it must now opt in explicitly. This continues v0.26.1's work on explicitly-destroyed types and encourages the ecosystem to support linear types by default. See Other changes.
Documentation
-
Added a new Mojo tips for Python devs page to the Mojo Manual, covering key differences Python developers encounter when learning Mojo. Topics include variable declarations with
var, type annotations, integer vs. floating-point division, implicit conversions, and other common gotchas. If you're coming from Python, this page helps you avoid surprises and write idiomatic Mojo faster. -
Added a new Instance initialization deep dive page to the Mojo Manual. This page explains the difference between logical and fieldwise initialization, how to write
__init__constructors that satisfy the compiler's field-by-field initialization requirements, and how copy and move constructors fit into the initialization model. Essential reading for anyone working with non-trivial struct types. -
Added a new Generics page to the Mojo Manual with worked examples of conditional trait conformance. The page shows how to use
whereclauses to declare that a generic type conforms to a trait only when its type parameters meet certain conditions, such as making a containerEquatableonly when its element type is. -
Restructured and expanded the metaprogramming section of the Mojo Manual. This includes a new introduction page, which provides an overview of Mojo's compile-time metaprogramming model, and new pages on compile-time evaluation, reflection, and materialization, as well as the previously-mentioned page on generics. Together these changes cover several previously undocumented topics and provide a smoother learning path from basic parameters to advanced metaprogramming techniques.
-
Updated the Jupyter Notebooks page with Apple Silicon GPU examples and setup instructions, so macOS users with M-series chips can run GPU-accelerated Mojo code in notebooks without needing an external GPU.
Language enhancements
-
defis now Mojo's standard function declaration keyword.deffunctions no longer implicitly raise and now have the same semantics asfn: they are non-raising by default, accept araisesspecifier, and support typed errors.fnis now deprecated and will be removed in a future release. We recommend switching allfndeclarations todef.# Previously
def foo(): # Implicitly raised Error.
fn bar(): # Did not raise.
fn baz() raises: # Explicitly raised Error.
fn qux() raises EmptyDictError: # Raised a typed error.
# Now
def foo(): # Does not raise.
def bar() raises: # Explicitly raises Error.
def baz() raises EmptyDictError: # Raises a typed error.
fn qux(): # Deprecated: use 'def' instead.
fn qux() raises: # Deprecated: use 'def' instead.
fn qux() raises EmptyDictError: # Deprecated: use 'def' instead.This change clarifies function contracts by making error handling explicit: a
defwithoutraisesguarantees that errors are handled locally or cannot occur, and the compiler enforces this no-throw guarantee. -
Mojo now supports conditional trait conformances on struct declarations. A struct can declare that it conforms to a trait only when its type parameters satisfy certain conditions, using
whereclauses in the conformance list. Thewhereclause accepts anyBool-typed parameter expression. For example,Listnow uses this to conditionally conform toEquatableandWritableonly when its element type does:struct List[T: Movable](
Copyable,
Equatable where conforms_to(T, Equatable),
Movable,
Writable where conforms_to(T, Writable),
):
...Here is a small self-contained example:
@fieldwise_init
struct Wrapper[T: Copyable & ImplicitlyDestructible](
Writable where conforms_to(T, Writable),
):
var value: Self.T
@fieldwise_init
struct NotWritable(Copyable, ImplicitlyDestructible):
var data: Int
def main():
var w = Wrapper[Int](42)
print(w) # OK — Int is Writable. Prints: Wrapper[Int](value=42)
var w2 = Wrapper[NotWritable](NotWritable(10)) # OK to construct
print(w2) # errors at parse timeCurrent limitations: Conditional conformance to
RegisterPassable,TrivialRegisterPassable, andImplicitlyDestructibleis not yet supported. -
Mojo now supports t-strings (template strings) for building structured string templates with interpolated expressions. T-strings use the
t"..."prefix and produce aTStringvalue that captures both the static format string and any runtime arguments, rather than immediately producing aString:def main():
var x = 10
var y = 20
print(t"{x} + {y} = {x + y}") # prints: 10 + 20 = 30Use
{{and}}to include literal braces in the output:print(t"Use {{braces}} like this") # prints: Use {braces} like this -
Mojo now supports
comptime ifandcomptime foras the preferred syntax for compile-time conditional and loop constructs, replacing the legacy@parameter ifand@parameter fordecorator forms:# Old syntax (still accepted, deprecated in a future release)
@parameter
if some_condition:
...
@parameter
for i in range(10):
...
# New syntax
comptime if some_condition:
...
comptime for i in range(10):
...Both syntaxes are accepted in this release. The
@parameterforms will be deprecated soon, and the parser will generate a warning and a fixit suggestion to migrate to the new syntax. -
Mojo now supports a standalone
assertstatement, similar to Python'sassert. It checks a condition at runtime and aborts the program if the condition is false:assert x > 0, "x must be positive"
assert len(items) != 0The condition must be a
Boolexpression and the optional message can be anyWritableexpression (StringLiteral,String,Int, etc.). Under the hood,assertdesugars to a call todebug_assert(), so it respects the existing-D ASSERTflag: assertions are active when compiled with-D ASSERT=alland are no-ops otherwise. -
Mojo now supports the
@align(N)decorator to specify minimum alignment for structs, similar to C++'salignasand Rust's#[repr(align(N))]. The valueNmust be a positive power of 2 and specifies the minimum alignment in bytes. The actual alignment ismax(N, natural_alignment)—you cannot use@alignto reduce alignment below the struct's natural alignment. For example,@align(1)on a struct containing anInt(8-byte aligned) will emit a warning and the struct will remain 8-byte aligned.from sys import align_of
@align(64)
struct CacheAligned:
var data: Int
def main():
print(align_of[CacheAligned]()) # Prints 64Both stack and heap allocations respect
@align.The alignment value can also be a struct parameter, enabling generic aligned types:
@align(Self.alignment)
struct AlignedBuffer[alignment: Int]:
var data: Int
def main():
print(align_of[AlignedBuffer[64]]()) # Prints 64
print(align_of[AlignedBuffer[128]]()) # Prints 128 -
Mojo now supports specifying a default value for an inferred parameter, making it easier to create a partially bound type:
struct Pointer[mut = False, // o: Origin[mut]] : pass
# Default to immutable pointer
comptime ImmPointer = Pointer[_]
# Explicitly set to mutable pointer
comptime mutPointer = Pointer[mut = True, _]
# Inferred mutability from `SomeOrigin()`
comptime InferredPointer = Pointer[SomeOrigin()]
# Parametric mutability pointer.
comptime ParametricPointer = Pointer[...] -
Mojo now enforces more explicit parameter binding rules:
-
[]is mandatory to make type more concrete:struct SomeStruct[a : Int = 1]:
pass
# SS1 is a parametric type
comptime SS1 = SomeStruct
# SS2 a concrete type alias of `SomeStruct[1]
comptime SS2 = SomeStruct[] -
When
[]is used, it must produce a concrete type unless_/...is used to unbind a specific/multiple missing parameters.struct SomeStruct[a : Int, b: Int, c: Int = 2]:
pass
# Error: can not infer `b`, since `[]` must produce a concrete type, without `_`/`...``
comptime SS1 = SomeStruct[1]
# This is a concrete type alias of `SomeStruct[1, 1, 2]`
comptime SS2 = SomeStruct[1, 1]
# Using `...` defers binding of b and c, and produces `SomeStruct[1, ?, ?]` (preserving possible defaults)
comptime SS3 = SomeStruct[1, ...]
# Even though SS3 unbinds `c`, Mojo keeps track of the fact that there is a default value for c:
# This install b (using 1) and c (using default value), and produce `SomeStruct[1, 1, 2]`
comptime SS4 = SS3[1]
# This installs the default and is identical to `SomeStruct[1, ?, 2]`
comptime SS5 = SomeStruct[1, _]
# This unbind the default explicitly and is identical to `SomeStruct[1, ?, ?]`
comptime SS6 = SomeStruct[1, _, _]
# The following two are equivalent:
comptime SS7 = SomeStruct
comptime SS8 = SomeStruct[...]
-
-
Mojo now supports more flexible default arguments and parameters, which can mismatch on declared type when their types are parametric. This allows inferring parameters from these when they are used as a default value, for example:
def take_string_slice[o: ImmOrigin](str: StringSlice[o] = ""): ...
def use_it():
take_string_slice() # Ok, defaults to empty string, inferring "o".
# Explicit calls also work of course.
take_string_slice(StaticString("hello"))
# Default value is checked for validity at the call site.
def defaultArgumentBadType2[T: AnyType](a: T = 1.0): pass
def callDefaultArgumentBadType2():
# Ok!
defaultArgumentBadType2[Float64]()
# error: value passed to 'a' cannot be converted from 'FloatLiteral[1]' to 'Int'
defaultArgumentBadType2[Int]() -
Mojo now allows move constructors to take their "take" argument as either
deinitorvar.deinitshould be generally preferred, butvarcan be useful in unusual cases where you want C++-like behavior where the destructor still runs on the value after the move constructor is done with it.def __init__(out self, *, var take: Self):
self.data = munge(take.data)
# take.__del__() runs here.
Language changes
Init unification
-
As part of "init unification", the
__moveinit__()and__copyinit__()methods are now renamed tofn __init__(out self, *, take: Self)andfn __init__(out self, *, copy: Self)respectively. Mojo now accepts these names for initializers, but also supports the legacy__moveinit__()and__copyinit__()names as well (for now). However, the argument name for these legacy methods must now be namedtakeandcopyrespectively. Please move to the more modern__init__names when possible. -
As part of init unification, the
__moveinit__is_trivialand__copyinit__is_trivialmembers ofMovableandCopyablehave been renamed to__move_ctor_is_trivialand__copy_ctor_is_trivialrespectively.
Deprecations
-
@register_passable("trivial")is now deprecated. Conform to theTrivialRegisterPassabletrait instead. The decorator will be removed after the next release. -
@register_passableis now deprecated. Conform to theRegisterPassabletrait instead. The decorator will be removed after the next release. -
LegacyUnsafePointeris now deprecated. UseUnsafePointerinstead. Check the docstrings inunsafe_pointer.mojofor guidance on migration.LegacyUnsafePointerwill be removed after the next release.
comptime assert
-
Unstable
__comptime_assertsyntax is now finalized ascomptime assert. A deprecation warning is emitted with a fixit for the old syntax. -
comptime assertno longer errors on always-false conditions. The assertion will only trigger if its parent scope is concretized. -
A statically False
comptime assertnow ends a scope. Any code following it in the same scope is now a warning, and can be removed.
Other changes
-
**_and*_are no longer supported in parameter binding lists. Use a more concise...to unbind any unspecified parameter explicitly. -
Homogeneous variadic parameters are now passed with the
VariadicParamListtype (formerly known asVariadicList) and arguments are now consistently passed withVariadicList(formerlyVariadicListMem) instead of trivial types being passed directly. -
Slice literals in subscripts have changed to be more similar to collection literals. They now pass an empty tuple as a required
__slice_literal__keyword argument to disambiguate slices. If you have defined your own range types, please add a__slice_literal__: () = ()argument to their constructors. -
traitdeclarations no longer automatically inherit fromImplicitlyDestructible.structdeclarations are not changed, and continue to inherit fromImplicitlyDestructible.Previously, the
@explicit_destroyannotation was required to opt-out ofImplicitlyDestructibleconformance. Now, if a trait's usage depends on implicit destructibility, it must opt-in explicitly:# Before
trait Foo:
...
# After:
trait Foo(ImplicitlyDestructible):
...Conversely, if a trait wanted to support non-implicitly-destructible types, it no longer needs to be annotated with
@explicit_destroy:# Before
@explicit_destroy
trait Foo:
...
# After
trait Foo:
...Making
structcontinue to inherit fromImplicitlyDestructibleand nottraitis intended to balance usability and familiarity in the common case, with the need to foster broad Mojo ecosystem support for explicitly destroyed types.It's not a problem if the majority of
structtypes areImplicitlyDestructiblein practice. However, if many ecosystem libraries are written with unnecessaryImplicitlyDestructiblebounds, that would hamper the usability of any individualstructtype that opts-in to being explicitly destroyed.Libraries with generic algorithms and types should be written to accommodate linear types. Making
ImplicitlyDestructibleopt-in for traits encourages a default stance of support, with specific types and functions only opting-in to the narrowerImplicitlyDestructiblerequirement if they truly need it.The majority of generic algorithms that take their inputs by reference should not be affected.
-
Re-exported symbols from a package's
__init__.mojoare no longer shadowed by a submodule of the same name. For example, ifpkg/foo.mojodefinesfn fooandpkg/__init__.mojodoesfrom .foo import foo, thenfrom pkg import fooandfrom pkg import *now correctly resolve to the function rather than the module. -
is_compile_time()has been renamed to__is_run_in_comptime_interpreter, which is now a keyword rather than a function call. It provides a mechanism for supporting different code execution paths in the comptime interpreter versus runtime-generated code. This value cannot be used as a constant in any comptime expressions because it is always evaluated asTruefor comptime expressions. Use the check as a condition for runtimeif. The compiler now warns if it is used as a condition forcomptime if. -
Mojo supports indexing with subscripts and names using the standard Python
__getitem__()and__getattr__()methods. Previously it used a heuristic to determine whether a__get*__method was operated with dynamic or parameter indexes. Mojo now has a simple and explicit policy: if a type implements a__getitem_param__()or__getattr_param__()method and the indices are valid parameter expressions, the compiler will pick it (but will not support a__setitem__()pair). If not or if the indices are only valid runtime values, Mojo will try__getitem__()/__setitem__()as usual. This makes the behavior more predictable and explicit, but requires types to switch to theparammethod names if they desire parameter-style subscripting. This only affects a small number of special types likeTupleandVariadicPack. -
The
@doc_privatedecorator has been renamed to@doc_hiddento better reflect its purpose of hiding declarations from documentation generation, rather than implying any change in access control. The old@doc_privatename is still accepted but deprecated and will be removed in a future release.
Library changes
Conditional conformances
-
Dictnow uses real conditional conformances forWritable. TheDicttype only conforms toWritablewhen both its key and value types do, enforced at compile time viawhereclauses. -
Standard library types now use conditional conformances, replacing previous
_constrained_conforms_tochecks:Deque:Equatable,Hashable,WritableDict:Writable,Equatable,HashableInlineArray:Copyable,Equatable,Hashable,WritableLinkedList:Equatable,Hashable,WritableList:Equatable,Hashable,WritableOptional:Hashable,Writable,Copyable,ImplicitlyCopyableSet:Copyable,Comparable,Equatable,Hashable,WritableTuple:Copyable,Equatable,Hashable,ImplicitlyCopyable,WritableVariant:Writable
Collections and iterators
-
Dictinternals have been replaced with a Swiss Table implementation using SIMD group probing for lookups. This improves lookup, insertion, and deletion performance—especially when looking up keys not in the dict—while increasing the load factor from 2/3 to 7/8 for better memory efficiency. Thepower_of_two_initial_capacitykeyword argument has been renamed tocapacityand now accepts any positive integer (it is rounded up to the next power of two internally, minimum 16). -
Dictnow performs in-place tombstone rehashing when the table is full of tombstones but the live element count is low. This prevents unnecessary capacity doubling after repeated insert/delete cycles. -
StaticTuplenow supports comparison operators.__eq__()/__ne__()are available whenelement_type: Equatable, and__lt__()/__le__()/__gt__()/__ge__()are available whenelement_type: Comparable. All use lexicographic ordering with compile-time unrolled loops. -
Set.pop()now usesDict.popitem()directly, avoiding a redundant rehash. Order changes from FIFO to LIFO, matching Python's unorderedset.pop(). -
Set.__gt__()andSet.__lt__()now use an O(1)len()check plus a singleissubset()traversal instead of two full traversals. -
InlineArraynow requires explicitly using literals for construction. For example:var a: InlineArray[UInt8, 4] = [1, 2, 3, 4]
# instead of InlineArray[UInt8, 4](1, 2, 3, 4) -
The following types now correctly implement
write_repr_to():List,Set
-
The
itertoolsmodule now includes three new iterator combinators:cycle(iterable): Creates an iterator that cycles through elements indefinitelytake_while[predicate](iterable): Yields elements while the predicate returns Truedrop_while[predicate](iterable): Drops elements while the predicate returns True, then yields the rest
String and text
-
The
StringableandRepresentabletraits are now deprecated. UseWritableinstead to make your types formattable as strings.Writableprovides a default implementation, so in many cases simply addingWritableconformance is enough without manually implementingwrite_to()orwrite_repr_to(), however, you can manually implement these to get custom output. -
The
stdlibis beginning to remove support for theStringableandRepresentabletraits in favor of the unifiedWritabletrait. Most stdlib types have had their conformance toStringableandRepresentableremoved and the associated__str__()and__repr__()methods have been deprecated. -
The
__reversed__()method onString,StringSlice, andStringLiteralhas been deprecated in favor of the newcodepoints_reversed()method. The new method name makes it explicit that iteration is over Unicode codepoints in reverse order, maintaining consistency with the existingcodepoints()andcodepoint_slices()methods. The deprecated__reversed__()methods will continue to work but will emit deprecation warnings. -
The
StringSliceconstructor fromStringnow propagates mutability. If you have a mutable reference to aString,StringSlice(str)returns a mutableStringSlice. TheString.as_string_slice()method is now deprecated in favor of theStringSlice(str)constructor, andString.as_string_slice_mut()has been removed. -
String.ljust(),String.rjust(), andString.center()have been renamed toString.ascii_ljust(),String.ascii_rjust(), andString.ascii_center(). Likewise for their equivalents onStringSliceandStaticString. -
String.resize()will now panic if the new length would truncate a codepoint. Previously it would result in a string with invalid UTF-8. -
String.resize()will now panic iffill_byteis >=128. Previously it would create invalid UTF-8. -
Subscripting into
StringandStringSlicewill now panic if the index falls in the middle of a UTF-8 encoded codepoint. Previously they would return invalid UTF-8. This panic is unconditional. Use.as_bytes()[...]if you really want the previous behavior. -
StringSlice[byte=]subscripting now returns aStringSliceinstead of aString. This is consistent with range-based subscripting. -
Subscripting
StringandStringSliceby byte position will now return an entire Unicode codepoint. Previously it would return a single byte, and produce invalid UTF-8 if the index fell on the starting byte of a multi-byte codepoint. -
String,StringSlice, andStringLiteral's.format()method now require their arguments to beWritable. -
TString.write_to()now uses a compact encoding for format strings. The format string is flattened at compile time into NUL-terminated literal segments, producing considerably smaller static data and faster runtime than the previous struct-based precompiled entries. -
Formatting compile-time format strings (
StringLiterals) no longer allocates memory. It usesglobal_constantto store what would be heap-allocated parsed formatting data. -
T-strings now support the raw prefix (
rt"...") which preserves backslashes as literal characters while still supporting interpolation.var name = "Mojo"
print(t"C:\{name}\Documents") # prints "C:\Mojo\Documents" -
Subscripting
StringandStringSlicenow requires a named parameter for range indexing. For example,s[1:3]is nows[byte=1:3].
Type system and traits
-
Boolno longer conforms to theIndexertrait. Previously,Boolcould be used to index into collections (for example,nums[True]), which is not desirable behavior for a strongly-typed language. UseInt(my_bool)to explicitly convert aBoolto an index. -
The following types now conform to
Writableand have custom implementations ofwrite_to()andwrite_repr_to().TupleVariantOptional
-
All traits and structs with the
@register_passable("trivial")decorator now extend theTrivialRegisterPassabletrait. The decorator is removed from them. -
A new
utils/type_functionsmodule was added for holding type functions. Type functions arecomptimedeclarations that produce a type from compile-time parameter inputs. Unlike regularfnfunctions which accept and return runtime values, type functions operate entirely at compile time—they takecomptimeparameters and evaluate to a type, with no runtime component.ConditionalType—A type function that conditionally selects between two types based on a compile-time boolean condition. It is the type-level equivalent of the ternary conditional expressionThen if If else Else.
-
reflection/traitshas been added, providing compile-time meta functions (AllWritable,AllMovable,AllCopyable,AllImplicitlyCopyable,AllDefaultable,AllEquatable) that evaluate toTrueif all types in a variadic type list conform to the corresponding trait. -
Added the
UnsafeNicheableandUnsafeSingleNicheabletraits tostd.utils. These traits allow types to expose known-invalid bit patterns ("niches") that can be used by containers likeOptionalandVariantto avoid storing a separate tag, so thatsize_of[Optional[T]]() == size_of[T]().UnsafeSingleNicheableis a simplified subtrait for the common case where a type has exactly one niche value.VariantandOptionalnow automatically take advantage of this: when the variant types qualify, the discriminant tag is elided entirely, reducing the size of the variant. For example,size_of[Optional[T]]() == size_of[T]()for anyTthat implementsUnsafeNicheable.Variantalso only works withMovabletypes in the interim instead ofAnyTypedue to some compiler limitations.
Math and numeric types
-
Implicit conversions from
InttoSIMDare now deprecated, and will be removed in a future version of Mojo. This includes deprecating conversions fromIntto specificSIMDscalar types likeInt8orFloat32.Note: The experimental
mojo build --experimental-fixitcommand may be useful in assisting with migrating your code to reflect this change.Code currently relying on implicit conversions, for example:
def foo(arg: Int) -> Float32:
return argshould be changed to use an explicit conversion:
def foo(arg: Int) -> Float32:
return Float32(arg)Occasionally, when an implicit conversion was happening from a scalar
Intto a widerSIMDvalue, the compiler will suggest a change to make the conversion explicit that is somewhat verbose, performing both the type conversion and the "splat" in a single step:var bits: SIMD[DType.uint8, 16] = ...
- var y = bits >> (j * 4)
+ var y = bits >> SIMD[DType.uint8, 16](j * 4)a simpler change is to make the type conversion explicit, and let the "splat" continue to happen implicitly (which will remain supported):
var bits: SIMD[DType.uint8, 16] = ...
- var y = bits >> (j * 4)
+ var y = bits >> UInt8(j * 4)A similar overly verbose suggestion can be emitted relating to
LayoutTensor.element_type, where the compiler may suggest a change of the form:- tensor[i] = i * l
+ tensor[i] = LayoutTensor[DType.uint32, Layout(IntTuple(-1)), stack].element_type(i * l)where a simpler change is sufficient:
- tensor[i] = i * l
+ tensor[i] = UInt32(i * l)
# Note: If the tensor's `dtype` is not statically known, this works as well:
+ tensor[i] = Scalar[tensor.dtype](i * l) -
SIMD's safe division operators (__floordiv__,__mod__,__divmod__) now return safe results in an elementwise fashion. They previously returned a zero result if any element of the divisor was zero:comptime Int32x2 = SIMD[DType.int32, 2]
var a = Int32x2(99, 99)
var b = Int32x2(4, 0)
# Now returns [24, 0]
# Previously returned [0, 0]
print(a.__floordiv__(b)) -
Int.__truediv__()now performs truncating integer division, returningIntinstead of the previously deprecatedFloat64. Use explicitFloat64casts for floating-point division. -
Documentation for
SIMD.__round__()now clarifies the pre-existing behavior that ties are rounded to the nearest even, not away from zero. -
Int.write_padded()now accounts for a negative sign when calculating the width, resulting in a consistent width regardless of sign:Int(1).write_padded(s, 4) # writes " 1"
Int(-1).write_padded(s, 4) # writes " -1" -
SIMDnow has awrite_padded()method for integralDTypes, matching the behavior ofInt.write_padded(). The padding is added elementwise. UnlikeSIMDregular printing, there are no spaces added between elements by default:Int32(1).write_padded(s, 4) # writes " 1"
Int32(-1).write_padded(s, 4) # writes " -1"
# "[ 1, -1, 0,1234]"
SIMD[DType.int32, 4](1,-1,0,1234).write_padded(s, 4)
# "[255,255]"
SIMD[DType.uint8, 2](255).write_padded(s, 1) -
Added
comb(n, k)andperm(n, k)tostd.math, matching Python'smath.comb()andmath.perm().comb(n, k)computes the binomial coefficient C(n, k) without computing full factorials, returning 0 whenk > n.perm(n, k)computes permutations P(n, k); omittingk(default-1) returnsn!. -
The
builtin.mathmodule has been merged intomath. The traitsAbsable,DivModable,Powable,Roundableand functionsabs(),divmod(),max(),min(),pow(),round()are now part of themathmodule and continue to be available in the prelude. Code that explicitly imported frombuiltin.mathshould update to import frommathinstead. -
Math functions in
std.math(exp(),exp2(),log2(),erf(),tanh(),sin(),cos(),tan(),acos(),asin(),atan(),atan2(),acosh(),asinh(),atanh(),cosh(),sinh(),expm1(),log10(),log1p(),logb(),cbrt(),erfc(),j0(),j1(),y0(),y1()) now usewhere dtype.is_floating_point()clauses on their signatures instead of__comptime_assertchecks in their bodies. This provides better compile-time error messages at the call site. Callers using these functions with genericdtypeparameters may need to add evidence proving (either awhereclause or__comptime_assert) that their type is floating point.
Pointer and memory
-
Added
uninit_move_n(),uninit_copy_n(), anddestroy_n()functions to thememorymodule for efficient bulk memory operations. These functions handle moving, copying, and destroying multiple values in contiguous memory, with automatic optimization for trivial types usingmemcpy. They encapsulate the common pattern of checking__move_ctor_is_trivial,__copy_ctor_is_trivial, or__del__is_trivialand selecting the appropriate implementation. TheListcollection now uses these functions internally for improved code clarity and maintainability. -
The
Originstruct now takes the underlying MLIR origin as a parameter instead of storing it. This follows the design ofIntLiteraland related types, and fixes some memory safety problems. -
UnsafeMaybeUninithas been renamed as such, and its methods have had their names updated to reflect theinitname. It also now exposes azeroed()method to get zeroed out uninitialized memory. It also no longer callsabort()when being copied or moved, allowing for more practical uses. -
Span[T]is no longer restricted toCopyabletypes. It now works withT: AnyType. There are a few restrictions including iteration requiringT: Copyable.
GPU programming
-
lane_group_sum(),lane_group_max(), andlane_group_min()instd.gpu.primitives.warpnow always broadcast the reduction result to all participating lanes, using optimized hardware-specific paths (AMD DPP, Blackwell redux, or butterfly shuffle pattern). The previouslane_group_sum_and_broadcast(),lane_group_max_and_broadcast()functions are deprecated—use the short names instead. -
TileTensornow supports hierarchical indexing. For example, aTileTensorwith shape(4, (3, 2))can be indexed by(1),(1, 1), or(1, (1, 1)). -
TileTensornow supports flattening up to depth-4. -
Many kernels in
nnhave been migrated to useTileTensor. We will have more documentation onTileTensorand its uses over the coming weeks.
FFI and system
-
The
ffimodule is now a top-level module in the standard library, rather than being nested undersys. This improves discoverability of FFI functionality. Update your imports fromfrom sys.ffi import ...tofrom ffi import .... -
Added
UnsafeUnion[*Ts], a C-style untagged union type for FFI interoperability. UnlikeVariant,UnsafeUniondoes not track which type is stored (no discriminant), making it suitable for interfacing with C unions and low-level type punning. The memory layout exactly matches C unions (size is max of elements, alignment is max of elements).All element types must have trivial copy, move, and destroy operations, matching C union semantics where types don't have constructors or destructors.
Construction is explicit (no implicit conversion) to emphasize the unsafe nature of this type. All accessor methods are prefixed with
unsafe_to make it clear that these operations are unsafe.from ffi import UnsafeUnion
# Define a union that can hold Int32 or Float32
comptime IntOrFloat = UnsafeUnion[Int32, Float32]
var u = IntOrFloat(Int32(42))
print(u.unsafe_get[Int32]()) # => 42
print(u) # => UnsafeUnion[Int32, Float32](size=4, align=4)
# Type punning (reinterpreting bits)
var u2 = IntOrFloat(Float32(1.0))
print(u2.unsafe_get[Int32]()) # => 1065353216 (IEEE 754 bits) -
The
env_get_bool(),env_get_int(),env_get_string(), andenv_get_dtype()functions in thesys.param_envmodule have been renamed toget_defined_bool(),get_defined_int(),get_defined_string(), andget_defined_dtype()in the newsys.definesmodule. The new names better reflect that these functions read compile-time defines (set via-D), not runtime environment variables. Theis_defined()function has also moved tosys.defineswithout renaming. The old names insys.param_envare deprecated but still available.
Testing
-
The
testingmodule now providesassert_equalandassert_not_equaloverloads forTuple, enabling direct tuple-to-tuple comparisons in tests instead of element-by-element assertions. Element types must conform toEquatable & Writable. -
assert_equalandassert_not_equalnow work with types implementingWritable.
Other library changes
-
Changed
CompilationTarget.unsupported_target_error()to returnNeverand removed itsresulttype parameter. -
Removed
DType.get_dtype[T]()andDType.is_scalar[T](). These were low-level operations for extracting theDTypeof aSIMDin generic code. There are better alternatives available in Mojo today using reflection capabilities. -
perf_counter_ns()now returns correct nanoseconds on GPU instead of raw cycle counts. Previously on NVIDIA GPUs it usedclock64(a cycle counter dependent on GPU core clock frequency); it now usesglobaltimerwhich provides actual nanosecond resolution. On AMD GPUs, it now usess_memrealtime(a constant-speed real-time clock) instead ofs_memtime(a cycle counter). -
The
DimListtype has moved to representing its dimensions as parameters to the type instead of values inside the type, directly reflecting that the dimensions are known at compile time. ChangeDimList(x, y)toDimList[x, y]().
Tooling changes
-
The Mojo compiler now accepts conjoined
-Doptions in addition to the non-conjoined form as before. Now, both-Dfooand-D fooare accepted. -
mojo buildnow supports several--print-*options for discovering target configuration and supported architectures:--print-effective-target: Shows the resolved target configuration after processing all command-line flags.--print-supported-targets: Lists all available LLVM target architectures.--print-supported-cpus: Lists valid CPU names for a given target triple (requires--target-triple).--print-supported-accelerators: Lists all supported GPU and accelerator architectures (NVIDIA, AMD, Apple Metal).
-
mojo formatnow only formats Mojo files (.mojo,.🔥) by default when run on a directory. Previously it would also format Python files, which conflicted with Python-specific formatters in pre-commit hooks. Users who want to format Python files can usemblackdirectly. -
mojo formatnow supports--print-cache-dir(hidden, use--help-hiddento see it) to display the path to the formatter cache directory. -
mojo build --emit=asmnow also emits GPU kernel assembly files alongside the host.soutput. For NVIDIA targets, PTX is written as<out>_<kernelfn>.ptx; for Apple Metal targets, LLVM IR is written as<out>_<kernelfn>.ll; for AMD targets,.amdgcnfiles are written. Multiple kernels generated from the same function are disambiguated with a numeric suffix (for example,<out>_<kernelfn>_1.ptx). -
mojo docis now more consistent with Python in its treatment of private members. Any member name starting with an underscore (_) is treated as private unless it is a "dunder" member (starting and ending with double-underscores). In practice this means that members that start but don't end with double underscores (__example()) are now treated as private. Dunder methods that start with__mlirare now treated as public unless marked with the@doc_privatedecorator.
❌ Removed
-
The
.🔥(flame) and📦(package) emoji file extensions are no longer supported. Use.mojofor all Mojo source files and__init__.mojofor package initialization files. The emoji extensions were removed to simplify the tooling and improve compatibility across different systems and editors. -
The
ownedkeyword has been removed. Usevarfor parameters ordeinitfor__moveinit__/__del__arguments as appropriate. -
Dict.EMPTYandDict.REMOVEDcomptime aliases have been removed. These were internal implementation details of the old hash table design. -
The
@nonmaterializabledecorator has been renamed to@__nonmaterializable. This decorator should not be used outside the standard library, and might be removed in a future release.
🛠️ Fixed
-
Fixed a bug where Mojo incorrectly passed or returned structs in
externC function calls. The compiler now applies platform ABI coercion (System V AMD64 on x86-64, AAPCS on ARM64) when lowering external calls, decomposing structs into the register types the C ABI requires—matching the behavior of Clang and Rust. This resolves silent wrong-answer bugs when calling C functions that take or return struct types. -
Issue #5845: Functions raising custom type with conversion fails when returning StringSlice
-
Issue #5722:
__del__incorrectly runs when__init__raises before all fields are initialized. -
Issue #5875: Storing
SIMD[DType.bool, N]with width > 1 to a pointer and reading back element-wise now returns correct values. -
StringSlice.find: Fixed integer overflow bug in SIMD string search that caused searches to fail when searching for strings longer thansimd_width_of[DType.bool]()and haystacks larger than UInt16.MAX. -
LinkedList.reverse(): Fixed missingprevpointer updates, which caused__reversed__()to produce wrong results after reversing. -
Mojo would previously consider types
Movableif they had a__moveinit__()method, even if they didn't conform toMovable. Movability is now tied to the conformance which implies the method.
Special thanks
Special thanks to our community contributors:
Amit Vijairania (@avijaira), Bernardo Taveira (@bertaveira), Brian Carroll (@brian-carroll), Brian Grenier (@bgreni), Byungchul Chae (@byungchul-sqzb), Derek Veit (@DerekVeit), Dylan Walker Brown (@walkerbrown), elpres (@elpres), Evan Owen (@ulmentflam), Joseph Wakeling (@JosephWakeling), josiahls (@josiahls), Krish Gupta (@KrxGu), lamija-i (@lamija-i), Mahendra Singh Rathore (@mahendrarathore1742), Manuel Saelices (@msaelices), martinvuyk (@martinvuyk), minkyu (@kkimmk), nejraberberovic (@nejraberberovic), Parsa Bahraminejad (@prsabahrami), pei0033 (@pei0033), Sören Brunk (@sbrunk), Taesu Kim (@tae-su-kim), Tolga Cangöz (@tolgacangoz), turakz (@turakz)