v0.7.0 (2024-01-25)
⭐️ New
-
A new Mojo-native dictionary type,
Dictfor storing key-value pairs.Dictstores values that conform to theCollectionElementtrait. Keys need to conform to the newKeyElementtrait, which is not yet implemented by other standard library types. In the short term, you can create your own wrapper types to use as keys. For example, the following sample defines aStringKeytype and uses it to create a dictionary that maps strings toIntvalues:from collections.dict import Dict, KeyElement
@value
struct StringKey(KeyElement):
var s: String
fn __init__(inout self, owned s: String):
self.s = s ^
fn __init__(inout self, s: StringLiteral):
self.s = String(s)
fn __hash__(self) -> Int:
return hash(self.s)
fn __eq__(self, other: Self) -> Bool:
return self.s == other.s
def main():
var d = Dict[StringKey, Int]()
d["cats"] = 1
d["dogs"] = 2
print(len(d)) # prints 2
print(d["cats"]) # prints 1
print(d.pop("dogs")) # prints 2
print(len(d)) # prints 1We plan to add
KeyElementconformance to standard library types in subsequent releases. -
Users can opt-in to assertions used in the standard library code by specifying
-D MOJO_ENABLE_ASSERTIONSwhen invokingmojoto compile your source file(s). In the case that an assertion is fired, the assertion message will be printed along with the stack trace before the program exits. By default, assertions are not enabled in the standard library right now for performance reasons. -
The Mojo Language Server now implements the References request. IDEs use this to provide support for Go to References and Find All References. A current limitation is that references outside of the current document are not supported, which will be addressed in the future.
-
The
sys.infomodule now includesnum_physical_cores(),num_logical_cores(), andnum_performance_cores()functions. -
Homogeneous variadic arguments consisting of memory-only types, such as
Stringare more powerful and easier to use. These arguments are projected into aVariadicListMem.(Previous releases made it easier to use variadic lists of register-passable types, like
Int.)Subscripting into a
VariadicListMemnow returns the element instead of an obscure internal type. In addition, we now supportinoutandownedvariadic arguments:fn make_worldly(inout *strs: String):
# This "just works" as you'd expect!
for i in range(len(strs)):
strs[i] += " world"
fn main():
var s1: String = "hello"
var s2: String = "konnichiwa"
var s3: String = "bonjour"
make_worldly(s1, s2, s3)
print(s1) # hello world
print(s2) # konnichiwa world
print(s3) # bonjour world(Previous releases made it easier to use variadic lists, but subscripting into a
VariadicListMemreturned a low-level pointer, which required the user to call__get_address_as_lvalue()to access the element.)Note that subscripting the variadic list works nicely as above, but iterating over the variadic list directly with a
forloop produces aReference(described below) instead of the desired value, so an extra subscript is required; We intend to fix this in the future.fn make_worldly(inout *strs: String):
# Requires extra [] to dereference the reference for now.
for i in strs:
i[] += " world"Heterogeneous variadic arguments have not yet been moved to the new model, but will in future updates.
Note that for variadic arguments of register-passable types like
Int, the variadic list contains values, not references, so the dereference operator ([]) is not required. This code continues to work as it did previously:fn print_ints(*nums: Int):
for num in nums:
print(num)
print(len(nums)) -
Mojo now has a prototype version of a safe
Referencetype. The compiler's lifetime tracking pass can reason about references to safely extend local variable lifetime, and check indirect access safety. TheReferencetype is brand new (and currently has no syntactic sugar) so it must be explicitly dereferenced with an empty subscript:ref[]provides access to the underlying value.fn main():
var a: String = "hello"
var b: String = " references"
var aref = Reference(a)
aref[] += b
print(a) # prints "hello references"
aref[] += b
# ^last use of b, it is destroyed here.
print(aref[]) # prints "hello references references"
# ^last use of a, it is destroyed here.While the
Referencetype has the same in-memory representation as a C pointer or the MojoPointertype, it also tracks a symbolic "lifetime" value so the compiler can reason about the potentially accessed set of values. This lifetime is part of the static type of the reference, so it propagates through generic algorithms and abstractions built around it.The
Referencetype can form references to both mutable and immutable memory objects, e.g. those on the stack or borrowed/inout/owned function arguments. It is fully parametric over mutability, eliminating the problems with code duplication due to mutability specifiers and provides the base for unified user-level types. For example, it could be used to implement an array slice object that handles both mutable and immutable array slices.While this is a major step forward for the lifetimes system in Mojo, it is still very early and awkward to use. Notably, there is no syntactic sugar for using references, such as automatic dereferencing. Several aspects of it need to be more baked. It is getting exercised by variadic memory arguments, which is why they are starting to behave better now.
Note: the safe
Referencetype and the unsafe pointer types are defined in the same module, currently namedmemory.unsafe. We expect to restructure this module in a future release. -
Mojo now allows types to implement
__refattr__()and__refitem__()to enable attribute and subscript syntax with computed accessors that return references. For common situations where these address a value in memory this provides a more convenient and significantly more performant alternative to implementing the traditional get/set pairs. Note: this may be changed in the future when references auto-dereference—at that point we may switch to just returning a reference from__getattr__(). -
Parametric closures can now capture register passable typed values by copy using the
__copy_capturedecorator. For example, the following code will print5, not2.fn foo(x: Int):
var z = x
@__copy_capture(z)
@parameter
fn formatter() -> Int:
return z
z = 2
print(formatter())
fn main():
foo(5) -
String now implements KeyElement and may be used as a key in Dict.
-
More robust support for structs with fields of self referencing types. For example, the following code will work and print
0:struct Foo(CollectionElement):
var vec: DynamicVector[Self]
fn __init__(inout self: Self):
self.vec = DynamicVector[Self]()
fn __moveinit__(inout self: Self, owned existing: Self):
self.vec = existing.vec ^
fn __copyinit__(inout self: Self, existing: Self):
self.vec = existing.vec
fn main():
var foo = Foo()
print(len(foo.vec))
❌ Removed
-
The
__takeinit__special constructor form has been removed from the language. This "non-destructive move" operation was previously wired into thex^transfer operator, but had unpredictable behavior that wasn't consistent. Now that Mojo has traits, it is better to model this as an explicit.take()operation on a type, which would transfer out the contents of the type without ending its lifetime. For example, for a type that holds a pointer,take()might return a new instance pointing to the same data, and null out its own internal pointer.This change makes it clear when a lifetime is ended versus when the contents of an LValue are explicitly taken.
-
The current implementation of autotuning has been deprecated, as Mojo's autotuning implementation is undergoing a redesign. Tutorials around the current implementation have also been removed as they are being rewritten.
Consequently, the
autotune(),autotune_fork(), andsearch()functions have been removed from the standard library. -
The
_OldDynamicVectortype that worked only on register passable element types has been removed. Please migrate uses toDynamicVectorwhich works on both register passable and memory types. -
The
UnsafeFixedVectorinutils.vectorhas been removed. We recommend using eitherDynamicVectororInlinedFixedVectorinstead. -
The
@adaptivedecorator has been removed from the language. Any uses of the decorator in a non-search context can be replaced with@parameter if. For example:@adaptive
fn foo[a: Bool]():
constrained[a]()
body1()
@adaptive
fn foo[a: Bool]():
constrained[not a]()
body2()Can be rewritten as:
fn foo[a: Bool]():
@parameter
if a:
body1()
else:
body2()Consequently, the special
__adaptive_setattribute has been removed as well. -
Result parameters have been removed from Mojo. Result parameter declarations in function parameter lists are no longer allowed, nor are forward alias declarations. This includes removing the
param_returnstatement. -
The
@noncapturingand@closuredecorators have been removed due to refinements and improvements to the closure model. See below for more details!
🦋 Changed
-
The Mojo closure model has been refined to be more straightforward and safe. Mojo has two closure types: parameter closures and runtime closures. Parameter closures can be used in higher-order functions and are the backbone of functions like
vectorizeandparallelize. They are always denoted by@parameterand have typefn() capturing -> T(whereTis the return type).On the other hand, runtime closures are always dynamic values, capture values by invoking their copy constructor, and retain ownership of their capture state. You can define a runtime closure by writing a nested function that captures values:
fn outer(b: Bool, x: String) -> fn() escaping -> None:
fn closure():
print(x) # 'x' is captured by calling String.__copyinit__
fn bare_function():
print("hello") # nothing is captured
if b:
# closure can be safely returned because it owns its state
return closure^
# function pointers can be converted to runtime closures
return bare_functionThe type of runtime closures are of the form
fn() escaping -> T. You can pass equivalent function pointers as runtime closures.Stay tuned for capture list syntax for move capture and capture by reference, and a more unified closure model!
-
The
@unroll(n)decorator can now take a parameter expression for the unroll factor, i.e.ncan be a parameter expression that is of integer type. -
The
cpythonmodule in thepythonpackage has been moved to be an internal module, i.e,_cpython. -
AnyTypeandDestructablehave been unified into a single trait,AnyType. Every nominal type (i.e. all structs) now automatically conform toAnyType. -
Previously, the
mojo packagecommand would output a Mojo package that included both partly-compiled Mojo code, as well as fully-compiled machine code for a specific computer architecture -- the architecture of the machine being used to invoke themojo packagecommand.Now,
mojo packageonly includes partly-compiled Mojo code. It is only fully compiled for the specific computer architecture being used at the point that the package is firstimport-ed. As a result, Mojo packages are smaller and more portable. -
The
simd_widthanddtypeparameters ofpolynomial_evaluatehave been switched. Based on the request in #1587, thepolynomial_evaluatefunction has also been extended so that thecoefficientsparameter can take either a either aStaticTupleor aVariadicList. -
As a tiny step towards removing
letdeclarations, this release removes the warning:'var' was never mutated, consider switching to a 'let'.
🛠️ Fixed
- #1595 - Improve error
message when trying to materialize
IntLiteralin runtime code. - Raising an error from the initializer of a memory-only type now works
correctly in the presence of complex control flow. Previously Mojo could run
the destructor on
selfbefore it was initialized when exiting with an error. - #1096 - Improve warning
messages for dead code in conditionals like
orexpressions. - #1419 - Fix assertion failure with uninitialized lattice values.
- #1402 - Fix movable trait
not detected on recursive struct implemented with
AnyPointer. - #1399 - Fix parser crash when a parameter type in a struct that implements a trait is misspelled.
- #1152 - Allow mutable
selfargument when overloading operators using dunder methods. - #1493 - Fix crash in
DynamicVectorcopy constructor in certain situations. - #1316 - The
benchmark.keepfunction now properly handles vector types. - #1505 - The
simd.shuffleoperation now works on 64 element permutations. - #1355 - Fix
String.find()returning wrong value when starting index is non-zero. - #1367 - Fix
String.replace()returning incorrect results for multi-character search strings. - #1535 - Invalid error
field 'w.x.y' destroyed out of the middle of a value, preventing the overall value from being destroyed. - #1475 - Assertion failure in nested loop.
- #1591 - Assertion failure
when using
AnyTypestruct member. - #1503 - Rename the mojo
build of LLDB to
mojo-lldb, to prevent name collisions with the system's LLDB. - #1542 -
@unrolldoes not accept alias as unroll factor. - #1443 - Compiler crash on variadic list of traits.
- #1604 - Variable of trivial type not destroyed by transferring ownership.
- #1341 - Segmentation fault when passing closures around.
- #217 - Closure state is stack allocated.