IMPORTANT: To view this page as Markdown, append `.md` to the URL (e.g. /docs/manual/basics.md). For the complete Mojo documentation index, see llms.txt.
Skip to main content

Mojo nightly

Version: 1.1.0.dev2026081919

This version is still a work in progress.

Highlights

  • Code that performs many implicit conversions, most visibly large collection literals, compiles faster: the compiler no longer runs parameter inference on constructors that cannot be used for an implicit conversion in the first place. Files that are mostly data, such as the standard library's Unicode lookup tables, compile about 1.3x faster.

Language enhancements

  • A thin function type can now carry trailing where clauses, constraining the parameters it declares. This lets a generic algorithm state what it promises the function it is handed, instead of leaving the constraint to be restated at every binding site.

    comptime Kernel = def[w: Int](Int) thin -> None where (
    w > 0, "width must be positive"
    )

    def apply[F: Kernel](x: Int):
    F[4](x) # ok
    F[0](x) # error: violated constraint

    The clause binds to the innermost function type, so a declaration-level where that follows a function-type result needs that result parenthesized:

    def make[n: Int]() -> (def() thin -> None) where n > 0: ...

Language changes

  • Binding a constrained function to a function type that declares no matching where clause is now an error, instead of silently dropping the constraint. Declare the obligation on the function type (now that a thin function type can carry a trailing where clause) or bind a function that does not require it. Passing an unconstrained function where a constrained type is expected is still allowed and still free.

  • Renamed the @parameter decorator on parametric closures to @__parameter. The deprecated @parameter if / @parameter for forms are unchanged; prefer comptime if / comptime for for compile-time control flow.

  • The module & package system:

    • Directories may now have "namespace" semantics; a single directory name may resolve across distinct locations on disk which share that name.

      # .
      # ├── one
      # │   └── foo
      # │      └── bar.mojo
      # └── two
      #    └── foo
      #       └── baz.mojo
      #
      # Compiles with -Ione -Itwo
      import foo.bar
      import foo.baz
    • Importing functions with the same name from different modules, combining them into one overload set, is now an error, following a period of deprecation.

    • Intra-package accesses without explicit imports are now an error, following a period of deprecation.

Library stabilizations

  • String
    • def __init__(out self):
    • def __init__(out self, *, capacity_bytes: Int):
    • def reserve_bytes(mut self, new_capacity_bytes: Int, /):

Library changes

  • CompilationTarget has a new is_arm() predicate, and is_x86() now reports the architecture rather than SSE4 availability. Both read the architecture from the target triple, so they no longer vary with --target-cpu. This changes is_x86() on x86 targets without SSE4.1 — most visibly the baseline x86-64 CPU, where it used to return False. Use has_sse4(), has_avx2(), and friends to gate code on a specific instruction set.

  • CompilationTarget can now describe RISC-V targets: is_riscv(), is_rv32(), and is_rv64() report the architecture, and has_riscv_extension["m"]() reports a single ISA extension by its lowercase LLVM name. An extension implied by another counts as present, so a target built with d also reports f. It is always False on a non-RISC-V target, and rejects an uppercase name at compile time.

    Selecting a RISC-V CPU or ISA string now resolves the extensions it implies, so --target-cpu=sifive-e31 and --march=rv32imac both report m, a, and c. Previously either one reported only the base integer ISA.

  • Bencher.bench_function() now takes a raising closure.

  • StringDict now conforms to Writable when its value type is Writable, matching the existing behavior of Dict. This lets you print() a StringDict or convert it to a String.

  • The chars argument of strip(), lstrip() and rstrip() on StringSpan, String and StringLiteral is now an ImmStringSpan, so a mutable string is accepted as chars, including the string being stripped (s.strip(s)).

  • StringDict.__getitem__() now accepts a StringSpan, so you can index a StringDict with a borrowed string view without first allocating a String just to perform the lookup.

  • Renamed the variadic type-list parameter on Tuple and VariadicPack to Ts, standardizing the naming convention used across the standard library. The old name, element_types, remains as a deprecated alias.

  • Added experimental DType.float6_e2m3fn and DType.float6_e3m2fn, the two 6-bit encodings from the Open Compute microscaling specification. Both are finite-only, so neither has an inf nor a NaN encoding.

    These are experimental storage formats for packed weights rather than general-purpose numeric types, and standard library support is deliberately partial. As with the existing DType.float4_e2m1fn, they are excluded from is_numeric(), arithmetic is not implemented, and converting to or from another floating-point type is unsupported on every target, so values cannot be printed either.

  • Array now conforms to Defaultable when its type T is also Defaultable.

  • Array now supports concatenation with the + operator when its type T is Movable. Both operands are consumed and their elements are moved into the new array, whose length is the sum of the operands' lengths.

  • Array now supports repetition with the * operator when its type T is Copyable and the multiplier is an integer literal. The array is consumed: its elements are copied into all but the last repetition and moved into the last one.

  • Deprecated is_trivially_movable(), is_trivially_copyable(), and is_trivially_deletable() in std.memory in favor of IsTriviallyMovable[T], IsTriviallyCopyable[T], and IsTriviallyDeinitable[T] in std.traits. The replacements are comptime predicates rather than functions, so drop the call parens at use sites, for example IsTriviallyCopyable[T] instead of is_trivially_copyable[T]().

  • Renamed UnsafeMaybeUninit to MaybeUninit. It conforms to Movable, Copyable/ImplicitlyCopyable, and Deinitable only when the contained type's own move, copy, or implicit deinitializer is trivial, since moving, copying, or destroying a MaybeUninit only touches its raw bits, never the contained value's own lifecycle methods. Gating conformance this way turns what would otherwise be silent memory-safety bugs into compile-time errors.

  • Atomic is now parameterized on a value type T instead of a DType. Update call sites from Atomic[DType.float32] to Atomic[Float32]. The atomic operations (load(), store(), fetch_add(), compare_exchange(), and so on) still only support Scalar types.

  • Added Pointer[T].unsafe_write(def() -> T), which initializes the pointee with the value returned by a closure, constructing it directly in place rather than moving an already-constructed value there. Unlike unsafe_write(var T), this does not require the pointee type to be Movable.

  • List's element type is now bounded by AnyType instead of Movable.

  • Added write() to MaybeUninit and Pointer, as a safe counterpart to unsafe_write() for types that are trivially deinitializable (for example Int). Since a trivial deinitializer is a no-op, overwriting a live value through write() can't leak a resource, so it's callable without first destroying the previous value. Prefer it over unsafe_write() whenever the pointee type is trivially deinitializable.

  • Pointer.mut_cast is now deprecated. Developers should prefer using explicit mutabilites at the callsite via MutPointer or ImmPointer. If mut casting is needed (it should try to be avoided) - you can use unsafe_mut_cast.

  • Added ptr() to StringLiteral, CStringSlice, ArcPointer, and OwnedPointer, deprecating their unsafe_ptr() methods. These types always hold a valid, live value, so a pointer to it is never unsafe.

  • The following APIs have been migrated to unified closures: sort, debug_assert, Span.apply.

  • Uncaught exceptions now print to stderr, not stdout.

Tooling changes

  • mojo doc now reports the condition of a conditional trait conformance, and the generated API docs show it alongside the trait. Previously the condition was dropped, making a conditional conformance indistinguishable from an unconditional one. Also fixed rendering of some where clauses.

Removed

This release completes the removal of APIs deprecated during the v1.0 cycle.

  • Removed the temporary InlineArray alias for Array, including its re-exports from std.collections and the prelude. Use Array directly.

  • Removed the std.gpu.profiler module and its ProfileBlock context manager. It timed host wall-clock, not GPU work, and reported the elapsed time with the operands reversed. Time a block of host code with perf_counter_ns() directly, and use a GPU profiler such as Nsight Systems or rocprof for device timings.

  • Removed memcmp and its std.memory re-export. Use unsafe_memcmp instead.

  • Removed String.set_byte_length(), an internal helper that set the length field without reserving capacity.

  • Removed the validate parameter from b64decode(), which now always validates. Passing validate=False did not skip any work on valid input; it only turned characters outside the base64 alphabet into silently corrupt output bytes. Drop [validate=True] from existing calls; calls that relied on the default now raise instead of returning garbage.

  • Removed the origin aliases left over from the Immut to Imm and External to Untracked renames. Use the surviving spelling in each case: ImmOrigin for ImmutOrigin, ImmUnsafeAnyOrigin for ImmutUnsafeAnyOrigin, ImmStaticOrigin for StaticConstantOrigin, UntrackedOrigin for ExternalOrigin, MutUntrackedOrigin for MutExternalOrigin, and ImmUntrackedOrigin for both ImmutUntrackedOrigin and ImmutExternalOrigin.

  • Removed the pre-unification pointer aliases MutUnsafePointer, ImmUnsafePointer, ImmutUnsafePointer, ImmutOpaquePointer, ImmutPointer, and OptionalUnsafePointer. Use MutPointer, ImmPointer, ImmOpaquePointer, and OptionalPointer instead. UnsafePointer itself remains available, but is deprecated in favor of Pointer.

  • Removed the raw memory functions superseded by their unsafe_-prefixed spellings: memcpy, memset, memset_zero, uninit_move_n, uninit_copy_n, and destroy_n. Use unsafe_memcpy, unsafe_memset, unsafe_memset_zero, unsafe_uninit_move_n, unsafe_uninit_copy_n, and unsafe_destroy_n instead.

  • Removed the size aliases left from the size to length rename: SIMD.size, Array.size, TypeList.size, and the SIMDSize alias for SIMDLength. Use length and SIMDLength.

  • Removed the as_immutable() and get_immutable() methods on Pointer, Span, and StringSpan. Use as_imm().

  • Removed the ImmutSpan alias. Use ImmSpan.

  • Removed String.as_string_slice(). Construct a StringSpan from the string instead: StringSpan(my_string).

  • Removed the ImplicitlyDestructible and ImplicitlyDeletable aliases. Use Deinitable.

  • Removed the deprecated ownership-transfer methods: List.steal_data() and OwnedPointer.steal_data() are now unsafe_take_allocation(), OwnedPointer.take() is into_inner(), and Variant.take() and Variant.unsafe_take() are unwrap() and unsafe_unwrap().

  • Removed the Pointer methods superseded by their unsafe_-prefixed spellings: as_noalias_ptr(), destroy_pointee(), destroy_pointee_with(), init_pointee_move(), init_pointee_copy(), and init_pointee_move_from(). Use unsafe_as_noalias(), unsafe_deinit_pointee(), unsafe_deinit_pointee_with(), unsafe_write(), and unsafe_write_move_from(). The Pointer.type alias for Pointer.T is gone as well.

  • Removed the ConditionalType type function and the std.utils.type_functions module. Use the ternary expression T if cond else U.

  • Removed trait_downcast(). Constrain on the trait instead, with conforms_to(type_of(src), Trait) in a where clause or a comptime assert.

  • Removed the parametric benchmark.run[func]() overloads. Pass the function as an argument to run(f) instead, which accepts a unified closure.

  • Removed AnyCoroutine, Coroutine and RaisingCoroutine from the prelude, and made the module that defines them private. Mojo's async support is unfinished, and these types being globally visible led people to build on an API that carries no stability guarantees. async def is unaffected: the compiler still synthesizes these types for you, so they continue to appear in inferred types and diagnostics. There is no supported way to name them directly.

  • Removed the async task API from the public std.runtime.asyncrt module, which is now private. initialize_runtime() and parallelism_level() are unaffected and have moved up to the std.runtime package, so import them from std.runtime instead of std.runtime.asyncrt.

  • Removed support for .mojopkg files after a period of deprecation. Use .mojoc files instead.

Fixed

  • A where clause naming a type that an enclosing where clause constrained to a tighter trait can now be proven. Calling a method declared where Ts.contains[T]() with such a T failed with lacking evidence to prove correctness, even though T was plainly in Ts.

  • mojo build can cross-compile to RISC-V again. Emitting LLVM IR, assembly, or an object for a riscv32 or riscv64 triple failed with target '...' is not supported by this build.

  • mojo build --print-supported-targets no longer lists targets that the compiler cannot generate code for.

  • mojo build --emit asm and --emit llvm now always write the offload kernel files next to the host output file. Building a kernel that an earlier build had already compiled could write them into the earlier build's output directory, or skip them with no diagnostic.

  • Parametric raises now accepts any primary expression as the thrown type in a function signature, matching the syntax positions where types otherwise appear. This most notably fixes raises Self.SomeAssocType on trait and struct methods, which would previously fail with an error. The parenthesized workaround (raises (Self.DriveErrorType)) is no longer required.

  • An integer range() with a step of zero is now always empty. It previously used to be an infinite loop - iterating forever at runtime, and hanging the compiler at comptime.

  • A strided range() no longer iterates forever when the element after the last one falls outside the element type, as in range(UInt8(250), UInt8(255), UInt8(2)). The cursor used to wrap past the type's limit and land back inside the range, so iteration restarted near the opposite limit and never agreed with len(). This affected signed and unsigned ranges in both step directions.

  • reversed() on a scalar range() no longer yields an empty iterator when the range starts within one step of the element type's limit, as in reversed(range(Int8.MIN, Int8.MIN + 8, Int8(1))). Unsigned ranges, and ranges whose span overflows their element type, are fixed by the same change.

    Reversing an already-reversed range, as in reversed(reversed(range(10))), is now a compile-time error.

  • Fixed ceildiv() returning 0 for unsigned operands near the type's maximum value. The unsigned code path computed numerator + denominator - 1, which overflows and wraps for large operands; it now derives the ceiling from the floor division and remainder instead.

  • Counter.most_common(n) now returns all elements when n exceeds the number of unique elements, matching Python, instead of aborting.

  • os.path.join() now inserts separators based on the accumulated path rather than the first argument, so join("/", "a", "b") returns /a/b (previously /ab) and join("a", "b/", "c") returns a/b/c (previously a/b//c).

  • base64.b64decode() now raises an error when the input length is not divisible by 4 instead of reading past the end of the input (or aborting when asserts are enabled).

  • On macOS, os.stat() and os.lstat() no longer return a negative st_mode for regular files. The underlying mode_t and nlink_t C type aliases were declared as signed 16-bit integers, but macOS defines them as unsigned, so any mode with the S_IFREG bit set (every regular file) sign-extended into a negative Int.

  • PythonObject no longer leaks a CPython reference per positional argument when calling a Python object, nor when setting an item, attribute, or set literal element.