Mojo v1.2.0
Highlights
-
Pattern matching: a mechanism for testing whether a value has a particular structure and optionally extracting pieces of that value into new bindings. A
matchstatement compares one value against a series of patterns and runs the code block for the first pattern that matches. Patterns include literals, tuples, structs, and many more. You can also use the same patterns inifandwhilestatements, and in destructuring assignments. See Language enhancements. -
Math functions on GPU: more than twenty
std.mathfunctions no longer call the system libm. CPU and GPU now run one shared Mojo implementation, soatan(),tan(),cbrt(),erfc(),lgamma(),log10(),remainder(),hypot(), the Bessel functions, and others run on accelerators for the first time. Each function documents its accuracy and subnormal behavior. See Math functions. -
Faster compilation of large packages: the
nnpackage now compiles in about 61 seconds, down from about 98 seconds. On Linux the compiler also uses jemalloc as its allocator, which makes compiling large GPU kernel files roughly 10 to 13 percent faster by cutting the system time spent in glibc's arena management. See Tooling changes. -
StringSpanis now immutable:StringSpanprovides only immutable byte access. This release removesMutStringSpan,MutStringSlice, and themutparameter onStringSpan. You need to update code that mutated string data through a span. See Strings, text, and hashing.
Documentation
- The Mojo Manual's control flow page and the
compound- and
simple-statement reference pages now
cover
match,comptime match, patterns,if case,while case, and list destructuring. - Added a Lexical structure page to the language reference, covering source encoding, lines and indentation, line continuation, comments and docstrings, semicolons, trailing commas, and delimiters.
- Rewrote the Mojo tips for Python devs guide around the Python habits that don't carry over: bracket literals, value semantics, fixed types, argument mutability, numeric representation, structs, traits, and error propagation.
- Added a reference page for the
@inlinedecorator, covering its inline levels, explicit level syntax, and use on parametric functions.
Language enhancements
-
Added
match/casepattern matching. Thematchstatement compares a value (the subject) against a set of patterns (the cases):match status_code:case 200:print("OK")case 301 | 302:print("Redirect")case 404:print("Not found")case code if code >= 500:print("Server error", code)case _:print("Unexpected status", status_code)Pattern types include:
- Literals (
case 100:) - Or-patterns (
case 100 | 200:) - Tuples (
case (10, 10, 10):) - Guards (
case limit if limit > 10:) - Wildcard (
case _:)
And many more. Where a name like
limitoccurs in the pattern, Mojo binds the name as an immutable reference to that part of the value. Dynamic match also supportsvar/refbindings.Mojo also supports
comptime matchfor testing compile-time values. Acomptime matchstatement binds names as parameter values.For an introduction to pattern matching, see the section on the match statement in the Mojo Manual. For comprehensive information on available patterns, see Match statements in the Mojo language reference.
This section shows a few illustrative examples of pattern matching:
def describe(p: Optional[Point]) -> String:match p:case .Some(Point(x=0, y=0)):return "origin"case .Some(Point(x=var x, y=0)) | .Some(Point(x=0, y=var x)):return String("axis:", x)case .Some(Point(x=x, y=y)) if x == y:return "diagonal"case .None:return "missing"case _:return "unreachable"# comptime match is guaranteed evaluated at compile time. The subject must be# a parameter and the bound names are also parameters. var and ref specifiers# are not allowed in comptime match, because they are not meaningful.def matmul_tile[m: Int, n: Int, k: Int](...):comptime match (m, n, k):case (16, 16, 16):# Hand-tuned 16³ path....case (m_tile, n_tile, k_tile) if m_tile * n_tile <= 256:# Small-tile path; bound sizes stay available as parameters....case _:# Generic fallback....# comptime match can also be used as a "type switch" since traits are# comparable to specific types with `==`.def classify_type[T: AnyType]() -> String:comptime match T:case == Int:return "integer"case == String:return "string"case _:return "other" - Literals (
-
Mojo now supports list destructuring assignments and list patterns for collections that conform to
Sizedand that are subscriptable. A single*namecaptures the elements between any leading and trailing patterns:var [first, *middle, last] = valuesmatch values:case []:print("empty")case [1, *middle, last]:print(len(middle), last)case [first, *rest]:print(first, len(rest))The subject must conform to
Sizedand support integer andContiguousSlicesubscripting for patterns that use*. A destructuring assignment with the wrong length raises an error, while a list pattern with the wrong length doesn't match. -
if caseandwhile case:if,elif, andwhilestatements now accept conditional patterns written ascase pattern = value. Mojo evaluates the value once, and the names that a successful pattern binds are available in the statement body. Anif casepattern failure continues to the nexteliforelsearm. Awhile casepattern failure exits the loop normally and runs itselseblock, if present:def describe_optional(value: Optional[Int]) -> String:if case .Some(item) = value:return String("some:", item)else:return "none"def consume():while case .Some(item) = get_next():process(item) -
You can now write the message on a
whereclause aswhere <condition> else "<message>", the preferred alternative to the existingwhere (<condition>, "<message>")spelling, which we plan to deprecate over time:def foo[sc: Int]() where sc > 1 else "scaling factor must be greater than 1":...struct Box[T: Deinitable](Marker where conforms_to(T, Marker) else "Box[T] is a Marker only when T is",):... -
A struct can now opt out of a trait by writing
not <Trait>in its conformance list. This spelling is the preferred alternative to the existing<Trait> where False, which continues to work:struct Handle(not Movable, Writable):...An opt-out can record the reason, using the same
else "<message>"spelling as awhereclause:struct Handle(not Movable else "a Handle is pinned to the port it opened"):...
Language changes
-
The
asyncandawaitkeywords are renamed to__asyncand__awaitto mark async support as unstable. Write__async defand__await expr. The unprefixed spellings still work but emit a warning that async is unstable, with a fix-it to the prefixed spelling. The prefixed spelling suppresses the warning but doesn't make async stable. -
The empty tuple
()now always refers to a tuple value and not a type. The type of an empty tuple is now always written asTuple[]. -
Since Mojo 1.1, the walrus operator can only assign to an existing variable, so code such as
if var id := map.get("test"):fails. Instead, declare the variable before the statement and assign to it with:=:# Beforeif var id := map.get("test"):print(id.value())# Aftervar id: Optional[Int]if id := map.get("test"):print(id.value())ref x := ...has no direct equivalent, since a laterx := ...assigns through the reference rather than rebinding it. In a plainif, bind it first withref x = ..., then writeif x:.
Library performance improvements
-
Stringpadding and repetition are faster.ascii_rjust(),ascii_ljust(),ascii_center(), and*now build their result in a single allocation, making padding up to 86x faster and repetition about 2x faster on large strings. -
atof()is faster on long decimal inputs, cutting a float-heavy parsing corpus from about 2.40 ms to 1.98 ms.
Library changes
Traits, enums, and reflection
-
The enum-behaving structs in the standard library now conform to
EnumLike, so they support enum-style pattern matching (matchwithcase .NAME) and reflective case metadata. These includeFastMathFlagandTestResult. Equivalent structs in the MAX kernels, runtime, and driver APIs gained the same conformance.match result:case .PASS:passed += 1case .FAIL | .SKIP:report(result) -
The default
Writableimplementation now formatsEnumLiketypes by their active case rather than their fields. A case prints asTypeName.case(payload), or asTypeName.casewhen it has no payload (NoneType):print(Shape.circle(3)) # Shape.circle(3)print(repr(Shape.circle(3))) # Shape.circle(Int(3))print(Shape.empty()) # Shape.emptyTypes that implement their own
write_to()orwrite_repr_to(), such asOptionalandBool, are unaffected. -
Defaultablenow provides a default__init__()implementation that default-constructs each field using reflection. A struct can conform toDefaultablewithout defining an initializer when all its fields also conform toDefaultable.
Collections, tuples, and spans
-
Arraynow supports slicing with contiguous slices, returning aSpanover the selected elements whose mutability matches the array reference:var arr = [1, 2, 3, 4]var middle = arr[1:3] # Span over [2, 3] -
Tuplegainedfirst_of[T](), which returns a reference to the first element of typeT. The lookup resolves at compile time, and it's a compile-time error if the tuple has no element of that type:var t = (1, String("two"), 3.0)t.first_of[String]() += "!" -
Spanhas new methods for initializing and deinitializing its elements in place. The initializing methods take a span ofMaybeUninit[T]elements and view the result as a span ofT:unsafe_deinit_elements()destroys every element in place and leaves the memory uninitialized.unsafe_init_with()initializes each element with the result of calling a function with that element's index.unsafe_init_copy_from()copies from another span.unsafe_init_move_from()moves out of another span.unsafe_assume_init()reinterprets the span as initialized, for memory that was initialized some other way.
Strings, text, and hashing
-
StringSpannow only provides immutable byte access.MutStringSpan,MutStringSlice, and themutparameter onStringSpanhave been removed. -
Hashing a byte sequence is now spelled
hash_bytes(), which takes anImmSpan[Byte]. The pointer-and-lengthhash()overload is deprecated:var digest = hash_bytes(text.as_bytes()) # was: hash(text.ptr(), text.byte_length())hash()keeps itsHashablemeaning, sohash(my_list)still hashes a list element by element whilehash_bytes(my_list)hashes its bytes. -
Other hashing changes:
std.hashlibwas renamed tostd.hash.Hasher.finish()no longer takes its argument by movable convention.Hasherno longer requires an__init__()method. You might need to update some code to requireDefaultable.
-
String.isspace()andStringSpan.isspace()are renamed tois_ascii_whitespace(), which better describes what they test. The old names still work but are deprecated. -
String.startswith()andString.endswith()now cost time proportional to the affix rather than to the whole string, which makes them orders of magnitude faster on large strings. Their edge cases now match Python: an empty affix is true at any in-range offset, a negativestartorendcounts back from the end, and anendpast the end of the string clamps.
SIMD and byte order
-
SIMD.from_bytes()andSIMD.as_bytes()now take anendianparameter of the newEndiantype (Endian.bigorEndian.little) instead of thebig_endianBool:var port = UInt16.from_bytes[endian=.big](bytes) # was: big_endian=TrueThe default is still the target's byte order, which
Endian.native()returns.is_big_endian()andis_little_endian()are deprecated; compareEndian.native()with.bigor.littleinstead. -
SIMDgainedto_endian()andfrom_endian(), which convert an integer value to and from a given byte order:var wire = UInt16(8080).to_endian[.big]() # stored as [0x1F, 0x90]var port = UInt16.from_endian[.big](wire) # 8080
Math functions
-
Many
std.mathfunctions are now implemented in Mojo rather than calling the system libm, so they run on GPU as well as CPU, and CPU and GPU share one implementation. Except where noted, CPU results can differ from the system libm in the last ulp:acosh()andasinh().atan()andatan2(). On targets that flush subnormals, subnormal inputs are handled and normal results are correct; subnormal results flush to zero.atanh()forfloat64, so itsfloat64form now runs on GPUs that havefloat64. All floating-point types share one implementation, sofloat32GPU results, previously up to 16 ulp off through a target-dependentlog, are now within 1 ulp of the CPU. Results can also differ between targets in the last ulp. On targets that flush subnormals, subnormal inputs are handled and normal results are correct; subnormal results flush to zero.- The Bessel functions
j0(),j1(),y0(), andy1(). Forfloat32, relative error grows near the zeros of each function, while absolute error stays small there. On targets that flush subnormals, subnormal inputs and subnormal results are handled; onlyj1()can produce a subnormal result infloat32, and it's kept. cbrt(), on GPU for every floating-point type, includingfloat64.float32results are within 0.505 ulp of the true root.erfc()forfloat64.float64erf()now uses afloat64approximation and is accurate to about 1 ulp. Both share one implementation withfloat32erfc().expm1()forfloat64. On targets that flush subnormals, subnormal inputs are handled; an input whose result would be subnormal is returned bit-exactly, so no subnormal result is ever flushed.gamma(). Subnormal inputs and results are also handled on targets that flush subnormals, such as Apple GPUs infloat32.hypot().lgamma(). It stays within about 2 ulp for negative inputs, including next to the negative real zeros oflgamma(such as -2.457 and -2.748), where the result is near 0.log10(), which no longer calls GPU intrinsics either: CPU and every GPU now run the same implementation, accurate to within 1 ulp on measured targets, andfloat64log10()now runs on AMD GPUs. Results can also differ between targets in the last ulp where the compiler contracts a multiply-add into an fma. On targets that flush subnormals, subnormal inputs are handled and normal results are correct; subnormal results flush to zero.logb(), which is exact: it reads the exponent from the bit pattern, so subnormal inputs return their true exponent even on GPUs that flush subnormals.nextafter(), instd.utils.numerics, which is exact and matches the system libm for non-NaN inputs because it steps the IEEE-754 bit pattern directly. Since every ordering, zero, and equality predicate reads the raw bits, it's unaffected by targets that flush subnormal arithmetic.std.math.ulp(), which is defined throughnextafter(), follows it onto the GPU.remainder(), which is exact, so it matches the system libm bit for bit. On targets that flush subnormals, subnormal inputs and subnormal results are handled; the results match the system libm bit for bit.scalb(), whose results are bit-exact against IEEE targets. Apple GPUs don't supportfloat64. On targets that flush subnormals, subnormal inputs and subnormal results are handled.tan(). CPU and GPU results can also differ from each other in the last ulp. On targets that flush subnormals, subnormal inputs are handled and normal results are correct; subnormal results flush to zero.
-
float64log()now uses double-precision coefficients and is within 1 ulp of the correctly rounded result; it was previously off by up to about 2e7 ulp. It runs the same Mojo code on CPU and GPU. -
log1p()now evaluates in the input's own precision with a Mojo implementation based on musl, shared by CPU and GPU, and is accurate to within 1 ulp forfloat32andfloat64.float16andbfloat16inputs are computed infloat32.float64log1p()is no longer supported on Apple GPUs, which have nofloat64arithmetic. -
std.mathgainedmulwide()andmulhi(), which compute the full-width product and the high half of a 32- or 64-bit integer multiply. They are no longer GPU-only.
Compilation targets and accelerators
-
The public
CompilationTargettype (introduced in Mojo 1.1) is now used in most APIs that are parameterized on compilation target, such assize_of(),align_of(), andcompile_info().Additionally,
CompilationTargetnow provides predicate methods for checking the vendor identity of a given target. The preexisting free functions with the same name now call these new methods. These include:.is_nvidia_gpu()for checking whether a compilation target is an NVIDIA GPU..is_amd_gpu()and.is_apple_gpu()provide the equivalent checks for AMD and Apple GPUs, respectively.
-
std.sysgained acurrent_accelerator()free function, which returns the accelerator target the current code is being compiled for and fails at compile time when that target isn't an accelerator. From host code, usedefault_accelerator(), which returns the default accelerator target, either determined automatically or as specified by--target-accelerator. Both functions return aCompilationTarget. In nightly builds after this release,default_accelerator()moves tomax.gpu.host.info. -
Added a new
TargetAcceleratortype for representing accelerator metadata at compile time, combiningGPUInfoandCompilationTargetvalues.Target constants such as
B200andMI355Xare nowTargetAcceleratorinstances rather thanGPUInfoinstances.Use
.gpu_infoto access the prior device metadata, or.targetfor the compilation target.
Unified closures
-
Tuple.consume_elements(),Tuple.deinit_with(), andVariadicPack.consume_elements()now take the element handler as a runtime closure argument instead of a compile-timecapturingparameter, matchingList.deinit_with()andVariadicList.consume_elements():def handler[idx: Int](var elt: t.Ts[idx]) {mut collected}:collected[idx] = elt.datat^.consume_elements(handler) # was: t^.consume_elements[handler]() -
SIMD.reduce()now takes a closure reduction function as a runtime argument instead of a compile-timecapturingparameter. Thethinfunction-pointer form is unchanged:def add[width: SIMDLength](lhs: SIMD[DType.int32, width], rhs: SIMD[DType.int32, width]) -> SIMD[DType.int32, width]:return lhs + rhsv.reduce(add) # was: v.reduce[add]()v.reduce[2](add) # was: v.reduce[add, 2]()
Other library changes
-
FileDescriptoris nowRawFileDescriptor, and it takes its descriptor as ac_int, matching the Cinttype that the underlying descriptor APIs take. Integer literals keep constructing one unchanged, but passing anIntvariable now requires an explicitc_int()conversion. The underlying descriptor value is no longer a public field. The descriptor value-1is the type's niche, soOptional[RawFileDescriptor]is the same size asRawFileDescriptor, and constructing aRawFileDescriptorfrom a negative value aborts; useOptional[RawFileDescriptor]to represent an absent descriptor. -
Layoutnow carries its alignment as a keyword-only parameter of the newAlignmenttype, instead of storing it as a runtime field. It defaults to the element type's natural alignment, soLayout[T](count=n)is unchanged. Build anAlignmentwithAlignment.of[T]()for a type's natural alignment orAlignment.of_bytes[n]()for an explicit byte count.AllocationandManagedAllocationcarry the same parameter.ThinAllocationdeliberately doesn't: it records only the element type, so the alignment travels with theLayoutyou supply tounsafe_with_layout().var layout = Layout[Int32, alignment = .of_bytes[64]()](count=8)var thin = alloc(layout).into_thin()dealloc(thin^.unsafe_with_layout(layout))For now, Mojo supports only compile-time alignments. This keeps the common case (natural type alignment) simple, so the
LayoutandAllocationtypes don't pay the cost of holding an extraIntfield. Dynamic (runtime) alignment will come after more design work. -
Coord.product()has a new parameterized overload,product[T: DType](), which accumulates and returns the product atTrather than atCoord.DTYPE:var c = Coord(Idx[4], Int(8), Int32(3))var n = c.product[DType.uint32]() # UInt32(96)The unparameterized
product()is unchanged and still returnsScalar[Coord.DTYPE]. ATtoo narrow to hold the result wraps rather than widening, so a caller that picks one owns the overflow. -
Coordhas a newreplace_dynamic[at](value)method, the in-place counterpart toreplace(). It keeps the coordinate's type, so the element atatmust already be dynamic, andvaluetakes that element's own dtype:var c = Coord(ComptimeInt[3](), Int64(0))c.replace_dynamic[1](Int64(7)) # Coord(ComptimeInt[3](), Int64(7)) -
CoordLikenow derives fromIntableand supplies a default__int__(), so any coordinate-like value, includingComptimeInt, converts withInt(c). -
The uninitialized-read check now accepts
MOJO_STDLIB_SIMD_UNINIT_CHECK=report, which prints the same diagnostic but keeps running, so a single run enumerates every offending site instead of aborting at the first one. Apple GPUs, which cannot print the diagnostic, still trap. The defaultabortbehavior is unchanged.
Tooling changes
- The
mojocompiler now uses jemalloc as its allocator on Linux. Compiling large GPU kernel files is roughly 10 to 13 percent faster because the compiler spends far less system time in glibc's arena management. - The Mojo language server now reports a value's address space as a semantic
token modifier (
addressSpace0throughaddressSpace15, matching theAddressSpaceindex) so editors can color shared, local, and constant memory differently. The address space is found in the value's type or in a type nested in its parameters, so anArrayof shared pointers is marked too. - Compiling large Mojo packages is significantly faster. The
nnpackage now compiles in about 61 seconds, down from about 98 seconds. mojo build --emitcan now write one output file per offload kernel, alongside whatever artifact the kernel already embeds.--emitvalidates the requested kind against the selected target and renders its help from the target's own declared kinds. Metal targets gained anairkind, which emits one optimized Apple AIR bitcode file per kernel.
Removed
-
Removed
has_nvidia_gpu_accelerator(),has_amd_gpu_accelerator(), andhas_apple_gpu_accelerator()fromstd.sys, including the sub-architecture overloads of the NVIDIA variant. Ask the target itself instead, using the newCompilationTargetpredicate methods:# Beforefrom std.sys import has_nvidia_gpu_acceleratorcomptime if has_nvidia_gpu_accelerator():...# Afterfrom std.sys import default_acceleratorcomptime if default_accelerator().is_nvidia_gpu():...Code that already holds a device context can check its target directly, for example
ctx.target.is_nvidia_gpu(). -
Removed
sum()from theCoordLiketrait and from its implementations (Coord,ComptimeInt, and theAllmarker). Nothing called it: a coordinate's elements are extents and indices, so adding them together carries no meaning, unlikeproduct(), whose result is the number of elements a shape describes. Useproduct()for that, or iterate the elements and add them yourself if you really want a sum. -
Removed
Coord.inner_product().Coorddescribes extents and indices, so treating two coordinates as Euclidean vectors had no meaning; useproduct(), or compute the sum of products yourself.
Fixed
Language and type checking
-
Struct layouts that recurse only through a parametric indirection now compile. A field such as
Optional[List[Pointer[Node, origin]]]orTuple[Pointer[Node, origin], Int]insidestruct Nodewas spuriously rejected withstruct has recursive reference to itself, despite the recursion being behind a pointer. -
Passing
x^to animmorrefargument now warns that the move has no effect, instead of silently ignoring it. -
#7153 - t-string interpolations are now captured by reference, so mutating an interpolated
Intbefore the t-string renders no longer prints a stale value. -
#7204 - Contextual member expressions now accept runtime arguments. A call such as
paint(.gray(level)), wherelevelis a runtime value, now resolves against the parameter type. -
An extension of a struct that hasn't been resolved yet no longer fails to check its members or reports a spurious cycle.
-
Struct extensions reached through a wildcard import of a precompiled package are no longer silently dropped.
-
A method reached through an extension that is visible from more than one enclosing scope no longer reports a spurious
ambiguous callerror. -
A function whose argument is typed with a constrained parametric comptime alias can now use that constraint in its body.
-
Slice-literal subscripts now work on types that define both
__getitem_param__()and__getitem__(). A slice literal with no contextual type falls back toContiguousSlice, or toSlicewhen it has a stride. -
A
tryblock that infers its error type now rejects an error naming a field of a local declared in the try body, instead of failing later with an IR verification error. -
origin_of()and origin specifiers now report an error when given a temporary, such asref [String("lit")] z: Int, instead of failing later with an internal compiler error. -
#6718 - A trailing
finallythat follows a nestedtry/exceptnow parses correctly instead of being absorbed into the inner block.
Compiler crashes and hangs
-
#7164 - Fixed a compiler crash at
-O2and-O3on code whose values flowed only into pure operations with an unused final result. -
#7127 - Fixed the compiler hanging in lifetime checking on a nested
forloop that assigns to a variable declared in the outer loop body. -
Move-capturing a trivially register-passable value in a closure capture list no longer crashes the compiler.
-
Calling the initializer of a parametric struct that raises a convertible error type no longer crashes the compiler.
-
Reflecting on a function whose mangled name contains escaped quotes no longer crashes the compiler.
-
Compile-time code that builds a cyclic data structure, such as a doubly linked list, no longer overflows the compiler's stack.
-
Misusing a kernel entry function now reports a compiler error instead of an internal assertion failure.
Code generation and optimization
-
Atomic updates to a value passed in memory are no longer dropped by the optimizer.
-
#7155 - A value used on a path that leads to a no-return call, such as
abort(), is no longer destroyed before the branches rejoin. -
#7154 - Integer constants of different widths no longer collide during constant folding, a bug that made
Int(UInt32.MAX)fold to-1instead of4294967295.
Math and numerics
-
atof()now rounds large mantissas correctly and accepts numbers with more than 19 significant digits:- Values whose significant digits exceed 53 bits, such as
atof("123456789012345678"), could round to the wrong neighboring double, and exact ties such asatof("4503599627370497.5")did not round to even. - Inputs with more than 19 significant digits previously raised
The number is too long. Malformed inputs such as"1..2","1e5.0", or"1e--5", which the old parser silently misread by skipping the offending characters, now raise.
- Values whose significant digits exceed 53 bits, such as
-
On macOS,
scalb()now returns NaN for a non-integral exponent, as it does on Linux. -
float32expm1()no longer overflows early for inputs just below 88.7, and it now returns-0.0for-0.0andinfforinf. -
float32erfc()is now accurate in its tail, for large positive inputs, and returns subnormal results instead of flushing them to zero. On targets that flush subnormals, subnormal inputs are handled and normal results are correct; subnormal results flush to zero. -
cbrt()ofinfand-infnow returnsinfand-infinstead ofFloat32.MAXand-Float32.MAX, andfloat32cbrt()is correct nearFloat32.MAXand for subnormal inputs. On targets that flush subnormals, subnormal inputs are handled and normal results are correct; subnormal results flush to zero, thoughcbrt()never produces one. -
#7302 -
log(),log2(), andlog1p()ofinfnow returninfinstead ofnan. -
#7101 -
float64log1p()is now accurate for inputs whose1 + xfalls in[sqrt(2)/2, sqrt(2)), where it was off by up to about 7e9 ulp. -
sqrt(),rsqrt(), andexp2()now compile when evaluated at compile time inside a Metal kernel.float16sin()andcos()now use the native half-precision Metal intrinsics. -
std.math.frexp()now returns the correct fraction and exponent for subnormal inputs, and handles values at the top of the exponent range.
SIMD and GPU
-
SIMD.insert()now compiles when the inserted value is aScalar, and on Apple GPUs. -
OptionalandOptionalRegnow convert aDevicePassablepayload to its device representation before it reaches a GPU kernel. -
A closure that copy-captures a type with a conditional
RegisterPassableconformance, such asTuple[Int32, Int32], is nowRegisterPassablewhen the capture satisfies the condition, andDevicePassablewhen every capture is alsoDevicePassable.
Other fixes
-
#7261 -
c_charnow reflects the target's actualcharsignedness: unsigned by default on Linux running on ARM (AAPCS64), signed everywhere else Mojo currently targets (x86-64 Linux, macOS on Apple silicon). Previouslyc_charwas always signed, which could bind an FFI declaration to the wrong C type on Linux/ARM. -
String.split()andStringSpan.split()no longer put the trailing empty slice out of bounds when splitting on an empty separator. -
Mojo tools no longer send the headers in
OTEL_EXPORTER_OTLP_HEADERS, or its signal-specific forms, to Modular's telemetry collector.
Special thanks
Special thanks to our community contributors:
Angel Marinov (@0xamlab), Arseni Ivanov (@arseniivanov), David Dada (@obadafidii), Giorgos Smyridis (@gsmyridis), Jay Hemnani (@jayhemnani9910), Krisztián Szűcs (@kszucs), Londo (@Londopy), Mahendra Rathore (@mahendrarathore1742), Manas (@weirdsmiley), Marius S (@winding-lines), Narayan (@vortex73), Oleksandr Tarasiuk (@a-tarasyuk), Wei-Cheng Hsu (@weicheng-hsu)