v0.26.1 (2026-01-29)
✨ Highlights
-
Expanded reflection module. The
reflectionmodule now provides extensive compile-time introspection: struct field enumeration, types, and byte offsets; source location tracking; and trait conformance checking on dynamically obtained types. These APIs enable advanced metaprogramming patterns like automatic serialization and debug formatting. See Reflection and introspection. -
Explicitly-destroyed types. Mojo now has first-class support for explicitly-destroyed types (sometimes referred to as "linear types"). The
AnyType,Movable, andCopyabletraits no longer require a__del__()method; useImplicitlyDestructiblewhen you need implicit destruction. Explicitly-destroyed types let you encode powerful invariants requiring explicit resource handling. See Language changes and Explicitly-destroyed types. -
Typed errors. Functions can now specify what type they raise instead of defaulting to
Error(for example,fn foo() raises CustomError -> Int). Typed errors are highly efficient—they compile to an alternate return value with no stack unwinding—making them suitable for GPU and embedded targets. See Language enhancements. -
Traits with default implementations. The
Hashable,Writable, andEquatabletraits now provide default implementations that automatically derive behavior from struct fields using reflection. Simple structs can conform to these traits without writing any boilerplate—just ensure all fields conform to the same trait. See Traits with default implementations. -
UInttype redesign. TheUIntstruct has been replaced by a type alias toScalar[DType.uint], enabling more powerful generic programming over unsigned SIMD data types of machine word size. See Other library changes. -
String UTF-8 safety.
Stringnow offers three explicit constructors for raw bytes:from_utf8=(validates and raises on error),from_utf8_lossy=(replaces invalid sequences with �), andunsafe_from_utf8=(no validation). This makes UTF-8 handling guarantees explicit at construction time. See String and text.
Documentation
-
The new Mojo quickstart page provides a "5-minute" introduction to installing Mojo and exploring basic language features.
-
The new Reflection page describes Mojo's new compile-time reflection capabilities, including example use cases.
-
Added a section to the Value destruction page about how to implement and use explicitly-destroyed types.
-
The new Jupyter Notebooks page provides step-by-step instructions for programming with Mojo in Google Colab and local JupyterLab environments, including GPU programming examples.
-
The new Materialization page describes how to make compile-time values available at run time.
Language enhancements
-
Mojo now supports raising "typed errors", where a function can specify what type it raises instead of defaulting to the
Errortype. This is done by specifying it after theraiseskeyword, for example,fn foo() raises CustomError -> Int.Raised errors in Mojo are very efficient - they work as an alternate return value: for example, a function like
fn () raises Int -> Float32:compiles into code that returns either anIntor aFloat32and uses an implicit boolean result to determine which one is valid - there is no expensive stack unwinding or slow dynamic logic that is implied. This means that thrown errors work fine on GPUs and other embedded targets.The 'caught' type in a
tryblock is automatically inferred to be the first thrown type inside of thetrybody, for example:try:
print(foo())
except err: # "err" is typed as CustomError
print(err)Typed throws "just work" with generics, allowing the definition of higher order functions like:
fn parametric_raise_example[ErrorType: AnyType](fp: fn () raises ErrorType) raises ErrorType:
# ... presumably some iteration or other exciting stuff happening here.
fp()This dovetails with other support to allow contextually generic thrown types, for example:
fn call_parametric_raise_example[GenTy: AnyType](func_ptr: fn () raises GenTy):
fn raise_int() raises Int: pass
try:
parametric_raise_example(raise_int)
except err_int: # Typed as Int
ref x: Int = err_int
fn raise_string() raises String: pass
try:
parametric_raise_example(raise_string)
except err_string: # Typed as String
ref s: String = err_string
try:
parametric_raise_example(func_ptr)
except err_gen: # Typed as GenTy
ref s: GenTy = err_gen
# Non-raising functions infer an error type of `Never`, allowing these
# functions to propagate non-raisability across generic higher-order
# functions conveniently.
fn doesnt_raise(): pass
# Note this isn't in a try block. Mojo knows 'parametric_raise_example'
# doesn't raise because the 'doesnt_raise' function doesn't.
parametric_raise_example(doesnt_raise)As part of this, context managers have been extended to support typed throws, and can also infer an error type if they need to handle it, for example:
struct MyGenericExitCtxtMgr:
# Called on entry to the with block.
fn __enter__(self): ...
# Called on exit from the with block when no error is thrown.
fn __exit__(self): ...
# Called on exit from the with block if an error is thrown.
fn __exit__[ErrType: AnyType](self, err: ErrType) -> Bool: ... -
Mojo now supports a
Nevertype, which can never be instantiated. This type can be used for functions (likeabort()) which do not have a normal return value, and for functions that are guaranteed to raise without returning a normal value. Functions that are declared to raiseNever(and generic functions instantiated withNeveras their error type) compile into the same ABI as functions that don'traise. -
Mojo now allows the use of a
comptime(x)expression to force a subexpression to be evaluated at compile time. This can help make working with certain types more elegant when you can't (or don't want to) materialize them into a runtime value. For example, if you just want the size from a compile time layout:fn takes_layout[a: Layout]():
# materializes entire layout value just to get the size out of it
print(a.size())
# Could already work around this with a comptime declaration, verbosely.
comptime a_size = a.size()
print(a_size)
# Can now tell Mojo to evaluate the expression at comptime.
print(comptime(a.size())) -
Mojo now differentiates between
...andpassin trait methods. The use of...continues to denote no default implementation—passnow specifies a default do-nothing implementation. For example:trait T:
# No default implementation
fn foo(self): ...
# Default implementation that does nothing
fn bar(self) : passThe compiler will error on the use of
passto define a default implementation for a trait method with results:trait T:
foo.mojo:2:26: error: trait method has results but default implementation returns no value; did you mean '...'?
fn foo(self) -> Int: pass
^
trait.mojo:2:8: note: in 'foo', declared here
fn foo(self) -> Int: pass
^ -
Mojo now allows implicit conversions between function types from a non-raising function to a raising function. It also allows implicit conversions between function types whose result types are implicitly convertible.
fn takes_raising_float(a: fn () raises -> Float32): ...
fn returns_int() -> Int: ...
fn example():
# This is now ok.
takes_raising_float(returns_int) -
Mojo now allows functions that return references to convert to functions that return values if the type is implicitly copyable or implicitly convertible to the destination type:
fn fn_returns_ref(x: SomeType) -> ref [x.field] Int: ...
fn examples():
# OK, Int result from fn_returns_ref can be implicitly copied.
var f1 : fn (x: SomeType) -> Int = fn_returns_ref
# OK, Int result from fn_returns_ref implicitly converts to Float64.
var f2 : fn (x: SomeType) -> Float64 = fn_returns_ref -
Context managers (used in
withstatements) can now define consuming exit methods—that is,fn __exit__(var self)—which can be useful for explicitly-destroyed context managers. This also works withdeinit. -
The
deinitargument convention can now be applied to any argument of a struct method, but the argument type still must be of the enclosing struct type. -
Mojo now supports the
...expression. It is a logically empty value ofEllipsisType. It can be used in overloaded functions, for example,getitem()calls:struct YourType:
fn __getitem__(self, idx: Int) -> Int:
# ... behavior when passed x[i]
fn __getitem__(self, idx: EllipsisType) -> Int:
# ... behavior when passed x[...]
Language changes
-
The Mojo language basic trait hierarchy has changed to expand first-class support for explicitly-destroyed types (sometimes referred to as "linear types").
The
AnyType,Movable, andCopyabletraits no longer require that a type provide a__del__()method that may be called by the compiler implicitly whenever an owned value is unused. Instead, theImplicitlyDestructibletrait should be used in generic code to require that a type is implicitly destructible.Explicitly-destroyed types enable Mojo programs to encode powerful invariants in the type system, by modeling a type in such a way that a user is required to take an action "in the future", rather than simply implicitly dropping an instance "on the floor".
Code using
T: AnyTypecan change to useT: ImplicitlyDestructibleto preserve its pre-existing behavior following this change.Relatedly, the
UnknownDestructibilitytrait is now no longer required, as it is equivalent to the newAnyTypebehavior. -
Mojo no longer supports overloading functions on parameters alone: it will not try to disambiguate between
fn foo[a: Int8]():andfn foo[a: Int32]():for example. Mojo never fully implemented the previous support in a reliable way, and removing this simplifies the language. It still supports overloading on function arguments of course. -
The
__next_ref__()method in for-each loops has been removed. Now you can implement the__next__()method of your iterator to return either a value or a reference. When directly using the collection, Mojo will use the ref-returning variant, but will allow it to conform toIteratorfor use with generic algorithms (which use a copied value). -
The
origin_of(x)operator now returns a value of typeOrigininstead of an internal MLIR type, and aliases likeImmutOriginare nowOrigintype as well. -
The
Origin.cast_from[x]syntax has been replaced with a safe implicit conversion from any origin to an immutable origin (ImmutOrigin(x)) and an explicit unsafe conversion (unsafe_origin_mutcast[origin, mut=m]). -
The
*_and**_syntax for explicitly unpacked parameters has been replaced with a simplified...syntax. Instead ofT[4, 5, *_, **_]you can now useT[4, 5, ...]. The...delays binding of both keyword and non-keyword parameters. -
The compiler will now warn on the use of
aliaskeyword and suggestcomptimeinstead. -
The compiler will now warn on unqualified access to struct parameters, for example
@fieldwise_init
struct MyStuff[my_param: Int]:
fn give_me_stuff(self) -> Int:
# Warning: unqualified access to struct parameter 'my_param'; use 'Self.my_param' instead
return my_param -
The Mojo compiler generates more clear error messages when diagnosing invalid calls: it mentions the argument name, instead of "argument #4".
Library changes
Reflection and introspection
-
The
reflectionmodule has been significantly expanded with new compile-time introspection capabilities. The module has moved fromcompile.reflectionto a top-levelreflectionmodule (update imports fromfrom compile.reflection import ...tofrom reflection import ...). Internally, the module is now organized intotype_infoandstruct_fieldssubmodules, though the public API viafrom reflection import ...remains unchanged.Struct field introspection - New APIs for compile-time struct analysis:
struct_field_count[T]()- Returns the number of fields in a structstruct_field_names[T]()- Returns field names asInlineArray[StaticString, N]struct_field_types[T]()- Returns a variadic of all field typesstruct_field_index_by_name[T, name]()- Returns field index by namestruct_field_type_by_name[T, name]()- Returns field type wrapped inReflectedType
These APIs work with both concrete types and generic type parameters:
fn print_fields[T: AnyType]():
comptime names = struct_field_names[T]()
comptime types = struct_field_types[T]()
@parameter
for i in range(struct_field_count[T]()):
print(names[i], get_type_name[types[i]]())Field access by index - Two new magic functions enable index-based field access without copying:
__struct_field_type_at_index(T, idx)- Returns field type at index__struct_field_ref(idx, ref s)- Returns a reference to the field
Unlike
kgen.struct.extractwhich copies,__struct_field_ref()returns a reference, enabling reflection utilities to work with non-copyable types:fn print_all_fields[T: AnyType](ref s: T):
comptime names = struct_field_names[T]()
@parameter
for i in range(struct_field_count[T]()):
print(names[i], "=", __struct_field_ref(i, s))Field byte offsets -
offset_of[T, name=field_name]()returns the byte offset of a named field within a struct, enabling no-copy serialization and other low-level memory operations. The offset is computed at compile time using the target's data layout, correctly accounting for alignment padding. This is analogous to C/C++'soffsetofand Rust'soffset_of!macro. Anoffset_of[T, index=i]()overload is also available to look up by field index.from reflection import offset_of
struct Point:
var x: Int # offset 0
var y: Float64 # offset 8 (aligned)
fn main():
comptime x_off = offset_of[Point, name="x"]() # 0
comptime y_off = offset_of[Point, name="y"]() # 8Type introspection utilities:
-
is_struct_type[T]()- ReturnsTrueifTis a Mojo struct type. Useful for guarding reflection code that uses struct-specific APIs to avoid compiler errors on non-struct types (for example, MLIR primitive types). Use@parameter ifsince these APIs are evaluated at compile time. (Issue #5734) -
get_base_type_name[T]()- Returns the unqualified name of a parameterized type's base type. For example,get_base_type_name[List[Int]]()returns"List". Useful for identifying collection types regardless of element types. (Issue #5735)
Source location introspection:
SourceLocation- A struct holding filename, line, and column informationsource_location()- Returns the location where it's calledcall_location()- Returns the location where the caller was invoked (requires the caller to be@always_inline)
These were previously internal APIs (
_SourceLocation,__source_location,__call_location) inbuiltin._location. The old module has been removed.from reflection import source_location, call_location, SourceLocation
fn main():
var loc = source_location()
print(loc) # main.mojo:5:15
@always_inline
fn log_here():
var caller_loc = call_location()
print("Called from:", caller_loc)Note: These APIs do not work correctly in parameter expressions (comptime contexts return placeholder values).
Trait conformance checking - The
conforms_to()builtin now accepts types from reflection APIs likestruct_field_types[T](), enabling conformance checks on dynamically obtained field types:@parameter
for i in range(struct_field_count[MyStruct]()):
comptime field_type = struct_field_types[MyStruct]()[i]
@parameter
if conforms_to(field_type, Copyable):
print("Field", i, "is Copyable")
Traits with default implementations
-
Several traits now have default implementations that use reflection to automatically derive behavior from struct fields. This means simple structs can conform to these traits without implementing any methods - all fields just need to conform to the same trait:
Hashable- Default__hash__()hashes all fields:@fieldwise_init
struct Point(Hashable):
var x: Float64
var y: Float64
hash(Point(1.5, 2.7)) # Works automaticallyWritable- Defaultwrite_to()formats all fields:@fieldwise_init
struct Point(Writable):
var x: Float64
var y: Float64
print(Point(1.5, 2.7)) # Point(x=1.5, y=2.7)Equatable- Default__eq__()compares all fields:@fieldwise_init
struct Point(Equatable):
var x: Int
var y: Int
print(Point(1, 2) == Point(1, 2)) # TrueNote: The default
Equatableperforms memberwise equality, which may not be appropriate for types with floating-point fields (due to NaN semantics). Override any of these methods for custom behavior.
Collections and iterators
-
Removed
Listvariadic initializer.-
Statements like:
var x = List[Int32](1, 2, 3)can be updated to:
var x: List[Int32] = [1, 2, 3] -
Expressions like:
var x = foo(List[Float32](1, 2, 3))can be updated to move the explicit type "hint" around the first element:
var x = foo([Float32(1), 2, 3]) -
Expressions like:
var data = Span(List[Byte](1, 2, 3))can be updated to move the explicit element type to the
Span:var data = Span[Byte]([1, 2, 3])
-
-
Listslicing without a stride now returns aSpan, instead of aListand no longer allocates memory. -
InlineArrayno longer conforms toImplicitlyCopyable. Users must explicitly copy arrays or take references. -
IndexListis no longer implicitly constructible fromInt. Previously, the fill constructor (which broadcasts a singleIntto all elements) was marked@implicit, allowing code likevar x: IndexList[3] = 5which would create(5, 5, 5). This implicit conversion has been removed to improve type safety. Use explicit construction instead:IndexList[3](5). -
Dictnow raises a customDictKeyErrortype on failure, making lookup failures more efficient to handle.var d = Dict[String, Int]()
var key = "missing_key"
try:
_ = d[key]
except e:
print(e) # Prints: DictKeyError -
New
ContiguousSliceandStridedSlicetypes were added to thebuiltin_slicemodule to support specialization for slicing without strides. -
Spannow conforms toIterable. -
any()andall()now work overIterables, which means they can act over the result ofmap(). -
Tuples have been improved:- Tuples can now be concatenated with
Tuple.concat(). - Tuples can now be reversed with
Tuple.reverse().
- Tuples can now be concatenated with
-
The
peekable()function has been added toiter. This allows users to peek at the next element of an iterator without advancing it.
String and text
-
Stringhas had its UTF-8 guarantees strengthened.- It now has three separate constructors when converting raw bytes
(
Span[Byte]) to aString:String(from_utf8=...): Raises an error if the bytes are invalid UTF-8String(from_utf8_lossy=...): Converts invalid UTF-8 byte sequences into the(U+FFFD, �)replacement character and does not raise an error.String(unsafe_from_utf8=...): Unsafely assumes the input bytes are valid UTF-8 without any checks.
append_byte()has been deprecated and has been replaced withappend().
- It now has three separate constructors when converting raw bytes
(
-
The
strip(),lstrip(), andrstrip()methods ofStringandStringSlicenow support stripping multi-byte unicode codepoints. Additionallylstrip()andstrip()will no longer produce invalid UTF-8 if the "chars" string contains characters sharing their first byte with a character in the string to be stripped. -
StringLiteral.format()now emits a compile-time constraint error if the format string is invalid (instead of a runtime error)."Hello, {!invalid}".format("world")
# note: constraint failed: Conversion flag "invalid" not recognized. -
StringSlice.char_length()has been renamedcount_codepoints(). The same function was added toStringandStringLiteral. -
Added a
CStringSliceas a type-safe way to interact with nul-terminated c-style strings (const char*). -
Removed
String.join(*Writable)overload that takes a variadic sequence of arguments, as it could be ambiguous with the remainingString.join(Span[Writable])overload. -
Removed the
Int.__init__(self, value: StringSlice, base: UInt)constructor. Users should callatol()directly.
Pointer and memory
-
OwnedDLHandle.get_symbol()now returnsUnsafePointer[T, MutAnyOrigin]instead ofUnsafePointer[T, ImmutAnyOrigin]. The vast majority of symbols loaded from shared libraries are meant to be used mutably, and it's safer to go from mutable → immutable (via.as_immutable()) than from immutable → mutable (via.unsafe_mut_cast[True]()). Users who need immutable pointers can now simply call.as_immutable()on the result. -
The "LegacyUnsafePointer" type has been changed to take its mutability as a first inferred parameter without a default, rather than a later explicit parameter with a default value of true. We recommend moving off of this type as soon as possible, but to roughly emulate the prior behavior, try out:
from memory import LegacyUnsafePointer
comptime UnsafePointer = LegacyUnsafePointer[mut=True, *_, **_] -
External origins are now expressed using type level
{Mut,Immut,}ExternalOriginaliases instead of being spelled likeOrigin[True].external, improving consistency with other origin types. -
UnsafePointercan now be initialized from a raw memory address using theunsafe_from_addressinitializer. -
alloc()now has adebug_assert()ensuring count is non-negative.
Explicitly-destroyed types
-
Basic support for explicitly-destroyed types in the standard library is now available. Explicitly-destroyed types—sometimes referred to as "linear types"—are types that don't define a
__del__()method that the compiler can call automatically to destroy an instance. Instead, a explicitly-destroyed type must provide a named method takingdeinit selfthat the programmer is required to call explicitly whenever an owned instance is no longer used.The updated
AnyTypetrait can be used in parameters to denote generic code that supports object instances that can't be implicitly destroyed.Span,UnsafePointer,Pointer, andOwnedPointercan point to explicitly-destroyed types.- Added
UnsafePointer.destroy_pointee_with(), for destroying explicitly-destroyed types in-place using a function pointer to the type's destructor.
- Added
List,InlineArray,Optional,Variant,VariadicListMem, andVariadicPackcan now contain explicitly-destroyed types.Variant.take()now takesdeinit selfinstead ofmut self.- Added
Variant.destroy_with()for destroying an explicitly-destroyed type in-place by passing in the type's destructor function. - The
*argslanguage syntax for arguments now supports explicitly-destroyed types.
Iterator.Elementno longer requiresImplicitlyDestructible.UnsafeMaybeUninitializedcan now contain explicitly-destroyed types.
Type system and traits
-
Using a new 'unconditional conformances' technique leveraging
conforms_to()andtrait_downcast()to perform "late" element type conformance checking, some standard library types are now able to conform to traits that they could not previously:List,Dict,Set,Deque,InlineArray, andLinkedListnow conform toWritable,Stringable, andRepresentable.Listalso conforms toEquatable.Pointer,ArcPointer, andOwnedPointernow conform toWritable.Iterator,Tuple,Variant, andOptionalno longer require their element types to beCopyable.
-
The
Iteratortrait and for-each loop have removed the__has_next__()method and now use a__next__()method thatraisesStopIteration. This follows Python precedent better, is more convenient to implement, and can be a minor performance win in some cases. -
The
ImplicitlyBoolabletrait has been removed. This trait enabled types to implicitly convert toBool. This behavior was rarely used, and could lead to subtle bugs, for example mistakenly passing types likeIntorUnsafePointerto an argument expecting aBoolwould silently compile successfully. -
The
Errortype no longer conforms toBoolableorDefaultable. Errors must now be constructed with meaningful context, and optionality should be expressed throughOptional[Error]rather than treating errors as boolean values. -
The
Copyabletrait now refines theMovabletrait. This means that structs and generic algorithms that already requireCopyabledon't need to also mention they requireMovable. -
The
EqualityComparabletrait has been deprecated in favor ofEquatable, which has identical functionality. -
We have removed
Identifiablefrom enum-like types (such asDTypeandAddressSpace). This change is related to the idea thatIdentifiableis for comparing memory addresses.
Python interoperability
-
The
ConvertibleFromPythontrait and associated initializers now have a required keyword argument. Before:Int(pyObj). After:Int(py=pyObj). This avoids ambiguities in cases where either multiple overloads could apply, or where implicit conversions toPythonObjectcould mask that a Python operation was happening. -
PythonObjectnow supports implicit conversion fromNone, allowing more natural Python-like code:var obj: PythonObject = None # Now works without explicit PythonObject(None)
fn returns_none() -> PythonObject:
return None # Implicit conversion
File I/O and OS
-
Basic file I/O operations in the
iomodule are now implemented natively in Mojo using directlibcsystem calls (open(),close(),read(),write(),lseek()). TheFileHandletype no longer depends on CompilerRT functions, providing better performance and transparency. Error handling now includes errno-based messages for improved diagnostics. -
The
os.processsubmodule has been added with utilities to spawn and wait on processes. These useposix_spawn()and do not go through the system shell. -
Various OS wrapper functions now include the value of
errnoin the raised error message. -
The
osmodule now exposes alink()function, wrapping the unixlink(2)system call. -
The
osmodule now exposes asymlink()function, wrapping the unixsymlink(2)syscall.
GPU programming
-
Layoutno longer conforms toImplicitlyCopyable. The motivation for this change is that it was easy to accidentally materialize aLayoutat runtime using methods such asLayout.size(). There are a few downstream changes users will have to adapt to with this change. Below are some common patterns which will help with this transition:from layout import Layout
fn foo(a: Layout) -> Layout:
return a.copy() # `a` needs to be explicitly copied.
fn bar(var a: Layout) -> Layout:
return a^ # If a is taken by value, one can use the transfer operator.
fn baz():
comptime a = Layout.row_major(4, 4)
# `a` needs to be materialized because `foo` returns a type that
# is not ImplicitlyCopyable (`Layout`).
var b = foo(materialize[a]())
# `bar` moves `b` by value, but `foo` also takes `b` by reference,
# so we need to explicitly copy.
_ = bar(b.copy())
_ = foo(b)
# Since we are no longer using `b`, it's fine to take it by value here.
_ = bar(b^)
# Since `Layout.size()` returns an `Int`, we can use a `comptime`
# expression to compute the return value without materializing `a`.
for i in range(comptime (a.size())):
... -
DeviceContext.enqueue_function_checked()andDeviceStream.enqueue_function_checked()have been renamed toenqueue_function(). Similarly,DeviceContext.compile_function_checked()has been renamed tocompile_function(). -
DeviceContext.enqueue_function()andDeviceContext.enqueue_function_experimental()now automatically inferfunc_attributetoFuncAttribute.MAX_DYNAMIC_SHARED_SIZE_BYTES(shared_mem_bytes)whenshared_mem_bytesis specified butfunc_attributeis not, for NVIDIA GPUs with allocations > 48KB. This eliminates the need to specify the same shared memory size twice in many cases, reducing boilerplate and preventing mismatched values. On AMD GPUs or for allocations ≤ 48KB, explicitfunc_attributevalues should be provided when needed. -
The
inlined_assembly()function is now publicly exported from thesysmodule, allowing users to embed raw assembly instructions directly into Mojo code. This provides fine-grained control over hardware operations using LLVM-style inline assembly syntax. Example:from sys import inlined_assembly
# Convert bfloat16 to float32 on NVIDIA GPU using PTX assembly.
var result = inlined_assembly[
"cvt.f32.bf16 $0, $1;",
Float32,
constraints="=f,h",
has_side_effect=False,
](my_bf16_as_int16)
Formatting and output
-
Writerhas been reworked to support only UTF-8 data instead of arbitraryBytesequences. Thewrite_bytes()method has been replaced withwrite_string().- In line with these changes,
String'swrite_bytes()method has also been deprecated, and its initializer__init__(out self, *, bytes: Span[Byte])has had its keyword argument renamed tounsafe_from_utf8. This brings it more in line with the existingStringSliceconstructors and explicitly states that construction from arbitrary bytes is inherently unsafe.
- In line with these changes,
-
WriterandWritablehave been moved into a newformatmodule and out ofio. These traits are not directly related to binary i/o, but are rather closely tied to type/value string formatting. -
The
Writabletrait now supports debug formatting through an optionalwrite_repr_to()method, called byrepr()and the{!r}format specifier. Additionally,repr()and string formatting methods (.format()onString,StringSlice, andStringLiteral) now acceptWritabletypes, enabling efficient formatting without intermediate string allocations. To preserve existing behavior, types implementing bothStringable & RepresentableandWritablewill continue usingStringable & Representablemethods; only types implementingWritablealone will use the new code paths. -
Counternow conforms toWritable,Stringable, andRepresentable. -
black_box()has been added to thebenchmarkutilities as a way to prevent the compiler from aggressively optimizing out values. Similar tokeep(), however, it returns its argument.
Other library changes
-
The
UIntstruct has been replaced by a newUInttype alias toScalar[DType.uint]. This is a major change that enables more powerful generic programming by abstracting over unsigned SIMD data dtypes of machine word size.This change will likely break code that relies on implicit conversions to/from
UIntandInt/SIMD. TheSIMDtype is also slightly less foldable at compile time, which can cause some code in where clauses, comptime expressions, and other code in parametric contexts to now fail or crash. These shortcomings will be addressed in subsequent patches as needed. -
Implicit conversion between
IntandUInthas been removed. -
The
randommodule now uses a pure Mojo implementation based on the Philox algorithm (via an internal wrapper), replacing the previousCompilerRTC++ dependency. The Philox algorithm provides excellent statistical quality, works on both CPU and GPU, and makes random number generation fully transparent and source-available. Note that this changes the random number sequence for a given seed value, which may affect tests or code relying on reproducible sequences. -
StringableRaisinghas been deprecated and its usages in the stdlib have been removed. -
Variadicnow haszip_types(),zip_values(), andslice_types().
Tooling changes
-
The Mojo compiler now supports the
-Werrorflag, which treats all warnings as compilation errors. This is useful for enforcing stricter code quality standards, particularly in CI/CD pipelines. The flag works with the Mojo compiler tools (mojo run,mojo build,mojo package,mojo doc). When used with--disable-warnings, warnings are promoted to errors first, so the errors are not suppressed.- The counterpart
-Wno-errorflag disables treating warnings as errors. When both flags are specified, the last one wins.
- The counterpart
-
Specifying CUDA architectures with
--target-acceleratornow expects a sm version string rather than just a compute capability. For example,--target-accelerator=nvidia:80should be changed to--target-accelerator=nvidia:sm_80. If an incorrect format is used for the version, the compiler will default to the lowest supported sm version. -
The Mojo LSP server now debounces document updates to reduce CPU usage during rapid typing. Previously, every keystroke triggered a full document parse; now updates are coalesced with a 150ms delay, reducing parse frequency by 10-50x during active editing.
-
The Mojo compiler now "diffs" very long types in error messages to explain what is going on in an easier to understand way.
-
Elaboration error printing with different levels of verbosity offers control on how parameter values are displayed as part of elaboration errors when function instantiation fails.
--elaboration-error-verbose=valuenow takes a value, where:no-paramsmeans don't display any concrete parameter values. This is helpful to collapse recursion-related error messages into shorter blobs.simple-paramsmeans display concretized parameter values for simple types, including numeric types and strings, in a user-friendly format (default value).all-paramsmeans show all concrete parameter values. This is for advanced programmers who don't mind reading MLIR attributes but want more visibility of parameter values.
-
--elaboration-max-depthis added to control maximum elaborator instantiation depth. This (unsigned) value helps to detect compile time recursion. The default isstd::numeric_limits<unsigned>::max(). -
Docstring validation with
--validate-doc-stringsnow emits an error when anfnfunction is declared to raise an error (raises) but it's missing aRaisesdocstring (previously it emitted only a warning). Because Mojo automatically treats alldeffunctions as raising functions, we don't enforceRaisesdocs fordeffunctions (to avoid noisy false positives). -
Docstring validation now includes
comptimealiases. The--diagnose-missing-doc-stringsflag now checks that public aliases have properly formatted docstrings (summary ends with period, starts with capital letter). Parametric aliases are also checked for properParameters:sections. -
The
--validate-doc-stringsflag has been deprecated formojo docand removed from other tools (mojo build,mojo run,mojo package). Use-Werrorinstead to treat warnings as errors. -
The Mojo compiler now supports the
-Xlinkerflag to pass options on directly to the linker, for example:mojo build -Xlinker -lfoo main.mojoNote: this option only has an effect with
mojo build. Withmojo run, the arguments are ignored and a warning is issued. -
The Mojo compiler now supports the
--experimental-export-fixitflag formojo build,mojo run, andmojo package. This flag exports fix-its to a YAML file compatible withclang-apply-replacements, instead of applying them directly. This is useful for integrating Mojo's fix-it suggestions into external tooling workflows. The flag is mutually exclusive with--experimental-fixit(which applies fix-its directly). -
The Mojo Debugger
mojo break-on-raisefeature now works correctly with multiple targets in a debugger instance. The setting is per-target.
Experimental changes
Changes described in this section are experimental and may be changed, replaced, or removed in future releases.
-
Mojo now supports compile-time trait conformance check (via
conforms_to()) and downcast (viatrait_downcast()). This allows users to implement features like static dispatching based on trait conformance. For example:fn maybe_print[T : AnyType](maybe_printable : T):
@parameter
if conforms_to(T, Writable):
print(trait_downcast[Writable](maybe_printable))
else:
print("[UNPRINTABLE]") -
Added support for
DTypeexpressions inwhereclauses:fn foo[dt: DType]() -> Int where dt == DType.int32:
return 42Currently, the following expressions are supported:
- equality and inequality
is_signed(),is_unsigned(),is_numeric(),is_integral(),is_floating_point(),is_float8(),is_half_float()
-
Added support for
SIMDexpressions inwhereclauses:fn foo[dt: DType, x: Int]() -> Int where SIMD[dt, 4](x) + 2 > SIMD[dt, 4](0):
return 42Currently, the following expressions are supported:
- default construction and construction from
IntandIntLiteral - equality, inequality, and other comparison operators
- addition, subtraction, and multiplication
- bitwise logical operations, excluding shifts
- default construction and construction from
Removed
-
The following deprecated GPU compatibility modules have been removed:
gpu.id- Usefrom gpu import block_idx, thread_idx, ...insteadgpu.block- Usefrom gpu.primitives.block import ...insteadgpu.warp- Usefrom gpu.primitives.warp import ...insteadgpu.cluster- Usefrom gpu.primitives.cluster import ...insteadgpu.grid_controls- Usefrom gpu.primitives.grid_controls import ...insteadgpu.mma- Usefrom gpu.compute.mma import ...insteadgpu.mma_operand_descriptor- Usefrom gpu.compute.mma_operand_descriptor import ...insteadgpu.mma_util- Usefrom gpu.compute.mma_util import ...insteadgpu.mma_sm100- Usefrom gpu.compute.arch.mma_nvidia_sm100 import ...insteadgpu.semaphore- Usefrom gpu.sync.semaphore import ...insteadgpu.tcgen05- Usefrom gpu.compute.arch.tcgen05 import ...instead
-
The DeviceContext
enqueue_function_unchecked()andcompile_function_unchecked()have been removed. Please migrate the code to useenqueue_function()andcompile_function(). -
NDBufferhas been removed. UseLayoutTensorinstead. -
The
UnsafePointer.offset()method is now deprecated. Use pointer arithmetic instead:# Before
new_ptr = ptr.offset(n)
# After
new_ptr = ptr + n
Fixed
- Several reflection-related compiler crashes have been fixed:
- Issue #5731: Reflection
functions now work correctly on builtin types like
Int,NoneType, andOrigin. - Issue #5732:
get_type_name()now handles types with constructor calls in their parameters (likeA[B(True)]) when extracted viastruct_field_types(). - Issue #5723:
get_type_name()now handles nested parametric types fromstruct_field_types(). - Issue #5754:
struct_field_type_by_name()now works correctly when usingReflectedType.Tas a type annotation. - Issue #5808:
rebind()andrebind_var()now accept downcasted types fromstruct_field_types(), allowing patterns likerebind_var[types[i]](downcast[types[i], Trait]()^).
- Issue #5731: Reflection
functions now work correctly on builtin types like
- Issue #5618: Compiler crash when should be implicit conversion error.
- Issue #5361: mojo doc crashes on alias of parametrized function with origin.
- Issue #5137: Tail call optimization doesn't happen for tail recursive functions with raises.
- Issue #5138: Tail call optimization doesn't happen for functions with local stack temporaries.
- Mojo no longer complains about "cannot infer parameter X" when unrelated type checking errors happen in complex parametric code. It now gives much more useful and actionable error messages in these cases.
time.sleep()now works correctly for durations longer than 1 millisecond on NVIDIA GPUs. Previously, sleep durations were silently capped at 1ms due to a hardware limitation in the underlyingnanosleepintrinsic. AMD GPUs now have basic sleep support using thes_sleepinstruction, which is sufficient for spin-wait backoff operations though it doesn't provide accurate wall-clock timing. Additionally,global_perf_counter_ns()is now exported from thetimepackage for GPU code that needs nanosecond-resolution timing.- Issue #5578: ownership
overloading not working when used with
ref. - Issue #1850: Mojo assumes string literal at start of a function is a doc comment
- Issue #4501: Incorrect parsing of incomplete assignment
- Issue #4765: Parser accepts pointless var ref a = n binding form
Codepoint.unsafe_decode_utf8_codepoint()no longer returnsCodepoint(0)(NUL) when passed an empty span. Instead, adebug_assert()now enforces the requirement that the input span be non-empty, consistent with the function's existing safety contract.- Issue #5635:
Dequeshrink reallocation incorrectly handled empty deque withcapacity > min_capacity.
Special thanks
Special thanks to our community contributors:
Alex Maldonado (@aalexmmaldonado), Bernhard Merkle (@bmerkle), Brian Grenier (@bgreni), Christoph Schlumpf (@christoph-schlumpf), Duba Sirisha (@SirishaDuba), Ethan Wu (@YichengDWu), Gunasekar (@sdgunaa), Hristo (Izo) G. (@izo0x90), Johannes Laute (@jaidmin), Jordan Rinder (@jrinder42), josiahls (@josiahls), Krish Gupta (@KrxGu), Magi Sharma J (@magi8101), Manuel Saelices (@msaelices), martinvuyk (@martinvuyk), Richard Johnsson (@jmikaelr), Ritesh Goru (@BlackWingedKing), Ross Campbell (@RossCampbellDev), RWayne93 (@RWayne93), soraros (@soraros), Sören Brunk (@sbrunk), turakz (@turakz), Valentin Erokhin (@saviorand), YeonguChoe (@YeonguChoe)