Mojo v1.1.0
Highlights
-
Contextually inferred member references: a leading-dot form such as
.redor.float64now resolves against the expected type of the expression, so you can omit a redundant type name wherever context already supplies it. For example, you can useSIMD[.float64, 4]instead ofSIMD[DType.float64, 4]. See Language enhancements. -
Performance improvements: compile times and generated code both improve. Code performing many implicit conversions compiles faster,
Listgrowth is no longer quadratic, and t-strings both compile faster and emit smaller objects. See Language enhancements and Library performance improvements. -
Continued migration to unified closures: the move from legacy closures (passed as compile-time parameters) to unified closures (passed as runtime arguments) carries on. More APIs now take their closures as runtime arguments, and the parameter forms are gone. On the language side, the
@parameterdecorator on parametric closures is now@__parameter, used only for declaring legacy closures. See Unified closures and Language changes. -
Continuing library stabilizations: the deliberately small set of stable standard library APIs introduced in 1.0 grows again, adding signatures on
String,SIMD, andList. See Library stabilizations. -
Continuing cleanup: the deprecation cleanup begun in the 1.0 cycle completes. The legacy
fn,alias, and__comptime_assertkeywords and the@parameter ifand@parameter forsyntax are gone, along with the rename aliases, the redundantIntoverloads, and the pre-unsafe_spellings of the raw memory and pointer APIs. Support for.mojopkgfiles and the public async task API have also been removed. See Removed.
Documentation
-
Revised the quickstart: the install matrix is now a plain project-setup checklist, every section ends in a "Checkpoint" callout, and it now covers functions and error handling (
try/except/else/finally). -
Expanded the get-started tutorial with a 45-60 minute time estimate up front, guidance for readers coming from another language on using the Checkpoints to map unfamiliar syntax, clearer sample code, and concrete next steps.
Language enhancements
-
Mojo now supports contextually inferred member references: a leading-dot form such as
.redor.float64resolves against the expected type of the expression, so you can omit a redundant type name when context already supplies it. Static methods, parametric static methods, parentheses, attribute chains, and typed collection literals all work:struct Color(ImplicitlyCopyable):comptime red = Color(...)comptime green = Color(...)@staticmethoddef hsb_to_rgb(h: Int, s: Int, b: Int) -> Color:return Color(...)def opacity(self, amount: Float64) -> Color:return Color(...)def __init__(out self, ...):def takes_color(c: Color):def takes_colors(colors: List[Color]):takes_color(.green)takes_color(.hsb_to_rgb(120, 100, 50))takes_color(.red.opacity(0.5))var x: Color = .redtakes_colors([.red, .green])Without a contextual type,
.memberis an error. -
The new
@inline(value)decorator selects a function's inline level. Name a level with the new preludeInlineLevelstruct:.always,.nodebug,.never, or.automatic, wherenodebugmatches@always_inline("nodebug")and also drops the inlined debug info. Spell the struct out, as inInlineLevel.never, where there is no contextual type to infer it from.The value need not be a constant: any comptime expression works, including a parameter, so one definition can be inlined or not per instantiation.
@inline(.always)def doubled(x: Int) -> Int:return x * 2@inline(policy)def scaled[policy: InlineLevel](x: Int) -> Int:return x * 3def main():print(doubled(1) + scaled[.never](2))The compiler resolves a constant when it parses the decorator, and resolves a value that depends on a parameter once it binds that parameter.
-
Two inline decorators that disagree are now an error rather than one silently winning.
@always_inlinetogether with@no_inlinepreviously compiled, picking whichever came first; write only the one you mean. -
Unknown declaration errors now suggest a unique near-miss spelling from the enclosing scopes (for example
coun→count), with a replace-token fixit. -
A
thinfunction type can now carry trailingwhereclauses, constraining the parameters it declares. This lets a generic algorithm state what it promises the function it is handed, instead of leaving every binding site to restate the constraint.comptime Kernel = def[w: Int](Int) thin -> None where (w > 0, "width must be positive")def apply[F: Kernel](x: Int):F[4](x) # okF[0](x) # error: violated constraintThe clause binds to the innermost function type, so a declaration-level
wherethat follows a function-type result needs that result parenthesized:def make[n: Int]() -> (def() thin -> None) where n > 0: ... -
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 changes
-
Implicit variable declaration now produces an error instead of a warning. The walrus operator also only updates an existing variable—it doesn't implicitly declare a new one.
-
A walrus expression now always yields its right-hand side, uniformly for every kind of target.
-
Use of the
readargument convention is now a hard error, following a period of deprecation; useimminstead. -
Binding a constrained function to a function type that declares no matching
whereclause is now an error, instead of silently dropping the constraint. Declare the obligation on the function type (now that athinfunction type can carry a trailingwhereclause) 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
@parameterdecorator on parametric closures to@__parameter, which is now used only for declaring legacy closures. Removed the@parameter ifand@parameter forforms; usecomptime ifandcomptime forfor compile-time control flow. -
The module and package system:
-
Directories may now have "namespace" semantics; a single directory name may resolve across distinct locations on disk that share that name.
# .# ├── one# │ └── foo# │ └── bar.mojo# └── two# └── foo# └── baz.mojo## Compiles with -Ione -Itwoimport foo.barimport 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 access without an explicit
importstatement is now an error, following a period of deprecation.
-
Library stabilizations
-
def __init__(out self):def __init__(out self, *, capacity_bytes: Int):def reserve_bytes(mut self, new_capacity_bytes: Int, /):
-
def __init__(out self):def __eq__(self, rhs: Self) -> Bool:def __len__(self) -> Int:
-
def append(mut self, var value: Self.T, /):
Library performance improvements
-
List.extend()andList.resize()now grow geometrically, so repeatedly extending or resizing by a small increment is no longer quadratic. As a result,capacity()can report more than you requested.reserve()is unchanged and still allocates exactly what you request. -
Files with many t-string literals (
t"...") compile faster: the compile-time step that encodes each literal's format string (part of elaboration, not the whole compile) is about 7x faster. The effect on total build time scales with how many t-string literals a file has. -
TStringno longer carries its format string as a parameter, so distinct t-string literals share one specialization instead of emitting a function each, and the message-formatting paths ofdebug_assert()andabort()are now out of line. A file with 2,500 distinct literals emits 14 functions instead of 2,510, shrinking its object by about 85%.
Library changes
Unified closures
Many more APIs have migrated from legacy closures (passed as parameters) to unified closures (passed as arguments).
-
Migrated the following APIs to unified closures:
sort(),debug_assert(), andSpan.apply(). -
std.algorithm'stile(),unswitch(),tile_and_unswitch(), andtile_middle_unswitch_boundaries()now take their workgroup function as a trailing closure argument instead of a legacy closure parameter, as intile[sizes](offset, bound, workgroup_function=body). -
The benchmarking APIs now take unified closures instead of legacy closures, and the parameter forms are gone:
-
Bench.bench_function()now takes a raising closure. -
The formerly zero-argument
Bench.bench_function()overload now takes a raising closure as a runtime argument. Removed the compile-time parameter formbench_function[fn]()for a raising zero-argument body. -
Removed the remaining compile-time parameter forms of
Bench.bench_function()andBencher.iter(). Pass the closure as a runtime argument. -
Bencher.iter_preproc()now takes its closures as runtime arguments instead of compile-time parameters, along with an explicit state value that it passes mutably to both: the preprocessing function prepares the state before each timed call of the benchmarked function, so you no longer have to shuttle state through mutable captures. -
Bench.bench_with_input()now takes its benchmark closure as a runtime argument. Its register-passable overload accepts both non-raising and raising closures. -
Bencher.iter_custom()now takes its closure only as a runtime argument. Removed the compile-time parameter form.
-
Collections
-
Arraynow conforms toComparablewhen its element type does, adding<,<=,>, and>=. The ordering is lexicographic: the first differing element decides, so[1, 5] < [2, 3]isTrue. -
Arraynow conforms toDefaultablewhen its typeTis alsoDefaultable. -
Arraynow supports concatenation with theconcat()method when its typeTisMovable.concat()consumes both operands and moves their elements into the new array, whose length is the sum of the operands' lengths. -
Arraynow supports repetition with therepeat()method when its typeTisCopyable.repeat()consumes the array: it copies the elements into all but the last repetition and moves them into the last one. -
Array[T, N]has a newfill_with=constructor that calls a function with each index in[0, N)and writes its result into that position, replacing theArray(uninitialized=True)plus manual fill-loop idiom. -
List's element type is now bounded byAnyTypeinstead ofMovable. -
Listhas a newfill_with=constructor that calls a function with each index in[0, length)and writes its result into that position, without requiring the element type to beMovable. -
StringDictnow conforms toWritablewhen its value type isWritable, matching the existing behavior ofDict. This lets youprint()aStringDictor convert it to aString. -
StringDict.__getitem__()now accepts aStringSpan, so you can index aStringDictwith a borrowed string view without first allocating aStringjust to perform the lookup. -
You can now construct a
Counterfrom any iterable of values, not just aList, for exampleCounter(["a", "a", "b"])orCounter(String("aaab").bytes()). This replaces the previousCounter(items: List[V])constructor.
Pointer and memory
-
Deprecated
is_trivially_movable(),is_trivially_copyable(), andis_trivially_deletable()instd.memoryin favor ofIsTriviallyMovable[T],IsTriviallyCopyable[T], andIsTriviallyDeinitable[T]instd.traits. The replacements arecomptimepredicates rather than functions, so drop the call parens at use sites, for exampleIsTriviallyCopyable[T]instead ofis_trivially_copyable[T](). -
Renamed
UnsafeMaybeUninittoMaybeUninit. It conforms toMovable,Copyable/ImplicitlyCopyable, andDeinitableonly when the contained type is trivially movable, copyable, or deinitable, since moving, copying, or destroying aMaybeUninitonly 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. -
Added a
deinit()free function for anyDeinitabletype, to explicitly extend a value's lifetime up to a specific point and run its deinitializer there. -
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. Unlikeunsafe_write(var T), this does not require the pointee type to beMovable. -
Added
write()toMaybeUninitandPointer, as a safe counterpart tounsafe_write()for types that are trivially deinitializable (for exampleInt). Since a trivial deinitializer is a no-op, overwriting a live value throughwrite()can't leak a resource, so it's callable without first destroying the previous value. Prefer it overunsafe_write()whenever the pointee type is trivially deinitializable. -
Deprecated
Pointer.mut_cast(). Prefer explicit mutabilities at the call site, usingMutPointerorImmPointer. Where a mut cast is unavoidable, useunsafe_mut_cast(). -
Renamed
CStringSlicetoCStringSpan, matching theSpan-based naming of the other non-owning view types (StringSpan,Span). The oldCStringSlicename remains available as a compatibility alias. Likewise,as_c_string_slice()is nowas_c_string_span(). -
Added
ptr()toStringLiteral,CStringSpan,ArcPointer, andOwnedPointer, deprecating theirunsafe_ptr()methods. These types always hold a valid, live value, so a pointer to it is never unsafe.
Traits and type system
-
OwnedDLHandlenow conforms toBoolable. -
Hasher.update()now takes anImmSpan[Byte, _]instead ofSome[Hashable]. Change code such ashasher.update(some_subobject)in__hash__implementations tosome_subobject.__hash__(hasher). -
Renamed the variadic type-list parameter on
TupleandVariadicPacktoTs, standardizing the naming convention used across the standard library. The old name,element_types, remains as a deprecated alias. -
Atomicis now parameterized on a value typeTinstead of aDType. Update call sites fromAtomic[DType.float32]toAtomic[Float32]. The atomic operations (load(),store(),fetch_add(),compare_exchange(), and so on) still only supportScalartypes. -
Renamed
any_satisfies()andall_satisfies()onTypeListandParameterListtoany()andall().
Compilation targets
-
CompilationTargethas a newis_arm()predicate, andis_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 changesis_x86()on x86 targets without SSE4.1—most visibly the baselinex86-64CPU, where it used to returnFalse. Usehas_sse4(),has_avx2(), and friends to gate code on a specific instruction set. -
CompilationTargetcan now describe RISC-V targets:is_riscv(),is_rv32(), andis_rv64()report the architecture, andhas_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 withdalso reportsf. It is alwaysFalseon 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-e31and--march=rv32imacboth reportm,a, andc. Previously either one reported only the base integer ISA.
Python interoperability
-
Python functions exposed through
PythonModuleBuilder.def_function(),PythonTypeBuilder.def_method(), andPythonTypeBuilder.def_staticmethod()no longer have a library-imposed limit on positional arguments. -
std.python.numpynow handles multi-dimensional NumPy arrays, not just 1-D:-
copy_to_numpy_tensor()copies aSpaninto a new NumPy array of a given shape. The shape is aCoord, so extents may be compile-time (Idx[N]) or runtime (Int) in any mix. -
from_numpy_tensor()borrows an N-D C-contiguous array as aNumPyView, which holds the buffer and its shape together and indexes asview[i, j].
from std.python.numpy import copy_to_numpy_tensor, from_numpy_tensorfrom std.utils.coord import Coord, Idxvar values: List[Float64] = [0, 1, 2, 3, 4, 5]var arr = copy_to_numpy_tensor(values, Coord(Idx[2], Idx[3]))var view = from_numpy_tensor[DType.float64, 2](arr)var value = view[1, 2]The existing 1-D
copy_to_numpy_array()andfrom_numpy_array()are unchanged. -
Other library changes
-
The
simdmodule moved fromstd.builtinto the top level ofstd, soSIMDand its aliases now live instd.simd. Nothing changes for code that relies on the prelude; an explicitfrom std.builtin.simd import ...becomesfrom std.simd import .... -
Coordhas a newreplace[at](value)method that returns aCoordwith the element atatswapped forvalue, keeping the other elements' types. A statically known element (ComptimeInt) has no runtime storage to assign into, so overwriting one with a runtime value yields aCoordof a different type rather than mutating in place. Unlikemake_dynamic(), which converts every element to aScalar, the untouched dimensions keep their compile-time values:var c = Coord(ComptimeInt[3](), ComptimeInt[4]())var moved = c.replace[1](Int64(7)) # Coord(ComptimeInt[3](), Int64(7)) -
The
charsargument ofstrip(),lstrip(), andrstrip()onStringSpan,String, andStringLiteralis now anImmStringSpan, socharsnow accepts a mutable string, including the string being stripped (s.strip(s)). -
Added experimental
DType.float6_e2m3fnandDType.float6_e3m2fn, the two 6-bit encodings from the Open Compute microscaling specification. Both are finite-only, so neither has an inf or 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 fromis_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. -
Uncaught exceptions now print to
stderr, notstdout. -
You can now construct a
Coordfrom anArray[Scalar[dtype], rank], mirroring the existingIndexListconstructor. AnArraycarries no compile-time extents, so the result is an all-dynamicCoord.
GPU programming
- The
max.gpupackage now mirrors everything reachable fromstd.gpu, making it a complete entry point for accelerator programming, and thestd.gpupackage is now private, asstd._gpu.max.gpuis the only public source for the GPU primitives, and its API reference is the only published one;/docs/std/gpu/...pages redirect to/api/mojo/max/gpu/.... Replacefrom std.gpu import ...withfrom max.gpu import ...; a failedstd.gpuimport carries a note pointing at the new home.
Tooling changes
-
mojo docnow reports the condition of a conditional trait conformance, and the generated API docs show it alongside the trait. Previouslymojo docdropped the condition, making a conditional conformance indistinguishable from an unconditional one. Also fixed rendering of somewhereclauses. -
The new
@__doc_inlinedecorator on an import statement documents the imported symbols in the importing module, so a package can document an API it re-exports from private modules. The decorator does not support wildcard (import *) or renamed (import ... as ...) imports.@__doc_inlinefrom ._impl import Widget, make_widget -
mojo buildandmojo runcan now report where a compile spends its time.--mlir-timingtimes every MLIR pass and analysis, and--llvm-timingdoes the same for LLVM, each printing a report to stderr when the compilation finishes, which formojo runis before the program starts.--mlir-timing-displaygroups the MLIR report as atree(the default), which nests by pipeline structure, or as alist, which aggregates by pass name and sorts by total time. These options are hidden; use--help-hiddento list them.Two things shape what the numbers mean. First,
--llvm-timingpins the compile to one thread and overrides--num-threads, because LLVM's timers are global to the process and are not thread safe, so its report measures the work LLVM does rather than the cost of a parallel build. Second, passes served from the compilation cache never run, so a warm cache reports little and an object cache hit leaves the LLVM report empty; pointMODULAR_CACHE_DIRat an empty directory to time a whole pipeline. -
--timing-jsonemits both timing reports as JSON, and--timing-filewrites them to a file. The two are independent, so the text reports can go to a file and the JSON can go to stderr.The JSON is one object per command, holding an
mlirmember, anllvmmember, or both, depending on which timing the command asked for. A command that asks for JSON but for no timing writes{}, so a consumer can parse the output without first checking which timing options ran.
Removed
This release completes the removal of APIs deprecated during the v1.0 cycle.
- Removed the legacy constructs replaced in 1.0, including the
fn,alias, and__comptime_assertkeywords and the@parameter ifand@parameter forsyntax.
Deprecated aliases and renamed APIs
-
Removed the temporary
InlineArrayalias forArray, including its re-exports fromstd.collectionsand the prelude. UseArraydirectly. -
Removed the origin aliases left over from the
ImmuttoImmandExternaltoUntrackedrenames. Use the surviving spelling in each case:ImmOriginforImmutOrigin,ImmUnsafeAnyOriginforImmutUnsafeAnyOrigin,ImmStaticOriginforStaticConstantOrigin,UntrackedOriginforExternalOrigin,MutUntrackedOriginforMutExternalOrigin, andImmUntrackedOriginfor bothImmutUntrackedOriginandImmutExternalOrigin. -
Removed the pre-unification pointer aliases
MutUnsafePointer,ImmUnsafePointer,ImmutUnsafePointer,ImmutOpaquePointer,ImmutPointer, andOptionalUnsafePointer. UseMutPointer,ImmPointer,ImmOpaquePointer, andOptionalPointerinstead.UnsafePointeritself remains available, but is deprecated in favor ofPointer. -
Removed the
sizealiases left from thesizetolengthrename:SIMD.size,Array.size,TypeList.size, and theSIMDSizealias forSIMDLength. UselengthandSIMDLength. -
Removed the
as_immutable()andget_immutable()methods onPointer,Span, andStringSpan. Useas_imm(). -
Removed the
ImmutSpanalias. UseImmSpan. -
Removed
String.as_string_slice(). Construct aStringSpanfrom the string instead:StringSpan(my_string). -
Removed the
ImplicitlyDestructibleandImplicitlyDeletablealiases. UseDeinitable. -
Removed the deprecated ownership-transfer methods:
List.steal_data()andOwnedPointer.steal_data()are nowunsafe_take_allocation(),OwnedPointer.take()isinto_inner(), andVariant.take()andVariant.unsafe_take()areunwrap()andunsafe_unwrap().
Redundant Int overloads
-
Removed redundant
Intoverloads across the standard library.Intis an alias forScalar[DType.int], so the genericSIMDoverloads already acceptIntarguments and returnInt; call sites need no changes.-
count_leading_zeros(),count_trailing_zeros(),bit_reverse(),byte_swap(),pop_count(),log2_ceil(),next_power_of_two(), andprev_power_of_two()instd.bit;readfirstlane()instd.sys; andumod()instd.math.uutils. -
rotate_bits_left()androtate_bits_right()instd.bit. TheSIMDoverloads now accept any integral element type instead of only unsigned ones—rotation is a pure bit-pattern operation, so signed and unsigned rotate identically—and therefore handleIntarguments directly. -
sqrt(),fma(),align_down(),align_up(),clamp(), andiota()fromstd.math.
-
Unsafe-prefixed replacements
-
Removed the APIs superseded by their
unsafe_-prefixed spellings:-
memcmp()and itsstd.memoryre-export. Useunsafe_memcmp()instead. -
The raw memory functions
memcpy(),memset(),memset_zero(),uninit_move_n(),uninit_copy_n(), anddestroy_n(). Useunsafe_memcpy(),unsafe_memset(),unsafe_memset_zero(),unsafe_uninit_move_n(),unsafe_uninit_copy_n(), andunsafe_destroy_n()instead. -
The
Pointermethodsas_noalias_ptr(),destroy_pointee(),destroy_pointee_with(),init_pointee_move(),init_pointee_copy(), andinit_pointee_move_from(). Useunsafe_as_noalias(),unsafe_deinit_pointee(),unsafe_deinit_pointee_with(),unsafe_write(), andunsafe_write_move_from(). ThePointer.typealias forPointer.Tis gone as well.
-
Async APIs
-
Removed
AnyCoroutine,Coroutine, andRaisingCoroutinefrom the prelude, and made the module that defines them private. Mojo's async support is unfinished, and their global visibility led people to build on an API that carries no stability guarantees.async defis 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.asyncrtmodule, which is now private.initialize_runtime()andparallelism_level()are unaffected and have moved up to thestd.runtimepackage, so import them fromstd.runtimeinstead ofstd.runtime.asyncrt.
Other removals
-
Removed
String.set_byte_length(), an internal helper that set the length field without reserving capacity. -
Removed the
validateparameter fromb64decode(), which now always validates. Passingvalidate=Falsedid 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
ConditionalTypetype function and thestd.utils.type_functionsmodule. Use the ternary expressionT if cond else U. -
Removed
trait_downcast(). Constrain on the trait instead, withconforms_to(type_of(src), Trait)in awhereclause or acomptime assert. -
Removed the parametric
benchmark.run[func]()overloads. Instead, pass the function as an argument torun(f), which accepts a unified closure. -
Removed support for
.mojopkgfiles after a period of deprecation. Use.mojocfiles instead.
Fixed
Compiler and comptime
-
The compiler can now prove a
whereclause naming a type that an enclosingwhereclause constrained to a tighter trait. Calling a method declaredwhere Ts.contains[T]()with such aTfailed withlacking evidence to prove correctness, even thoughTwas plainly inTs. -
Parametric
raisesnow accepts any primary expression as the thrown type in a function signature, matching the syntax positions where types otherwise appear. This most notably fixesraises Self.SomeAssocTypeon trait and struct methods, which previously failed with an error. The parenthesized workaround (raises (Self.DriveErrorType)) is no longer required. -
A spurious
attempt to resolve a recursive reference to declarationerror no longer fires on valid code. Resolving the signature of a trait method inherited from a parent trait no longer forces the parent's default body to resolve, so a default whose body reaches a type conforming to the inheriting trait no longer forms a resolution cycle. -
The compiler no longer treats an implicit conversion whose constructor candidate it can neither prove nor disprove in the asking scope as a definitive rejection, so a conversion that depends on a parametric constraint alias now resolves once the constraint is known.
-
Origin inference now works through an implicit constructor that binds its operand by
ref. Passing an rvalue to a function taking an origin-parameterized type, as intake_wrapper(a + b)where the implicit__init__takesref[origin] value, materializes a temporary and infers its origin instead of failing. -
to_layout_tensor()no longer hangs the compiler on a tensor with a nested layout. The type-onlycoord_to_int_tuplenow recurses on a nestedCoord's own element types, andto_layout_tensor()flattens shape and stride so nested layouts get one entry per leaf; flat layouts are unchanged. -
The compiler no longer crashes while printing a parameter list that contains a positional variadic bound to a single type, so calls such as
helper(Bag[Leaf, tail=1]())now report a proper conversion error. -
Recursive or excessively deep comptime call graphs no longer crash the compiler. Parameter-expression inlining now detects cycles and bounds its recursion depth, so an unbounded expression such as
f[n]callingf[n + 1]reports an error instead of overflowing the stack. -
Forwarding a
VariadicPackthat came from somewhere other than a call site, such as one returned from a function declared-> VariadicPack[..], no longer crashes the compiler. Origin tracking did not handle a pack it had not itself constructed. -
A comptime call that names its callee concretely no longer aborts in the compile-time interpreter. The compiler looked the callee up by name without checking that it had finished elaborating, so the interpreter received a body still holding unresolved parameters.
-
The compiler no longer folds a loop result that was only constant on some paths to that constant. Constant propagation could conclude a loop result was known while it had not yet analyzed some of the loop's
breakorcontinueedges, producing a value only one path actually computed.
Numerics and SIMD
-
#6850 -
SIMD.__init__(py=...)now reads unsigned dtypes through the unsigned CPython entry point (PyLong_AsSize_t). Constructing an unsignedSIMDfrom a Python int in[2**63, 2**64)no longer overflows, and a negative Python int now raises instead of silently wrapping to the maximum value. -
#6921 - A union whose widest member is a
SIMD[DType.bool, N]withN > 1, such asOptional[SIMD[DType.bool, 2]], now compiles. -
#6851 -
hash()on a floating-pointSIMDvalue now normalizes the sign of zero, sohash(-0.0) == hash(0.0). Hashing the raw bit pattern broke theHashablecontract that equal values hash equally: aDictorSetcould hold both-0.0and0.0as separate keys even though they compare equal, and a lookup could then return a value stored under the other key. -
Fixed
ceildiv()returning0for unsigned operands near the type's maximum value. The unsigned code path computednumerator + denominator - 1, which overflows and wraps for large operands; it now derives the ceiling from the floor division and remainder instead. -
sqrt(),rsqrt(),pow(),sin(),cos(),log10(), andlog1p()now work on Apple GPUs. Mojo now widens narrow floats such asbfloat16tofloat32around the underlying Metal intrinsic, which has nobfloat16overload, andlog1p()no longer evaluates its polynomial infloat64, a type Metal does not have at all; that previously surfaced as an LLVM verifier abort rather than a diagnostic.
Memory and pointers
-
Destroying an
OwnedDLHandlethat holds a null handle no longer crashes the process. -
unsafe_uninit_move_n()andunsafe_uninit_copy_n()withoverlapping=Truenow handle an overlap in either direction whenTis not trivially movable or copyable. They always walked front-to-back, so adestabovesrcoverwrote elements that had not been moved or read yet. -
Every value of a struct type whose
@align(N)exceeds its natural alignment is now aligned toN, including every element of an array or aListof that type. -
unsafe_memcpy()now chunks GPU copies by register width. It passed a bit width where an element count belonged, making each chunk eight times too wide; NVPTX then gave up on vectors and copied byte by byte. Error-reporting code, which does most of the standard library's copying, shrinks substantially as a result.
Tooling and build
-
mojo buildcan cross-compile to RISC-V again. Emitting LLVM IR, assembly, or an object for ariscv32orriscv64triple failed withtarget '...' is not supported by this build. -
mojo build --print-supported-targetsno longer lists targets that the compiler cannot generate code for. -
mojo build --emit asmand--emit llvmnow 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. -
Importing a precompiled package now resolves that package's own recorded dependencies from the importing file's location, so the compiler again finds a dependency that lives beside the main file.
Ranges and iteration
-
An integer
range()with a step of zero is now always empty. It previously produced 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 inrange(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 withlen(). This affected signed and unsigned ranges in both step directions. -
reversed()on a scalarrange()no longer yields an empty iterator when the range starts within one step of the element type's limit, as inreversed(range(Int8.MIN, Int8.MIN + 8, Int8(1))). The same change fixes unsigned ranges, and ranges whose span overflows their element type.Reversing an already-reversed range, as in
reversed(reversed(range(10))), is now a compile-time error.
Strings and encoding
-
#6831 -
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). -
#3446 -
b64decode()now ignores ASCII whitespace in its input, so base64 text wrapped across lines by a MIME encoder or thebase64command-line tool decodes without the caller stripping it first. Only the six ASCII whitespace bytes are ignored; unlike Python'sbase64.b64decode(), any other byte outside the base64 alphabet still raises. The "length must be divisible by 4" error now counts only the significant characters. -
#6834 -
atol()(and thereforeInt(String)) now raises for every value outside theIntrange. Values just pastInt.MAX(such asInt.MAX + 1) no longer wrap silently, andInt.MINparses correctly by design rather than by wraparound.atol()now also raises instead of aborting on a string that holds only whitespace, or only whitespace and a sign.
OS and system
-
os.path.join()now inserts separators based on the accumulated path rather than the first argument, sojoin("/", "a", "b")returns/a/b(previously/ab) andjoin("a", "b/", "c")returnsa/b/c(previouslya/b//c). -
#6838 - On macOS,
os.stat()andos.lstat()no longer return a negativest_modefor regular files. Mojo declared the underlyingmode_tandnlink_tC type aliases as signed 16-bit integers, but macOS defines them as unsigned, so any mode with theS_IFREGbit set (every regular file) sign-extended into a negativeInt. -
#6839 - On macOS,
os.stat()andos.lstat()now report file timestamps with the correct nanosecond values._CTimeSpec.as_nanoseconds()previously treated thetimespec.tv_nsecfield as microseconds, inflating the subsecond component by a factor of up to 1,000.
Other fixes
-
Counter.most_common(n)now returns all elements whennexceeds the number of unique elements, matching Python, instead of aborting. -
#6833 -
PythonObjectno longer leaks a CPython reference per positional argument when calling a Python object, nor when setting an item, attribute, or set literal element.
Special thanks
Special thanks to our community contributors:
Amr Hesham (@AmrDeveloper), BlueDestination (@BlueDestination), Christoph Grüninger (@gruenich), Christoph Schlumpf (@christoph-schlumpf), Danilo Salve (@odanilosalve), David Dada (@obadafidii), Giorgos Smyridis (@gsmyridis), iMostfa (@iMostfa), Jay Hemnani (@jayhemnani9910), Kavindu Sachinthe (@kavix), Mahendra Rathore (@mahendrarathore1742), Manuel Saelices (@msaelices), Nithesh (@Nithesh8678), Nitin Krishna Mucheli (@NewtonChutney), Ratul (@ratulb), Sherlock Xu (@Sherlock113), Vihaan Agarwal (@VihaanAgarwal), Vladimir Babin (@chiliec), void (@robinber)