Skip to main content
Version: 1.0

v25.3 (2025-05-06)

✨ Highlights

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 example var 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 of StringLiteral.

  • 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 to x because 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 self in initializers, please switch over to fn __init__(out self) instead.

  • def functions now require type annotations on arguments, and treat a missing return type as returning None. Previously these defaulted to the object type which led to a variety of problems. Support for object has been removed until we have time to investigate a proper replacement.

Standard library changes

String types in Mojo got several significant improvements:

  • The String type no longer copies data from StringLiteral and StaticString since 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, String can hold up to 23 bytes inline. Its copy constructor is now O(1), performing string data copy lazily on mutation.

  • The types StringSlice and StaticString are 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 StringLiteral type 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 with StringLiteral that involved attempting to manipulate a StringLiteral at run time. As a consequence of this change, many APIs should switch to using StaticString instead of StringLiteral. For more information on this "dependent type" design for literals, see the proposal, Fixing Simple Literals in Mojo.

  • String supports a new String(unsafe_uninit_length=x) constructor and str.resize(unsafe_uninit_length=x) for clients that want to allocate space that they intend to fill in with custom unsafe initialization patterns. The String(ptr=x, length=y) constructor has been removed.

  • String supports working with legacy C APIs that assume null termination, but the details have changed: String is now no longer implicitly null-terminated, which means that it is incorrect to assume that str.unsafe_ptr() will return a null-terminated string. For that, use the str.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 List type has been improved similarly to String to reduce inconsistency and enable power-user features, including removing adding List(unsafe_uninit_length=x) and list.resize(unsafe_uninit_size=n) methods avoid initialized memory that the caller plans to overwrite.

  • Set now conforms to the Copyable trait so you can store sets in other types of collections (for example, as values in a Dict).

  • 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 EqualityComparableCollectionElement with EqualityComparable & CollectionElement. StringableCollectionElement was already deprecated and scheduled to be removed; it can be replaced with Writable & CollectionElement.

  • The PythonObject type is being reworked in preparation for some improvements to Mojo-Python interoperability:

    • Since virtually any operation on a PythonObject can raise, the PythonObject struct no longer implements the following traits: ImplicitlyBoolable, ImplicitlyIntable.

    • PythonObject is 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 new Python.list() and Python.tuple() factory methods. For example:

      var x = Python.list(1, 2, "foo")

      (The list() and tuple() factory methods were originally added on PythonObject, but have been moved to the Python struct.)

      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-only from_borrowed_ptr argument.

    • The deprecated PythonObject.to_float64() method has been removed. Use the Float64() constructor, instead.

  • Span now has a swap_elements() method which takes two indices and swaps them within the span.

  • Pointer now has a get_immutable() method to return a new Pointer with the same underlying data but with an ImmutableOrigin.

  • You can now forward a VariadicPack where all values are Writable to a writer using WritableVariadicPack:

    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 print call 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 a Writable argument instead of a Stringable argument. 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 Consistency struct.

  • 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 type parameter of SIMD has been renamed to dtype.

  • The is_power_of_two(x) function in the bit package is now a method on Int, UInt and SIMD.

  • The Pointer.address_of(...) and UnsafePointer.address_of(...) functions have been deprecated. Please use the Pointer(to=...) and UnsafePointer(to=...) constructors instead. Conceptually, this is saying "please initialize a Pointer (a reference, if you will) to some other address in memory. In the future, these address_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 index dialect that are used in methods of Int and UInt types. That allows users to finally compute constants at compile time:

    alias a: Int = 1000000000
    alias b: Int = (5 * a) // 2

    Previously, the compiler would throw the error "cannot fold operation".

  • Added a new --emit-llvm option to the mojo build command, which allows users to emit LLVM IR. When --emit-llvm is 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_op is 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 the round() function.

  • Error messages about the obsolete borrowed and inout keywords, as well as the obsolete -> Int as name syntax have been removed.

  • The object type has been removed.

  • utils.numerics.ulp has been removed. Use the ulp() function from the math package instead.

  • Several free functions that were deprecated in the 25.2 release have now been removed. This includes:

    • The str free function. Use the String constructor instead.
    • The int free function. Use the Int constructor instead.
    • The bool free function. Use the Bool constructor instead.
    • The float free function. Use the Float64 constructor instead.
  • Removed deprecated DeviceContext methods copy_sync() and memset_sync().

  • The unroll() utility has been removed. Use the @parameter for construct 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 InlinedString type has been removed. Use String instead which now supports the Small String Optimization (SSO).

  • The AsBytes trait has been removed.

🛠️ Fixed

  • #3510 - PythonObject doesn't handle large UInt64 correctly.

  • #3847 - Count leading zeros can't be used on SIMD at compile time.

  • #4198 - Apple M4 is not properly detected with sys.is_apple_silicon().

  • #3662 - Code using llvm.assume cannot run at compile time.

  • #4273 - count_leading_zeros doesn'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 IntLiteral incorrectly eliminated despite side-effects.

  • #4431 - [BUG] Python.evaluate doesn't handle null termination correctly.

  • #4492 - Fix StringSlice.replace seg fault.

Special thanks

Special thanks to our community contributors:

@auris, @bgreni, @christianbator, @KamilGucik, @kasmith11, @martinvuyk, @ratulb, @rd4com, @sora, @thatstoasty, and @winding-lines.