v25.3 (2025-05-06)
✨ Highlights
-
Parts of the Mojo standard library continue to be progressively open sourced! Packages that are open sourced now include:
For more information, see the Standard library reference and the Standard library source.
-
Parts of the MAX AI kernels library continue to be progressively open sourced! Packages that are open sourced now include:
For more information, see the MAX AI kernels library reference and the MAX AI kernels source.
-
Trait compositions are now supported via the
&syntax. A trait composition combines two traits into one logical trait whose constraint set is the union of the constraint sets of the two original traits. For more information, see Trait compositions in the Mojo Manual. -
String types in Mojo got several significant improvements. See Standard library changes for details.
Language changes
-
Mojo can now use user-declared
__merge_with__()dunder methods to merge values when using different types in ternary operations. This has been adopted to allow pointers to work naturally with the ternary operator, for examplevar x = one_pointer if cond else other_pointer. -
Auto-parameterization now extends to struct metatypes. For example, this declaration
fn foo[M: type_of(StringLiteral[_])]will auto-parameterize on the unbound parameter ofStringLiteral. -
The Mojo compiler now warns about stores to values that are never used, e.g.:
x = foo(); x = bar()will warn about the first assignment toxbecause it is overwritten. You can generally address this by deleting dead code, or by assigning to_instead:_ = foo(); x = bar(). You may also encounter this in variable declarations, e.g.var x = 0; ...; x = foo(). In this case, change the variable to being declared as uninitialized, e.g.var x: Int. You may also silence this warning entirely for a variable by renaming it to start with an underscore, e.g._x. -
The Mojo compiler now warns about obsolete use of
mut selfin initializers, please switch over tofn __init__(out self)instead. -
deffunctions now require type annotations on arguments, and treat a missing return type as returningNone. Previously these defaulted to theobjecttype which led to a variety of problems. Support forobjecthas been removed until we have time to investigate a proper replacement.
Standard library changes
String types in Mojo got several significant improvements:
-
The
Stringtype no longer copies data fromStringLiteralandStaticStringsince they are known-static-constant values. This allows us to make construction from these values be implicit, which improves ergonomics and performance together. It also implements the "small string optimization", which avoids heap allocation for common short strings. On a 64-bit system,Stringcan hold up to 23 bytes inline. Its copy constructor is now O(1), performing string data copy lazily on mutation. -
The types
StringSliceandStaticStringare now part of the prelude, there is no need to import them anymore. These are useful for code that just needs a "view" of string data, not to own and mutate it. -
The
StringLiteraltype has been moved to a more reliable "dependent type" design where the value of the string is carried in a parameter instead of a stored member. This defines away a category of compiler crashes when working withStringLiteralthat involved attempting to manipulate aStringLiteralat run time. As a consequence of this change, many APIs should switch to usingStaticStringinstead ofStringLiteral. For more information on this "dependent type" design for literals, see the proposal, Fixing Simple Literals in Mojo. -
Stringsupports a newString(unsafe_uninit_length=x)constructor andstr.resize(unsafe_uninit_length=x)for clients that want to allocate space that they intend to fill in with custom unsafe initialization patterns. TheString(ptr=x, length=y)constructor has been removed. -
Stringsupports working with legacy C APIs that assume null termination, but the details have changed:Stringis now no longer implicitly null-terminated, which means that it is incorrect to assume thatstr.unsafe_ptr()will return a null-terminated string. For that, use thestr.unsafe_cstr_ptr()method. It now requires the string to be mutable in order to make null-termination lazy on demand. This improves performance for strings that are not passed to legacy APIs. -
The
Listtype has been improved similarly toStringto reduce inconsistency and enable power-user features, including removing addingList(unsafe_uninit_length=x)andlist.resize(unsafe_uninit_size=n)methods avoid initialized memory that the caller plans to overwrite. -
Setnow conforms to theCopyabletrait so you can store sets in other types of collections (for example, as values in aDict). -
The following traits have been removed in favor of trait composition:
EqualityComparableCollectionElement,RepresentableCollectionElement,TestableCollectionElement,Testable,StringableIdentifiable,StringableCollectionElement,IntervalPayload,WritableCollectionElement,ComparableCollectionElement,BoolableCollectionElement,EqualityComparableWritableCollectionElement,EqualityComparableWritableCollectionElementNew,CollectionElementNew,WritableCollectionElementNew.For example, you can replace
EqualityComparableCollectionElementwithEqualityComparable & CollectionElement.StringableCollectionElementwas already deprecated and scheduled to be removed; it can be replaced withWritable & CollectionElement. -
The
PythonObjecttype is being reworked in preparation for some improvements to Mojo-Python interoperability:-
Since virtually any operation on a
PythonObjectcan raise, thePythonObjectstruct no longer implements the following traits:ImplicitlyBoolable,ImplicitlyIntable. -
PythonObjectis no longer implicitly constructible from tuple or list literals. For example,var x : PythonObject = [1, 2, "foo"]is no longer accepted. Instead, please use the newPython.list()andPython.tuple()factory methods. For example:var x = Python.list(1, 2, "foo")(The
list()andtuple()factory methods were originally added onPythonObject, but have been moved to thePythonstruct.)We hope to re-enable literal syntax in the future as the standard library matures.
-
PythonObject.from_borrowed_ptr()has been removed in favor of a constructor with a keyword-onlyfrom_borrowed_ptrargument. -
The deprecated
PythonObject.to_float64()method has been removed. Use theFloat64()constructor, instead.
-
-
Spannow has aswap_elements()method which takes two indices and swaps them within the span. -
Pointernow has aget_immutable()method to return a newPointerwith the same underlying data but with anImmutableOrigin. -
You can now forward a
VariadicPackwhere all values areWritableto a writer usingWritableVariadicPack:from utils.write import WritableVariadicPack
fn print_message[*Ts: Writable](*messages: *Ts):
print("message:", WritableVariadicPack(messages), "[end]")
x = 42
print_message("'x = ", x, "'")message: 'x = 42' [end]In this example the variadic pack is buffered to the stack in the
printcall along with the extra arguments, before doing a single syscall to write to stdout. -
debug_assert()in AMD GPU kernels now behaves the same as on NVIDIA, printing the thread information and variadic args passed after the condition:from gpu.host import DeviceContext
fn kernel():
var x = 1
debug_assert(x == 2, "x should be 2 but is: ", x)
def main():
with DeviceContext() as ctx:
ctx.enqueue_function[kernel](grid_dim=2, block_dim=2)Running
mojo run -D ASSERT=all [filename]will output:At /tmp/test.mojo:5:17: block: [0,0,0] thread: [0,0,0] Assert Error: x should be 2 but is: 1
At /tmp/test.mojo:5:17: block: [0,0,0] thread: [1,0,0] Assert Error: x should be 2 but is: 1
At /tmp/test.mojo:5:17: block: [1,0,0] thread: [0,0,0] Assert Error: x should be 2 but is: 1
At /tmp/test.mojo:5:17: block: [1,0,0] thread: [1,0,0] Assert Error: x should be 2 but is: 1 -
The
constrained[cond, string]()function now accepts multiple strings that are printed concatenated on failure, so you can use:constrained[cond, "hello: ", String(n), ": world"]()This is more compile-time efficient and somewhat more ergonomic than using string concatenation.
-
pathlib.Path.write_text()now accepts aWritableargument instead of aStringableargument. This makes the function more efficient by removing a String allocation. -
Added
pathlib.Path.write_bytes()which enables writing raw bytes to a file. -
Added
os.path.split_extension()to split a path into its root and extension. -
Added
os.path.is_absolute()to check if a given path is absolute or not. -
One can now specify the consistency model used in atomic operations with the default being sequential consistency. The consistency models are defined in the
Consistencystruct. -
Added
Variant.is_type_supported()method. (PR #4057) Example:def takes_variant(mut arg: Variant):
if arg.is_type_supported[Float64]():
arg = Float64(1.5)
def main():
var x = Variant[Int, Float64](1)
takes_variant(x)
if x.isa[Float64]():
print(x[Float64]) # 1.5 -
The
typeparameter ofSIMDhas been renamed todtype. -
The
is_power_of_two(x)function in thebitpackage is now a method onInt,UIntandSIMD. -
The
Pointer.address_of(...)andUnsafePointer.address_of(...)functions have been deprecated. Please use thePointer(to=...)andUnsafePointer(to=...)constructors instead. Conceptually, this is saying "please initialize aPointer(a reference, if you will) to some other address in memory. In the future, theseaddress_of()functions will be removed.
Tooling changes
-
Fixed SIMD boolean display in debugger: SIMD boolean values now display correctly with proper bit extraction.
-
Improved language server performance: The language server now avoids parsing more than it needs to, improving performance across the board.
-
The Mojo compiler is now able to interpret all arithmetic operations from the
indexdialect that are used in methods ofIntandUInttypes. That allows users to finally compute constants at compile time:alias a: Int = 1000000000
alias b: Int = (5 * a) // 2Previously, the compiler would throw the error "cannot fold operation".
-
Added a new
--emit-llvmoption to themojo buildcommand, which allows users to emit LLVM IR. When--emit-llvmis specified, the build process will: compile mojo code to LLVM IR, save the IR to a .ll file (using the same name as the input file), and print the IR to stdout for immediate inspection.
Other changes
- The syntax for adding attributes to an
__mlir_opis now limited to inherent attributes (those defined by the op definition). Most users will not need to attach other kinds of attributes, and this helps guard against typos and mojo code getting outdated when the dialect changes.
❌ Removed
-
The
SIMD.roundeven()method has been removed from the standard library. This functionality is now handled by theround()function. -
Error messages about the obsolete
borrowedandinoutkeywords, as well as the obsolete-> Int as namesyntax have been removed. -
The
objecttype has been removed. -
utils.numerics.ulphas been removed. Use theulp()function from themathpackage instead. -
Several free functions that were deprecated in the 25.2 release have now been removed. This includes:
- The
strfree function. Use theStringconstructor instead. - The
intfree function. Use theIntconstructor instead. - The
boolfree function. Use theBoolconstructor instead. - The
floatfree function. Use theFloat64constructor instead.
- The
-
Removed deprecated
DeviceContextmethodscopy_sync()andmemset_sync(). -
The
unroll()utility has been removed. Use the@parameter forconstruct instead.from utils.loop import unroll
# Before
@always_inline
@parameter
fn foo[i: Int]():
body_logic[i]()
unroll[foo, iteration_range]()
# After
@parameter
for i in range(iteration_range):
body_logic[i]() -
The
InlinedStringtype has been removed. UseStringinstead which now supports the Small String Optimization (SSO). -
The
AsBytestrait has been removed.
🛠️ Fixed
-
#3510 -
PythonObjectdoesn't handle largeUInt64correctly. -
#3847 - Count leading zeros can't be used on
SIMDat compile time. -
#4198 - Apple M4 is not properly detected with
sys.is_apple_silicon(). -
#3662 - Code using
llvm.assumecannot run at compile time. -
#4273 -
count_leading_zerosdoesn't work for vectors with size > 1 at compile time. -
#4320 - Intermittent miscompilation with bytecode imported traits.
-
#4281 - MAX does not support RTX 5000-series GPUs.
-
#4163 - Corner case in initializers.
-
#4360 - Fix constructor emission for parameterized types conforming to a trait composition.
-
#4362 - Function call with
IntLiteralincorrectly eliminated despite side-effects. -
#4431 - [BUG] Python.evaluate doesn't handle null termination correctly.
-
#4492 - Fix
StringSlice.replaceseg fault.
Special thanks
Special thanks to our community contributors:
@auris, @bgreni, @christianbator, @KamilGucik, @kasmith11, @martinvuyk, @ratulb, @rd4com, @sora, @thatstoasty, and @winding-lines.