This is a running log of Mojo changes for users. It doesn’t include all the internal implementation changes.
Each update to this changelog is based on the date we released a new build to the Mojo Playground.
July 2023
2023-07-12
⭐️ New
-
Mojo now has support for global variables! This enables
var
andlet
declaration at the top-level scope in Mojo files. Global variable initializers are run when code modules are loaded by the platform according to the order of dependencies between global variables, and their destructors are called in the reverse order. -
The Mojo programming manual is now written as a Jupyter notebook, and available in its entirety in the Mojo Playground (
programming-manual.ipynb
). (Previously,HelloMojo.ipynb
included most of the same material, but it was not up-to-date.) -
As a result, we’ve also re-written
HelloMojo.ipynb
to be much shorter and provide a more gentle first-user experience. -
Coroutine
module documentation is now available. Coroutines form the basis of Mojo’s support for asynchronous execution. Calls toasync fn
s can be stored into aCoroutine
, from which they can be resumed, awaited upon, and have their results retrieved upon completion.
🦋 Changed
simd_bit_width
in theTargetInfo
module has been renamed tosimdbitwidth
to better align withsimdwidthof
,bitwidthof
, etc.
🛠️ Fixed
-
The walrus operator now works in if/while statements without parentheses, e.g.
if x := function():
. -
Issue #428 – The
FloatLiteral
andSIMD
types now support conversion toInt
via theto_int
or__int__
method calls. The behavior matches that of Python, which rounds towards zero.
2023-07-05
⭐️ New
- Tuple expressions now work without parentheses. For example,
a, b = b, a
works as you’d expect in Python. - Chained assignments (e.g.
a = b = 42
) and the walrus operator (e.g.some_function(b := 17)
) are now supported.
🦋 Changed
-
The
simd_width
anddtype_simd_width
functions in theTargetInfo
module have been renamed tosimdwidthof
. -
The
dtype_
prefix has been dropped fromalignof
,sizeof
, andbitwidthof
. You can now use these functions (e.g.alignof
) with any argument type, includingDType
. -
The
inf
,neginf
,nan
,isinf
,isfinite
, andisnan
functions were moved from theNumerics
module to theMath
module, to better align with Python’s library structure.
🛠️ Fixed
-
Issue #253 – Issue when accessing a struct member alias without providing parameters.
-
Issue #404 – The docs now use
snake_case
for variable names, which more closely conforms to Python’s style. -
Issue #379 – Tuple limitations have been addressed and multiple return values are now supported, even without parentheses.
-
Issue #347 – Tuples no longer require parentheses.
-
Issue #320 – Python objects are now traversable via
for
loops.
June 2023
2023-06-29
⭐️ New
- You can now share
.ipynb
notebook files in Mojo Playground. Just save a file in theshared
directory, and then right-click the file and select Copy Sharable link. To open a shared notebook, you must already have access to Mojo Playground; when you open a shared notebook, click Import at the top of the notebook to save your own copy. For more details about this feature, see the instructions inside thehelp
directory, in the Mojo Playground file browser.
🦋 Changed
- The
unroll2()
andunroll3()
functions in theFunctional
module have been renamed to overload theunroll()
function. These functions unroll 2D and 3D loops andunroll()
can determine the intent based on the number of input parameters.
🛠️ Fixed
-
Issue #229 – Issue when throwing an exception from
__init__
before all fields are initialized. -
Issue #74 – Struct definition with recursive reference crashes.
-
Issue #285 – The
TargetInfo
module now includesis_little_endian()
andis_big_endian()
to check if the target host uses either little or big endian. -
Issue #254 – Parameter name shadowing in nested scopes is now handled correctly.
2023-06-21
⭐️ New
-
Added support for overloading on parameter signature. For example, it is now possible to write the following:
fn foo[a: Int](x: Int): pass fn foo[a: Int, b: Int](x: Int): pass
For details on the overload resolution logic, see the programming manual.
-
A new
cost_of()
function has been added toAutotune
. This meta-function must be invoked at compile time, and it returns the number of MLIR operations in a function (at a certain stage in compilation), which can be used to build basic heuristics in higher-order generators.from Autotune import cost_of fn generator[f: fn(Int) -> Int]() -> Int: @parameter if cost_of[fn(Int) -> Int, f]() < 10: return f() else: # Do something else for slower functions...
-
Added a new example notebook with a basic Ray Tracing algorithm.
🦋 Changed
- The
assert_param_msg()
in theAssert
module has been renamed toassert_param()
.
🛠️ Fixed
-
Overloads marked with
@adaptive
now correctly handle signatures that differ only in declared parameter names, e.g. the following now works correctly:@adaptive fn foobar[w: Int, T: DType]() -> SIMD[T, w]: ... @adaptive fn foobar[w: Int, S: DType]() -> SIMD[S, w]: ...
-
Issue #219 – Issue when redefining a function and a struct defined in the same cell.
-
Issue #355 – The loop order in the Matmul notebook for Python and naive mojo have been reordered for consistency. The loop order now follows (M, K, N) ordering.
-
Issue #309 – Use snake case naming within the testing package and move the asserts out of the TestSuite struct.
2023-06-14
⭐️ New
-
Tuple type syntax is now supported, e.g. the following works:
fn return_tuple() -> (Int, Int): return (1, 2)
🦋 Changed
- The
TupleLiteral
type was renamed to justTuple
, e.g.Tuple[Int, Float]
.
🛠️ Fixed
- Issue #354 – Returning a tuple doesn’t work even with parens.
- Issue #365 – Copy-paste error in
FloatLiteral
docs. - Issue #357 – Crash when missing input parameter to variadic parameter struct member function.
2023-06-07
⭐️ New
- Tuple syntax now works on the left-hand side of assignments (in “lvalue” positions), enabling things like
(a, b) = (b, a)
. There are several caveats: the element types must exactly match (no implicit conversions), this only works with values ofTupleLiteral
type (notably, it will not work withPythonObject
yet) and parentheses are required for tuple syntax.
❌ Removed
- Mojo Playground no longer includes the following Python packages (due to size, compute costs, and environment complications):
torch
,tensorflow
,keras
,transformers
.
🦋 Changed
- The data types and scalar names now conform to the naming convention used by numpy. So we use
Int32
instead ofSI32
, similarly usingFloat32
instead ofF32
. Closes Issue #152.
🛠️ Fixed
- Issue #287 – computed lvalues don’t handle raising functions correctly
- Issue #318 – Large integers are not being printed correctly
- Issue #326 – Float modulo operator is not working as expected
- Issue #282 – Default arguments are not working as expected
- Issue #271 – Confusing error message when converting between function types with different result semantics
May 2023
2023-05-31
⭐️ New
-
Mojo Playground now includes the following Python packages (in response to popular demand):
torch
,tensorflow
,polars
,opencv-python
,keras
,Pillow
,plotly
,seaborn
,sympy
,transformers
. -
A new optimization is applied to non-trivial copyable values that are passed as an owned value without using the transfer (
^
) operator. Consider code like this:var someValue : T = ... ... takeValueAsOwned(someValue) ...
When
takeValueAsOwned()
takes its argument as anowned
value (this is common in initializers for example), it is allowed to do whatever it wants with the value and destroy it when it is finished. In order to support this, the Mojo compiler is forced to make a temporary copy of thesomeValue
value, and pass that value instead ofsomeValue
, because there may be other uses ofsomeValue
after the call.The Mojo compiler is now smart enough to detect when there are no uses of
someValue
later, and it will elide the copy just as if you had manually specified the transfer operator liketakeValueAsOwned(someValue^)
. This provides a nice “it just works” behavior for non-trivial types without requiring manual management of transfers.If you’d like to take full control and expose full ownership for your type, just don’t make it copyable. Move-only types require the explicit transfer operator so you can see in your code where all ownership transfer happen.
-
Similarly, the Mojo compiler now transforms calls to
__copyinit__
methods into calls to__moveinit__
when that is the last use of the source value along a control flow path. This allows types which are both copyable and movable to get transparent move optimization. For example, the following code is compiled into moves instead of copies even without the use of the transfer operator:var someValue = somethingCopyableAndMovable() use(someValue) ...let otherValue = someValue # Last use of someValue use(otherValue) ...var yetAnother = otherValue # Last use of otherValue mutate(yetAnother)
This is a significant performance optimization for things like
PythonObject
(and more complex value semantic types) that are commonly used in a fluid programming style. These don’t want extraneous reference counting operations performed by its copy constructor.If you want explicit control over copying, it is recommended to use a non-dunder
.copy()
method instead of__copyinit__
, and recall that non-copyable types must always use of the transfer operator for those that want fully explicit behavior.
🛠️ Fixed
- Issue #231 – Unexpected error when a Python expression raises an exception
- Issue #119 – The REPL fails when a python variable is redefined
2023-05-24
⭐️ New
finally
clauses are now supported ontry
statements. In addition,try
statements no longer requireexcept
clauses, allowingtry-finally
blocks.finally
clauses contain code that is always executed from control-flow leaves any of the other clauses of atry
statement by any means.
🦋 Changed
-
with
statement emission changed to use the newfinally
logic so thatwith ContextMgr(): return
Will correctly execute
ContextMgr.__exit__
before returning.
🛠️ Fixed
- Issue #204 – Mojo REPL crash when returning a String at compile-time
- Issue #143 – synthesized init in
@register_passable
type doesn’t get correct convention. - Issue #201 – String literal concatenation is too eager.
- Issue #209 – [QoI] Terrible error message trying to convert a type to itself.
- Issue #32 – Include struct fields in docgen
- Issue #50 – Int to string conversion crashes due to buffer overflow
- Issue #132 – PythonObject
to_int
method has a misleading name - Issue #189 – PythonObject bool conversion is incorrect
- Issue #65 – Add SIMD constructor from Bool
- Issue #153 – Meaning of
Time.now
function result is unclear - Issue #165 – Type in
Pointer.free
documentation - Issue #210 – Parameter results cannot be declared outside top-level in function
- Issue #214 – Pointer offset calculations at compile-time are incorrect
- Issue #115 – Float printing does not include the right number of digits
- Issue #202 –
kgen.unreachable
inside nested functions is illegal - Issue #235 – Crash when register passable struct field is not register passable
- Issue #237 – Parameter closure sharp edges are not documented
2023-05-16
⭐️ New
-
Added missing dunder methods to
PythonObject
, enabling the use of common arithmetic and logical operators on imported Python values. -
PythonObject
is now printable from Mojo, instead of requiring you to import Python’s print function.
🛠️ Fixed
2023-05-11
⭐️ New
-
NDBuffer
andBuffer
are now constructable viaPointer
andDTypePointer
. -
String
now supports indexing with either integers or slices. -
Added factorial function to the
Math
module.
🦋 Changed
-
The “byref” syntax with the
&
sigil has changed to use aninout
keyword to be more similar to theborrowed
andowned
syntax in arguments. Please see Issue #7 for more information. -
Optimized the Matrix multiplication implementation in the notebook. Initially we were optimizing for expandability rather than performance. We have found a way to get the best of both worlds and now the performance of the optimized Matmul implementation is 3x faster.
-
Renamed the
^
postfix operator from “consume” to “transfer.”
🛠️ Fixed
2023-05-02
📢 Released
- Mojo publicly launched! This was epic, with lots of great coverage online including a wonderful post by Jeremy Howard. The team is busy this week.
⭐️ New
- Added a Base64 encoding function to perform base64 encoding on strings.
🦋 Changed
-
Decreased memory usage of serialization of integers to strings.
-
Speedup the sort function.
🛠️ Fixed
- Fixed time unit in the
sleep
function.
April 2023
Week of 2023-04-24
-
📢 The default behavior of nested functions has been changed. Mojo nested functions that capture are by default are non-parametric, runtime closures, meaning that:
def foo(x): # This: def bar(y): return x*y # Is the same as: let bar = lambda y: x*y
These closures cannot have input or result parameters, because they are always materialized as runtime values. Values captured in the closure (
x
in the above example), are captured by copy: values with copy constructors cannot be copied and captures are immutable in the closure.Nested functions that don’t capture anything are by default “parametric” closures: they can have parameters and they can be used as parameter values. To restore the previous behavior for capturing closures, “parametric, capture-by-unsafe-reference closures”, tag the nested function with the
@parameter
decorator. -
📢 Mojo now has full support for “runtime” closures: nested functions that capture state materialized as runtime values. This includes taking the address of functions, indirect calls, and passing closures around through function arguments. Note that capture-by-reference is still unsafe!
You can also take references to member functions with instances of that class using
foo.member_function
, which creates a closure withfoo
bound to theself
argument. -
📢 Mojo now supports Python style
with
statements and context managers.These things are very helpful for implementing things like our trace region support and things like Runtime support.
A context manager in Mojo implements three methods:
fn __enter__(self) -> T: fn __exit__(self): fn __exit__(self, err: Error) -> Bool:
The first is invoked when the context is entered, and returns a value that may optionally be bound to a target for use in the with body. If the with block exits normally, the second method is invoked to clean it up. If an error is raised, the third method is invoked with the Error value. If that method returns true, the error is considered handled, if it returns false, the error is re-thrown so propagation continues out of the ‘with’ block.
-
📢 Mojo functions now support variable scopes! Explicit
var
andlet
declarations inside functions can shadow declarations from higher “scopes”, where a scope is defined as any new indentation block. In addition, thefor
loop iteration variable is now scoped to the loop body, so it is finally possible to writefor i in range(1): pass for i in range(2): pass
-
📢 Mojo now supports an
@value
decorator on structs to reduce boilerplate and encourage best practices in value semantics. The@value
decorator looks to see the struct has a memberwise initializer (which has arguments for each field of the struct), a__copyinit__
method, and a__moveinit__
method, and synthesizes the missing ones if possible. For example, if you write:@value struct MyPet: var name: String var age: Int
The
@value
decorator will synthesize the following members for you:fn __init__(inout self, owned name: String, age: Int): self.name = name^ self.age = age fn __copyinit__(inout self, existing: Self): self.name = existing.name self.age = existing.age fn __moveinit__(inout self, owned existing: Self): self.name = existing.name^ self.age = existing.age
This decorator can greatly reduce the boilerplate needed to define common aggregates, and gives you best practices in ownership management automatically. The
@value
decorator can be used with types that need custom copy constructors (your definition wins). We can explore having the decorator take arguments to further customize its behavior in the future. -
📚 Memcpy and memcmp now consistently use count as the byte count.
-
📚 Add a variadic sting join on strings.
-
📚 Introduce a
reduce_bit_count
method to count the number of 1 across all elements in a SIMD vector. -
📚 Optimize the
pow
function if the exponent is integral. -
📚 Add a
len
function which dispatches to__len__
across the different structs that support it.
Week of 2023-04-17
-
📢 Error messages have been significantly improved, thanks to prettier printing for Mojo types in diagnostics.
-
📢 Variadic values can now be indexed directly without wrapping them in a
VariadicList
! -
📢
let
declarations in a function can now be lazily initialized, andvar
declarations that are never mutated get a warning suggesting they be converted to alet
declaration. Lazy initialization allows more flexible patterns of initialization than requiring the initializer be inline, e.g.:let x : Int if cond: = foo() x else: = bar() x use(x)
-
📢 Functions defined with
def
now returnobject
by default, instead ofNone
. This means you can return values (convertible toobject
) insidedef
functions without specifying a return type. -
📢 The
@raises
decorator has been removed. Raisingfn
should be declared by specifyingraises
after the function argument list. The rationale is thatraises
is part of the type system, instead of a function modifier. -
📢 The
BoolLiteral
type has been removed. Mojo now emitsTrue
andFalse
directly asBool
. -
📢 Syntax for function types has been added. You can now write function types with
fn(Int) -> String
orasync def(&String, *Int) -> None
. No more writing!kgen.signature
types by hand! -
📢 Float literals are not emitted as
FloatLiteral
instead of an MLIRf64
type! -
📢 Automatic destructors are now supported by Mojo types, currently spelled
fn __del___(owned self):
(the extra underscore will be dropped shortly). These destructors work like Python object destructors and similar to C++ destructors, with the major difference being that they run “as soon as possible” after the last use of a value. This means they are not suitable for use in C++-style RAII patterns (use thewith
statement for that, which is currently unsupported).These should be generally reliable for both memory-only and register-passable types, with the caveat that closures are known to not capture values correctly. Be very careful with interesting types in the vicinity of a closure!
-
A new (extremely dangerous!) builtin function is available for low-level ownership muckery. The
__get_address_as_owned_value(x)
builtin takes a low-level address value (of!pop.pointer
type) and returns anowned
value for the memory that is pointed to. This value is assumed live at the invocation of the builtin, but is “owned” so it needs to be consumed by the caller, otherwise it will be automatically destroyed. This is an effective way to do a “placement delete” on a pointer.# "Placement delete": destroy the initialized object begin pointed to. = __get_address_as_owned_value(somePointer.value) _ # Result value can be consumed by anything that takes it as an 'owned' # argument as well. consume(__get_address_as_owned_value(somePointer.value))
-
Another magic operator, named
__get_address_as_uninit_lvalue(x)
joins the magic LValue operator family. This operator projects a pointer to an LValue like__get_address_as_lvalue(x)
. The difference is that__get_address_as_uninit_lvalue(x)
tells the compiler that the pointee is uninitialized on entry and initialized on exit, which means that you can use it as a “placement new” in C++ sense.__get_address_as_lvalue(x)
tells the compiler that the pointee is initialized already, so reassigning over it will run the destructor.# "*Re*placement new": destroy the existing SomeHeavy value in the memory, # then initialize a new value into the slot. = SomeHeavy(4, 5) __get_address_as_lvalue(somePointer.value) # Ok to use an lvalue, convert to borrow etc. use(__get_address_as_lvalue(somePointer.value)) # "Placement new": Initialize a new value into uninitialied memory. = SomeHeavy(4, 5) __get_address_as_uninit_lvalue(somePointer.value) # Error, cannot read from uninitialized memory. use(__get_address_as_uninit_lvalue(somePointer.value))
Note that
__get_address_as_lvalue
assumes that there is already a value at the specified address, so the assignment above will run theSomeHeavy
destructor (if any) before reassigning over the value. -
📢 Implement full support for
__moveinit__
(aka move constructors)This implements the ability for memory-only types to define two different types of move ctors if they’d like:
fn __moveinit__(inout self, owned existing: Self)
: Traditional Rust style moving constructors that shuffles data around while taking ownership of the source binding.fn __moveinit__(inout self, inout existing: Self):
: C++ style “stealing” move constructors that can be used to take from an arbitrary LValue.
This gives us great expressive capability (better than Rust/C++/Swift) and composes naturally into our lifetime tracking and value categorization system.
-
The
__call__
method of a callable type has been relaxed to takeself
by borrow, allow non-copyable callees to be called. -
Implicit conversions are now invoked in
raise
statements properly, allowing converting strings toError
type. -
Automatic destructors are turned on for
__del__
instead of__del___
. -
📚 Add the builtin FloatLiteral type.
-
📚 Add integral
floordiv
andmod
for the SIMD type that handle negative values. -
📚 Add an F64 to String converter.
-
📚 Make the
print
function take variadic inputs.
Week of 2023-04-10
-
📢 Introduce consume operator
x^
This introduces the postfix consume operator, which produces an RValue given a lifetime tracked object (and, someday, a movable LValue).
-
Mojo now automatically synthesizes empty destructor methods for certain types when needed.
-
The
object
type has been built out into a fully-dynamic type, with dynamic function dispatch, with full error handling support.def foo(a) -> object: return (a + 3.45) < [1, 2, 3] # raises a TypeError
-
📢 The
@always_inline
decorator is no longer required for passing capturing closures as parameters, for both the functions themselves as functions with capturing closures in their parameters. These functions are still inlined but it is an implementation detail of capturing parameter closures. Mojo now distinguishes between capturing and non-capturing closures. Nested functions are capturing by default and can be made non-capturing with the@noncapturing
decorator. A top-level function can be passed as a capturing closure by marking it with the@closure
decorator. -
📢 Support for list literals has been added. List literals
[1, 2, 3]
generate a variadic heterogeneous list type. -
Variadics have been extended to work with memory-primary types.
-
Slice syntax is now fully-supported with a new builtin
slice
object, added to the compiler builtins. Slice indexing witha[1:2:3]
now emits calls to__setitem__
and__getitem__
with a slice object. -
Call syntax has been wired up to
__call__
. You can nowf()
on custom types! -
Closures are now explicitly typed as capturing or non-capturing. If a function intends to accept a capturing closure, it must specify the
capturing
function effect. -
📚 Add a
Tile2D
function to enable generic2D
tiling optimizations. -
📚 Add the
slice
struct to enable getting/setting spans of elements viagetitem
/setitem
. -
📚 Add syntax sugar to autotuning for both specifying the autotuned values, searching, and declaring the evaluation function.
Week of 2023-04-03
-
The
AnyType
andNoneType
aliases were added and auto-imported in all files. -
📢 The Mojo vscode plugin has been improved with docstring validation. It will now warn when a function’s docstring has a wrong argument name, for example.
-
📢 A new built-in literal type
TupleLiteral
was added in_CompilerBuiltin
. It represents literal tuple values such as(1, 2.0)
or()
. -
📢 The
Int
type has been moved to a newBuiltin
module and is auto-imported in all code. The type of integer literals has been changed from the MLIRindex
type to theInt
type. -
Mojo now has a powerful flow-sensitive uninitialized variable checker. This means that you need to initialize values before using them, even if you overwrite all subcomponents. This enables the compiler to reason about the true lifetime of values, which is an important stepping stone to getting automatic value destruction in place.
-
📢 Call syntax support has been added. Now you can directly call an object that implements the
__call__
method, likefoo(5)
. -
📢 The name for copy constructors got renamed from
__copy__
to__copyinit__
. Furthermore, non-@register_passable
types now implement it like they do an init method where you fill in a by-reference self, for example:fn __copyinit__(inout self, existing: Self): self.first = existing.first self.second = existing.second
This makes copy construction work more similarly to initialization, and still keeps copies
x = y
distinct from initializationx = T(y)
. -
📢 Initializers for memory-primary types are now required to be in the form
__init__(inout self, ...):
with a None result type, but for register primary types, it remains in the form__init__(...) -> Self:
. TheT{}
initializer syntax has been removed for memory-primary types. -
Mojo String literals now emit a builtin
StringLiteral
type! One less MLIR type to worry about. -
New
__getattr__
and__setattr__
dunder methods were added. Mojo calls these methods on a type when attempting member lookup of a non-static member. This allows writing dynamic objects likex.foo()
wherefoo
is not a member ofx
. -
Early destructor support has been added. Types can now define a special destructor method
__del___
(note three underscores). This is an early feature and it is still being built out. There are many caveats, bugs, and missing pieces. Stay tuned! -
📚 Integer division and mod have been corrected for rounding in the presence of negative numbers.
-
📚 Add scalar types (UI8, SI32, F32, F64, etc.) which are aliases to
SIMD[1, type]
.
March 2023
Week of 2023-03-27
-
📢 Parameter names are no longer load-bearing in function signatures. This gives more flexibility in defining higher-order functions, because the functions passed as parameters do not need their parameter names to match.
# Define a higher-order function... fn generator[ __mlir_type[`!kgen.signature<`, Int, `>() -> !lit.none`] func: ]():pass # Int parameter is named "foo". fn f0[foo: Int](): pass # Int parameter is named "bar". fn f1[bar: Int](): pass fn main(): # Both can be used as `func`! generator[f0]() generator[f1]()
Stay tuned for improved function type syntax…
-
📢 Two magic operators, named
__get_lvalue_as_address(x)
and__get_address_as_lvalue
convert stored LValues to and from!pop.pointer
types (respectively). This is most useful when using thePointer[T]
library type. ThePointer.address_of(lvalue)
method uses the first one internally. The second one must currently be used explicitly, and can be used to project a pointer to a reference that you can pass around and use as a self value, for example:# "Replacement new" SomeHeavy value into the memory pointed to by a # Pointer[SomeHeavy]. = SomeHeavy(4, 5) __get_address_as_lvalue(somePointer.value)
Note that
__get_address_as_lvalue
assumes that there is already a value at the specified address, so the assignment above will run theSomeHeavy
destructor (if any) before reassigning over the value. -
The
(((x)))
syntax is __mlir_op has been removed in favor of__get_lvalue_as_address
which solves the same problem and is more general. -
📢 When using a mutable
self
argument to a struct__init__
method, it now must be declared with&
, like any other mutable method. This clarifies the mutation model by making__init__
consistent with other mutating methods. -
📚 Add variadic string join function.
-
📚 Default initialize values with 0 or null if possible.
-
📚 Add compressed, aligned, and mask store intrinsics.
Week of 2023-03-20
-
Initial
String
type is added to the standard library with some very basic methods. -
Add
DimList
to remove the need to use an MLIR list type throughout the standard library. -
📢 The
__clone__
method for copying a value is now named__copy__
to better follow Python term of art. -
📢 The
__copy__
method now takes its self argument as a “borrowed” value, instead of taking it by reference. This makes it easier to write, works for@register_passable
types, and exposes more optimization opportunities to the early optimizer and dataflow analysis passes.# Before: fn __clone__(inout self) -> Self: ... # After: fn __copy__(self) -> Self: ...
-
📢 A new
@register_passable("trivial")
may be applied to structs that have no need for a custom__copy__
or__del__
method, and whose state is only made up of@register_passable("trivial")
types. This eliminates the need to define__copy__
boilerplate and reduces the amount of IR generated by the compiler for trivial types likeInt
. -
You can now write back to attributes of structs that are produced by a computed lvalue expression. For example
a[i].x = ..
works whena[i]
is produced with a__getitem__
/__setitem__
call. This is implemented by performing a read ofa[i]
, updating the temporary, then doing a writeback. -
The remaining hurdles to using non-parametric,
@register_passable
types as parameter values have been cleared. Types likeInt
should enjoy full use as parameter values. -
Parameter pack inference has been added to function calls. Calls to functions with parameter packs can now elide the pack types:
fn foo[*Ts: AnyType](*args: *Ts): pass 1, 1.2, True, "hello") foo(
Note that the syntax for parameter packs has been changed as well.
-
📚 Add the runtime string type.
-
📚 Introduce the DimList struct to remove the need to use low-level MLIR operations.
Week of 2023-03-13
-
📢 Initializers for structs now use
__init__
instead of__new__
, following standard practice in Python. You can write them in one of two styles, either traditional where you mutate self:fn __init__(self, x: Int): self.x = x
or as a function that returns an instance:
fn __init__(x: Int) -> Self: return Self {x: x}
Note that
@register_passable
types must use the later style. -
📢 The default argument convention is now the
borrowed
convention. A “borrowed” argument is passed like a C++const&
so it doesn’t need to invoke the copy constructor (aka the__clone__
method) when passing a value to the function. There are two differences from C++const&
:- A future borrow checker will make sure there are no mutable aliases with an immutable borrow.
@register_passable
values are passe