https://docs.modular.com/mojo/changelog.html#v0.6.0-2023-12-04
Modular Docs
* MAX Engine
* MAX Serving
* Mojo
* Get Started
Mojo changelog
* Overview
* Why Mojo
* -----------------------------------------------------------------
* Mojo Manual
+ Intro
+ Get started
o Get Mojo
o Hello, world!
+ Language basics
+ Functions
+ Variables
+ Structs
+ Traits
+ Value ownership
o Introduction
o Value semantics
o Ownership and borrowing
+ Value lifecycle
o Introduction
o Life of a value
o Death of a value
+ Metaprogramming
+ Python
o Python integration
o Python types
+ Decorators
o Overview
o @always_inline
o @noncapturing
o @nonmaterializable
o @parameter
o @register_passable
o @unroll
o @value
+ Modules and packages
* -----------------------------------------------------------------
* Mojo notebooks
+ Overview
+ Low-level IR in Mojo
+ Mandelbrot in Mojo with Python plots
+ Matrix multiplication in Mojo
+ Fast memset in Mojo
+ Ray tracing in Mojo
* Mojo library
+ Module index
+ Algorithm
o functional
o reduction
o sort
+ Autotune
o autotuning
+ Base64
o base64
+ Benchmark
o benchmark
+ Builtin
o bool
o builtin_list
o builtin_slice
o constrained
o coroutine
o debug_assert
o destructable
o dtype
o error
o file
o float_literal
o int
o int_literal
o io
o len
o object
o range
o rebind
o simd
o str
o string
o string_literal
o stringref
o tuple
o type_aliases
o value
+ Complex
o complex
+ Math
o bit
o limit
o math
o polynomial
+ Memory
o buffer
o memory
o unsafe
+ Os
o atomic
o env
+ Pathlib
o path
+ Python
o object
o python
+ Random
o random
+ Sys
o arg
o info
o intrinsics
o param_env
+ Tensor
o tensor
o tensor_shape
o tensor_spec
+ Testing
o testing
+ Time
o time
+ Utils
o index
o list
o static_tuple
o vector
* Mojo CLI
+ mojo
+ mojo build
+ mojo debug
+ mojo demangle
+ mojo doc
+ mojo format
+ mojo package
+ mojo repl
+ mojo run
* Roadmap & sharp edges
* Changelog
* FAQ
* Community
On this page
* Update Mojo
* v0.6.0 (2023-12-04)
+ Legendary
+ [?] New
+ Changed
+ [?] Fixed
+ Known Issue
* v0.5.0 (2023-11-2)
+ [?] New
+ Changed
+ [?] Fixed
* v0.4.0 for Mac (2023-10-19)
+ Legendary
* v0.4.0 (2023-10-05)
+ [?] New
+ Changed
+ [?] Fixed
* v0.3.1 (2023-09-28)
+ [?] Fixed
* v0.3.0 (2023-09-21)
+ [?] New
+ Changed
+ [?] Fixed
* v0.2.1 (2023-09-07)
+ Legendary
+ [?] New
+ Changed
+ [?] Fixed
* August 2023
+ 2023-08-24
+ 2023-08-09
+ 2023-08-03
* July 2023
+ 2023-07-26
+ 2023-07-20
+ 2023-07-12
+ 2023-07-05
* June 2023
+ 2023-06-29
+ 2023-06-21
+ 2023-06-14
+ 2023-06-07
* May 2023
+ 2023-05-31
+ 2023-05-24
+ 2023-05-16
+ 2023-05-11
+ 2023-05-02
* April 2023
+ Week of 2023-04-24
+ Week of 2023-04-17
+ Week of 2023-04-10
+ Week of 2023-04-03
* March 2023
+ Week of 2023-03-27
+ Week of 2023-03-20
+ Week of 2023-03-13
+ Week of 2023-03-06
* February 2023
+ Week of 2023-02-27
+ Week of 2023-02-20
+ Week of 2023-02-13
+ Week of 2023-02-06
* January 2023
+ Week of 2023-01-30
+ Week of 2023-01-23
+ Week of 2023-01-16
+ Week of 2023-01-09
+ Week of 2023-01-02
* December 2022
+ Week of 2022-12-26
+ Week of 2022-12-19
+ Week of 2022-12-12
+ Week of 2022-12-05
* November 2022
+ Week of 2022-11-28
+ Week of 2022-11-21
+ Week of 2022-11-14
+ Week of 2022-11-07
* October 2022
+ Week of 2022-10-31
+ Week of 2022-10-24
+ Week of 2022-10-17
+ Week of 2022-10-10
+ Week of 2022-10-03
* September 2022
+ Week of 2022-09-26
Edit this page
Report an issue
Mojo changelog
A history of significant Mojo changes.
This is a running list of significant changes for the Mojo language
and tools. It doesn't include all internal implementation changes.
Update Mojo
If you don't have Mojo yet, see the get started guide.
To see your current Mojo version, run this:
mojo --version
To update Mojo to the latest release, run this:
modular update mojo
However, if your current version is 0.3.0 or lower, you'll need these
additional commands:
sudo apt-get update
sudo apt-get install modular
modular clean
modular install mojo
v0.6.0 (2023-12-04)
Legendary
* Traits have arrived!
You can now define a trait, which consists of a required set of
method prototypes. A struct can conform to the trait by
implementing these methods. This lets you write generic functions
that work on any structs that conform to a given trait.
The following section gives a brief overview of traits--see the
Mojo Manual and this traits blog post for more details!
Traits are declared with the trait keyword. The bodies of traits
should contain method signatures declared with ... as their
bodies. Default method implementations are not supported yet.
trait SomeTrait:
fn required_method(self, x: Int): ...
The trait can be implemented on a struct by inheriting from it.
struct SomeStruct(SomeTrait):
fn required_method(self, x: Int):
print("hello traits", x)
You can then write a generic functions that accepts any type that
conforms to the trait. You do this by creating a parameterized
function with a trait-typed parameter:
fn fun_with_traits[T: SomeTrait](x: T):
x.required_method(42)
Which can be invoked with instances of types that conform to the
trait:
var thing = SomeStruct()
# Infer the parameter `T`!
fun_with_traits(thing)
Traits can also inherit from other traits, which simply requires
that implementors of the child trait also conform to all parent
traits.
trait Parent:
fn parent_func(self): ...
trait Child(Parent):
fn child_func(self): ...
Then, both child and parent trait methods can be invoked on
instances of the trait Child. As well, an instance of the child
trait can be converted to an instance of the parent trait.
fn the_parents[T: Parent](x: T):
x.parent_func()
fn the_children[T: Child](x: T):
x.child_func()
x.parent_func()
# Upcast `x` from instance of `Child` to `Parent`.
the_parents(x)
For more information, see the Traits page in the Mojo Manual.
* A fundamental Destructable trait has been added to the language.
This is a core trait that every trait automatically conforms to.
This enables destruction of generic types and generic
collections.
Note: We're aware that this trait might be better spelled
Destructible. We're planning on removing it in the future and
moving its functionality to AnyType so that any type that doesn't
provide its own destructor will have a default, no-op destructor.
* We've added some traits to the standard library, you can
implement these on your own types:
+ Destructable
+ Copyable
+ Movable
+ Stringable
+ Intable
+ Sized
+ CollectionElement
* We added built-in len(), str(), and int() functions, which work
with types that implement the Sized, Stringable, and Intable
traits, respectively.
* DynamicVector is now a proper generic collection that can use any
type that implements the Movable and Copyable traits. This means
you can now write, for example, DynamicVector[String]. Also,
DynamicVector now invokes its element destructors upon
destruction, so _del_old has been deleted.
* print now works on any types that implement Stringable by
invoking their __str__ method:
@value
struct BoxedInt(Stringable):
var value: Int
fn __str__(self) -> String:
return self.value
print(BoxedInt(11), "hello traits!", BoxedInt(42))
[?] New
* The Mojo Manual is an all-new, complete Mojo user guide. It
doesn't include everything about Mojo yet, but it includes a lot,
and more than the original programming manual (now deprecated).
Plus, the entire Mojo Manual and other Mojo docs are now
open-sourced on GitHub, and we'd love to accept contributions to
help us improve them!
* Mojo now supports partial automatic parameterization: when a
function is declared with an argument of a partially bound type,
the unbound parameters of that type are implicitly added to the
function's input parameters. For example:
@value
struct Fudge[a: Int, b: Int, c: Int = 7]: ...
# These function declarations are roughly equivalent:
fn eat(f: Fudge[5]): ... # implicitly parameterized
fn eat[_b: Int](f: Fudge[5, _b]): ... # explicitly parameterized
In the first signature for eat(), the b parameter isn't bound, so
it's implicitly added as an input parameter on the function.
In the second signature for eat(), the author has explicitly
defined an input parameter (_b), which is bound to the second
parameter on the argument type (which happens to be b).
Both functions can be called like this:
eat(Fudge[5, 8]())
Mojo infers the value of the b parameter from the argument (in
this case, 8).
With the second signature, you can also pass the _b parameter
value explicitly:
eat[3](Fudge[5, 3]())
Moreover, Mojo now allows you to explicitly mark parameters as
unbound using the _ as syntax meaning "placeholder for an unbound
parameter." For example:
# These function declarations are roughly equivalent:
fn eat(f: Fudge[5, _, c=_]): ... # implicitly parameterized
fn eat(f: Fudge[c=_, a=5, b=_]): ... # implicitly parameterized
fn eat[_b: Int, _c: Int](f: Fudge[5, _b, _c]): ... # explicitly parameterized
The first two signatures explicitly unbind the b and c
parameters.
In the last signature, the _b and _c parameters are explicitly
declared by the author, and bound to the b and c parameters in
the argument type.
Any of these signatures can be called like this:
eat(Fudge[5, 8]())
eat(Fudge[5, 8, 9]())
Note that the default parameter values of struct parameters are
bound, unless explicitly unbound by the user.
For more information, see the Mojo Manual.
* Parametric types can now be partially bound in certain contexts.
For example, a new Scalar type alias has been added defined as:
alias Scalar = SIMD[size=1]
Which creates a parametric type alias Scalar with a single
parameter of type DType. Types can also be partially or fully
bound in other contexts. For instance, alias declarations of type
values inside functions now work properly:
fn type_aliases():
alias T = SIMD
print(T[DType.float32, 1]())
alias Partial = T[type=DType.int32]
print(Partial[2]())
* The __mlir_op feature now supports operations that return
multiple results. To use them, you write the _type field as a
Tuple of types. For example:
# The `ret` variable has type `Tuple[Int, Int]`.
let ret = __mlir_op.`multi_result_op`[ _type = (Int, Int) ]()
* Mojo now has the ability to read raw bytes from a file using the
read_bytes() method. For example:
with open("file.binary", "r") as f:
data = f.read_bytes()
* A size argument was added to the read() and read_bytes() methods
on the builtin file.FileHandle. The size argument defaults to -1
and maintains the previous "read to EOF" behavior when size is
negative.
with open("file.binary", "r") as f:
data1 = f.read_bytes(1024)
data2 = f.read_bytes(256)
* Path now has read_bytes() and read_text() methods to read file
contents from a path:
let text_path = Path("file.txt")
let text = text_path.read_text()
let binary_path = Path("file.binary")
let data = binary_path.read_bytes()
* Tensor has new save() and load() methods to save and load to
file. These methods preserve shape and datatype information. For
example:
let tensor = Tensor[DType.float32]()
tensor.save(path)
let tensor_from_file = Tensor[DType.float32].load(path)
+ Subscripting added to DTypePointer and Pointer:
let p = DTypePointer[DType.float16].alloc(4)
for i in range(4):
p[i] = i
print(p[i])
* file.FileHandle now has a seek() method.
* String now has an rfind() method analogous to Python's str.rfind
().
* String now has an split() method analogous to Python's str.split
().
* Path now has a suffix() method analogous to Python's
pathlib.Path.suffix.
* The Mojo REPL now supports indented expressions, making it a bit
easier to execute expressions copied from an indented block (such
as a doc string).
* The Mojo Language Server now implements the Document Symbols
request. IDEs use this to provide support for Outline View and Go
to Symbol. This addresses Issue #960.
* The Mojo Language Server now shows documentation when code
completing modules or packages in import statements.
* The Mojo Language Server now supports processing code examples,
defined as markdown Mojo code blocks, inside of doc strings. This
enables IDE features while writing examples in API documentation.
* The Mojo Language Server now provides semantic token information,
providing better highlighting for symbols whose semantics are not
statically analyzable.
* The Mojo Language Server now classifies doc strings as folding
ranges, making them easier to collapse, reducing vertical space
while editing.
* Command line options for the mojo driver that take arguments can
now be written in either of two ways: both --foo FOO and --foo=
FOO. Previously, only the former was valid.
Changed
* Variadic list types VariadicList and VariadicListMem are now
iterable. Variadic arguments are automatically projected into one
of these types inside the function body, so var args can be
iterated:
fn print_ints(*nums: Int):
for num in nums:
print(num)
print(len(nums))
* The assert functions in the testing package now raise an Error
when the assertion fails instead of returning a Bool for whether
the assertion succeeded or not.
* Parameters of AnyType type are no longer (implicitly) assumed to
be register-passable. A new AnyRegType type is used to represent
generic types that are register passable.
* Changing the units in a benchmark report is now an argument
instead of a parameter:
let report = benchmark.run[timer]()
report.print(Unit.ms)
* Default values on inout arguments are no longer permitted,
i.e. the following will now raise an error:
fn inout_default(inout x: Int = 2): ...
* The to_string() function has been removed from PythonObject in
favor of the new __str__() function. This composes better with
traits so it can be used with the generic str() function.
[?] Fixed
* #734 - Consumption of struct works only for types with a __del__
method.
* #910 - Parser crash when using memory-only generic type as return
of function that raises.
* #1060 - Mojo happily parses code that has messed up indentation
* #1159 - The language server doesn't warn about bad return type.
* #1166 - warning: unreachable code after return statement with
context manager
* #1098 - The language server doesn't highlight properties of
PythonObjects correctly.
* #1153 - The language server crashes when parsing an invalid
multi-nested module import.
* #1236 - The language server doesn't show autocomplete in if
statements.
* #1246 - Warning diagnostics are transient in the presence of
caching.
Known Issue
* There is an issue affecting Jupyter notebooks that use autotuning
and traits. This issue only manifests on macOS, and the same code
runs without issue outside of the notebooks. This issue affects
the Matrix multiplication in Mojo notebook.
v0.5.0 (2023-11-2)
[?] New
* The SIMD type now defaults to the architectural SIMD width of the
type. This means you can write SIMD[DType.float32] which is
equivalent to SIMD[DType.float32, simdwidthof[DType.float32]()].
* The SIMD type now contains a join() function that allows you to
concatenate two SIMD values together and produce a new SIMD
value.
* Mojo now supports compile-time keyword parameters, in addition to
existing support for keyword arguments. For example:
fn foo[a: Int, b: Int = 42]():
print(a, "+", b)
foo[a=5]() # prints '5 + 42'
foo[a=7, b=13]() # prints '7 + 13'
foo[b=20, a=6]() # prints '6 + 20'
Keyword parameters are also supported in structs:
struct KwParamStruct[a: Int, msg: String = "mojo"]:
fn __init__(inout self):
print(msg, a)
fn use_kw_params():
KwParamStruct[a=42]() # prints 'mojo 42'
KwParamStruct[5, msg="hello"]() # prints 'hello 5'
KwParamStruct[msg="hello", a=42]() # prints 'hello 42'
For more detail, see the programming manual.
For the time being, the following notable limitations apply:
+ Keyword-only parameters are not supported yet:
fn baz[*args: Int, b: Int](): pass # fails
fn baz[a: Int, *, b: Int](): pass # fails
(The analogous keyword-only arguments in Python are described
in PEP 3102.)
+ Variadic keyword parameters are not supported yet:
fn baz[a: Int, **kwargs: Int](): pass # fails
* Mojo now supports "automatic" parameterization of functions. What
this means is that if a function argument type is parametric but
has no bound parameters, they are automatically added as input
parameters on the function. This works with existing features to
allow you to write parametric functions with less boilerplate.
@value
struct Thing[x: Int, y: Int]:
pass
fn foo(v: Thing):
print(v.x)
print(v.y)
fn main():
let v = Thing[2, 3]()
foo(v)
However, partial autoparameterization is not supported yet:
fn foo(v: Thing[y=7]): # Partially bound type not allowed yet.
...
* Keyword argument passing is supported when invoking __getitem__
using the bracket syntax:
@value
struct MyStruct:
fn __getitem__(self, x: Int, y: Int, z: Int) -> Int:
return x * y + z
MyStruct()[z=7, x=3, y=5] # returns 22
However, keyword argument passing to __setitem__ using the
bracket syntax is not supported yet:
@value
struct OtherStruct:
fn __setitem__(self, x: Int, y: Int): pass
OtherStruct()[x=1] = 4 # fails
* Function argument input parameters can now be referenced within
the signature of the function:
fn foo(x: SIMD, y: SIMD[x.type, x.size]):
pass
* The benchmark module has been simplified and improved so you can
now run:
import benchmark
from time import sleep
fn sleeper():
sleep(.01)
fn main():
let report = benchmark.run[sleeper]()
print(report.mean())
It no longer requires a capturing fn so can benchmark functions
outside the same scope.
You can print a report with:
report.print()
---------------------
Benchmark Report (s)
---------------------
Mean: 0.012314264957264957
Total: 1.440769
Iters: 117
Warmup Mean: 0.0119335
Warmup Total: 0.023866999999999999
Warmup Iters: 2
Fastest Mean: 0.012227958333333334
Slowest Mean: 0.012442699999999999
Units for all functions default to seconds, but can be changed
with:
from benchmark import Unit
report.print[Unit.ms]()
* Mojo now supports struct parameter deduction (a.k.a. class
template argument deduction, or CTAD) for partially bound types.
Struct parameter deduction is also possible from static methods.
For example:
@value
struct Thing[v: Int]: pass
struct CtadStructWithDefault[a: Int, b: Int, c: Int = 8]:
fn __init__(inout self, x: Thing[a]):
print("hello", a, b, c)
@staticmethod
fn foo(x: Thing[a]):
print("", a, b, c)
fn main():
_ = CtadStructWithDefault[b=7](Thing[6]()) # prints 'hello 6 7 8'
CtadStructWithDefault[b=7].foo(Thing[6]()) # prints ' 6 7 8'
* Tensor has new fromfile() and tofile() methods to save and load
as bytes from a file.
* The built-in print() function now works on the Tensor type.
* TensorShape and TensorSpec now have constructors that take
DynamicVector[Int] and StaticIntTuple to initialize shapes.
* The String type now has the count() and find() methods to enable
counting the number of occurrences or finding the offset index of
a substring in a string.
* The String type now has a replace() method which allows you to
replace a substring with another string.
Changed
* VariadicList and VariadicListMem moved under builtins, and no
longer need to be imported.
* Variadic arguments are now automatically projected into a
VariadicList or VariadicListMem inside the function body. This
allows for more flexibility in using var args. For example:
fn print_ints(*nums: Int):
let len = len(nums)
for i in range(len):
print(nums[i])
print(len)
* The parameters for InlinedFixedVector have been switched. The
parameters are now [type, size] instead of [size, type]. The
InlinedFixedVector now has a default size which means that one
can just use InlinedFixedVector as InlinedFixedVector[Float32]
and the default size is used.
* write_file() method in Buffer and NDBuffer is renamed to tofile()
to match the Python naming.
* Mojo will now utilize all available cores across all NUMA sockets
on the host machine by default. The prior default behavior was to
use all the cores on the first socket.
Removed
* The math.numerics module is now private, because its types
(FPUtils and FlushDenormals) should not be used externally.
[?] Fixed
* #532 - Compiler optimizing while True loop away
* #760 - Compilation error: 'hlcf.for.yield' op specifies 0 branch
inputs but target expected 1 along control-flow edge from here
* #849 - The Tensor type is now initialized with zeros at
construction time.
* #912 - Invalid load for __get_address_as_lvalue.
* #916 - Parser crash when specifying default values for inout
arguments.
* #943 - Mojo hangs if you use continue in the nested loop
* #957 - Parser crash when a function call with variadic arguments
of a memory-only type is evaluated at compile time.
* #990 - Fixes rounding issue with floor division with negative
numerator.
* #1018 - In some cases the sort function was returning invalid
results. This release fixes some of these corner cases.
* #1010 - Initializing tensor in alias declaration results in
crash.
* #1110 - The time.now() function now returns nanoseconds across
all operating systems.
* #1115 - cannot load non-register passable type into SSA register.
v0.4.0 for Mac (2023-10-19)
Legendary
* Mojo for Mac!
The Mojo SDK now works on macOS (Apple silicon). This is the same
version previously released for Linux. Get the latest version of
the SDK for your Mac system:
Download Now!
v0.4.0 (2023-10-05)
[?] New
* Mojo now supports default parameter values. For example:
fn foo[a: Int = 3, msg: StringLiteral = "woof"]():
print(msg, a)
fn main():
foo() # prints 'woof 3'
foo[5]() # prints 'woof 5'
foo[7, "meow"]() # prints 'meow 7'
Inferred parameter values take precedence over defaults:
@value
struct Bar[v: Int]:
pass
fn foo[a: Int = 42, msg: StringLiteral = "quack"](bar: Bar[a]):
print(msg, a)
fn main():
foo(Bar[9]()) # prints 'quack 9'
Structs also support default parameters:
@value
struct DefaultParams[msg: StringLiteral = "woof"]:
alias message = msg
fn main():
print(DefaultParams[]().message) # prints 'woof'
print(DefaultParams["meow"]().message) # prints 'meow'
* The new file module adds basic file I/O support. You can now
write:
var f = open("my_file.txt", "r")
print(f.read())
f.close()
or
with open("my_file.txt", "r") as f:
print(f.read())
* Mojo now allows context managers to support an __enter__ method
without implementing support for an __exit__ method, enabling
idioms like this:
# This context manager consumes itself and returns it as the value.
fn __enter__(owned self) -> Self:
return self^
Here Mojo cannot invoke a noop __exit__ method because the
context manager is consumed by the __enter__ method. This can be
used for types (like file descriptors) that are traditionally
used with with statements, even though Mojo's guaranteed early
destruction doesn't require that.
* A very basic version of pathlib has been implemented in Mojo. The
module will be improved to achieve functional parity with Python
in the next few releases.
* The memory.unsafe module now contains a bitcast function. This is
a low-level operation that enables bitcasting between pointers
and scalars.
* The input parameters of a parametric type can now be directly
accessed as attribute references on the type or variables of the
type. For example:
@value
struct Thing[param: Int]:
pass
fn main():
print(Thing[2].param) # prints '2'
let x = Thing[9]()
print(x.param) # prints '9'
Input parameters on values can even be accessed in parameter
contexts. For example:
fn foo[value: Int]():
print(value)
let y = Thing[12]()
alias constant = y.param + 4
foo[constant]() # prints '16'
* The Mojo REPL now supports code completion. Press Tab while
typing to query potential completion results.
* Error messages from Python are now exposed in Mojo. For example
the following should print No module named
'my_uninstalled_module':
fn main():
try:
let my_module = Python.import_module("my_uninstalled_module")
except e:
print(e)
* Error messages can now store dynamic messages. For example, the
following should print "Failed on: Hello"
fn foo(x:String) raises:
raise Error("Failed on: " + x)
fn main():
try:
foo("Hello")
except e:
print(e)
Changed
* We have improved and simplified the parallelize function. The
function now elides some overhead by caching the Mojo parallel
runtime.
* The Mojo REPL and Jupyter environments no longer implicitly
expose Python, PythonObject, or Pointer. These symbols must now
be imported explicitly, for example:
from python import Python
from python.object import PythonObject
from memory.unsafe import Pointer
* The syntax for specifying attributes with the __mlir_op prefix
have changed to mimic Python's keyword argument passing syntax.
That is, = should be used instead of :, e.g.:
# Old syntax, now fails.
__mlir_op.`index.bool.constant`[value : __mlir_attr.`false`]()
# New syntax.
__mlir_op.`index.bool.constant`[value=__mlir_attr.`false`]()
* You can now print the Error object directly. The message() method
has been removed.
[?] Fixed
* #794 - Parser crash when using the in operator.
* #936 - The Int constructor now accepts other Int instances.
* #921 - Better error message when running mojo on a module with no
main function.
* #556 - UInt64s are now printed correctly.
* #804 - Emit error instead of crashing when passing variadic
arguments of unsupported types.
* #833 - Parser crash when assigning module value.
* #752 - Parser crash when calling async def.
* #711 - The overload resolution logic now correctly prioritizes
instance methods over static methods (if candidates are an
equally good match otherwise), and no longer crashed if a static
method has a Self type as its first argument.
* #859 - Fix confusing error and documentation of the rebind
builtin.
* #753 - Direct use of LLVM dialect produces strange errors in the
compiler.
* #926 - Fixes an issue that occured when a function with a return
type of StringRef raised an error. When the function raised an
error, it incorrectly returned the string value of that error.
* #536 - Report More information on python exception.
v0.3.1 (2023-09-28)
Our first-ever patch release of the Mojo SDK is here! Release v0.3.1
includes primarily installation-related fixes. If you've had trouble
installing the previous versions of the SDK, this release may be for
you.
[?] Fixed
* #538 - Installation hangs during the testing phase. This issue
occurs on machines with a low number of CPU cores, such as free
AWS EC2 instances and GitHub Codespaces.
* #590 - Installation fails with a "failed to run python" message.
* #672 - Language server hangs on code completion. Related to #538,
this occurs on machines with a low number of CPU cores.
* #913 - In the REPL and Jupyter notebooks, inline comments were
being parsed incorrectly.
v0.3.0 (2023-09-21)
There's more Mojo to love in this, the second release of the Mojo
SDK! This release includes new features, an API change, and bug
fixes.
There's also an updated version of the Mojo extension for VS Code.
[?] New
* Mojo now has partial support for passing keyword arguments to
functions and methods. For example the following should work:
fn foo(a: Int, b: Int = 3) -> Int:
return a * b
fn main():
print(foo(6, b=7)) # prints '42'
print(foo(a=6, b=7)) # prints '42'
print(foo(b=7, a=6)) # prints '42'
Parameters can also be inferred from keyword arguments, for
example:
fn bar[A: AnyType, B: AnyType](a: A, b: B):
print("Hello ")
fn bar[B: AnyType](a: StringLiteral, b: B):
print(a)
fn main():
bar(1, 2) # prints `Hello `
bar(b=2, a="Yay!") # prints `Yay!`
For the time being, the following notable limitations apply:
+ Keyword-only arguments are not supported:
fn baz(*args: Int, b: Int): pass # fails
fn baz(a: Int, *, b: Int): pass # fails
(Keyword-only arguments are described in PEP 3102.)
+ Variadic keyword arguments are not supported:
fn baz(a: Int, **kwargs: Int): pass # fails
* Mojo now supports the @nonmaterializable decorator. The purpose
is to mark data types that should only exist in the parameter
domain. To use it, a struct is decorated with @nonmaterializable
(TargetType). Any time the nonmaterializable type is converted
from the parameter domain, it is automatically converted to
TargetType. A nonmaterializable struct should have all of its
methods annotated as @always_inline, and must be computable in
the parameter domain. In the following example, the NmStruct type
can be added in the parameter domain, but are converted to
HasBool when materialized.
@value
@register_passable("trivial")
struct HasBool:
var x: Bool
fn __init__(x: Bool) -> Self:
return Self {x: x}
@always_inline("nodebug")
fn __init__(nms: NmStruct) -> Self:
return Self {x: True if (nms.x == 77) else False}
@value
@nonmaterializable(HasBool)
@register_passable("trivial")
struct NmStruct:
var x: Int
@always_inline("nodebug")
fn __add__(self: Self, rhs: Self) -> Self:
return NmStruct(self.x + rhs.x)
alias stillNmStruct = NmStruct(1) + NmStruct(2)
# When materializing to a run-time variable, it is automatically converted,
# even without a type annotation.
let convertedToHasBool = stillNmStruct
* Mojo integer literals now produce the IntLiteral infinite
precision integer type when used in the parameter domain.
IntLiteral is materialized to the Int type for runtime
computation, but intermediate computations at compile time, using
supported operators, can now exceed the bit width of the Int
type.
* The Mojo Language Server now supports top-level code completions,
enabling completion when typing a reference to a variable, type,
etc. This resolves #679.
* The Mojo REPL now colorizes the resultant variables to help
distinguish input expressions from the output variables.
Changed
* Mojo allows types to implement two forms of move constructors,
one that is invoked when the lifetime of one value ends, and one
that is invoked if the compiler cannot prove that. These were
previously both named __moveinit__, with the following two
signatures:
fn __moveinit__(inout self, owned existing: Self): ...
fn __moveinit__(inout self, inout existing: Self): ...
We've changed the second form to get its own name to make it more
clear that these are two separate operations: the second has been
renamed to __takeinit__:
fn __moveinit__(inout self, owned existing: Self): ...
fn __takeinit__(inout self, inout existing: Self): ...
The name is intended to connote that the operation takes the
conceptual value from the source (without destroying it) unlike
the first one which "moves" a value from one location to another.
For more information, see the Mojo Manual section on move
constructors.
* The Error type in Mojo has changed. Instead of extracting the
error message using error.value you will now extract the error
message using error.message().
[?] Fixed
* #503 - Improve error message for failure lowering
kgen.param.constant.
* #554 - Alias of static tuple fails to expand.
* #500 - Call expansion failed due to verifier error.
* #422 - Incorrect comment detection in multiline strings.
* #729 - Improve messaging on how to exit the REPL.
* #756 - Fix initialization errors of the VS Code extension.
* #575 - Build LLDB/REPL with libedit for a nicer editing
experience in the terminal.
v0.2.1 (2023-09-07)
The first versioned release of Mojo!
All earlier releases were considered version 0.1.
Legendary
* First release of the Mojo SDK!
You can now develop with Mojo locally. The Mojo SDK is currently
available for Ubuntu Linux systems, and support for Windows and
macOS is coming soon. You can still develop from a Windows or Mac
computer using a container or remote Linux system.
The Mojo SDK includes the Mojo standard library and the Mojo
command-line interface (CLI), which allows you to run, compile,
and package Mojo code. It also provides a REPL programming
environment.
Get the Mojo SDK!
* First release of the Mojo extension for VS Code.
This provides essential Mojo language features in Visual Studio
Code, such as code completion, code quick fixes, docs tooltips,
and more. Even when developing on a remote system, using VS Code
with this extension provides a native-like IDE experience.
[?] New
* A new clobber_memory function has been added to the benchmark
module. The clobber memory function tells the system to flush all
memory operations at the specified program point. This allows you
to benchmark operations without the compiler reordering memory
operations.
* A new keep function has been added to the benchmark module. The
keep function tries to tell the compiler not to optimize the
variable away if not used. This allows you to avoid compiler's
dead code elimination mechanism, with a low footprint side
effect.
* New shift_right and shift_left functions have been added to the
simd module. They shift the elements in a SIMD vector right/left,
filling elements with zeros as needed.
* A new cumsum function has been added to the reduction module that
computes the cumulative sum (also known as scan) of input
elements.
* Mojo Jupyter kernel now supports code completion.
Changed
* Extends rotate_bits_left, rotate_left, rotate_bits_right, and
rotate_right to operate on Int values. The ordering of parameters
has also been changed to enable type inference. Now it's possible
to write rotate_right[shift_val](simd_val) and have the dtype and
simd_width inferred from the argument. This addresses Issue #528.
[?] Fixed
* Fixed a bug causing the parser to crash when the with statement
was written without a colon. This addresses Issue #529.
* Incorrect imports no longer crash when there are other errors at
the top level of a module. This fixes Issue #531.
August 2023
2023-08-24
* Fixed issue where the with expr as x statement within fn behaved
as if it were in a def, binding x with function scope instead of
using lexical scope.
[?] New
* Major refactoring of the standard library to enable packaging and
better import ergonomics:
+ The packages are built as binaries to improve startup speed.
+ Package and module names are now lowercase to align with the
Python style.
+ Modules have been moved to better reflect the purpose of the
underlying functions (e.g. Pointer is now within the unsafe
module in the memory package).
+ The following modules are now included as built-ins: SIMD,
DType, IO, Object, and String. This means it's no longer
necessary to explicitly import these modules. Instead, these
modules will be implicitly imported for the user. Private
methods within the module are still accessible using the
builtin.module_name._private_method import syntax.
+ New math package has been added to contain the bit, math,
numerics, and polynomial modules. The contents of the
math.math module are re-exported into the math package.
* Mojo now supports using memory-only types in parameter
expressions and as function or type parameters:
@value
struct IntPair:
var first: Int
var second: Int
fn add_them[value: IntPair]() -> Int:
return value.first + value.second
fn main():
print(add_them[IntPair(1, 2)]()) # prints '3'
* In addition, Mojo supports evaluating code that uses
heap-allocated memory at compile-time and materializing
compile-time values with heap-allocated memory into dynamic
values:
fn fillVector(lowerBound: Int, upperBound: Int, step: Int) -> DynamicVector[Int]:
var result = DynamicVector[Int]()
for i in range(lowerBound, upperBound, step):
result.push_back(i)
return result
fn main():
alias values = fillVector(5, 23, 7)
for i in range(0, values.__len__()):
print(values[i]) # prints '5', '12', and then '19'
Changed
* def main():, without the explicit None type, can now be used to
define the entry point to a Mojo program.
* The assert_param function has been renamed to constrained and is
now a built-in function.
* The print function now works on Complex values.
[?] Fixed
* Fixed issues with print formatting for DType.uint16 and
DType.int16.
* Issue #499 - Two new rotate_right and rotate_left functions have
been added to the SIMD module.
* Issue #429 - You can now construct a Bool from a SIMD type whose
element-type is DType.bool.
* Issue #350 - Confusing Matrix implementation
* Issue #349 - Missing load_tr in struct Matrix
* Issue #501 - Missing syntax error messages in Python expressions.
2023-08-09
Changed
* The ref and mutref identifiers are now treated as keywords, which
means they cannot be used as variable, attribute, or function
names. These keywords are used by the "lifetimes" features, which
is still in development. We can consider renaming these (as well
as other related keywords) when the development work gels,
support is enabled in public Mojo builds, and when we have
experience using them.
* The argument handling in def functions has changed: previously,
they had special behavior that involved mutable copies in the
callee. Now, we have a simple rule, which is that def argument
default to the owned convention (fn arguments still default to
the borrowed convention).
This change is mostly an internal cleanup and simplification of
the compiler and argument model, but does enable one niche
use-case: you can now pass non-copyable types to def arguments by
transferring ownership of a value into the def call. Before, that
would not be possible because the copy was made on the callee
side, not the caller's side. This also allows the explicit use of
the borrowed keyword with a def that wants to opt-in to that
behavior.
2023-08-03
[?] New
* A new Tensor type has been introduced. This tensor type manages
its own data (unlike NDBuffer and Buffer which are just views).
Therefore, the tensor type performs its own allocation and free.
Here is a simple example of using the tensor type to represent an
RGB image and convert it to grayscale:
from tensor import Tensor, TensorShape
from utils.index import Index
from random import rand
let height = 256
let width = 256
let channels = 3
# Create the tensor of dimensions height, width, channels and fill with
# random value.
let image = rand[DType.float32](height, width, channels)
# Declare the grayscale image.
var gray_scale_image = Tensor[DType.float32](height, width)
# Perform the RGB to grayscale transform.
for y in range(height):
for x in range(width):
let r = image[y,x,0]
let g = image[y,x,1]
let b = image[y,x,2]
gray_scale_image[Index(y,x)] = 0.299 * r + 0.587 * g + 0.114 * b
[?] Fixed
* Issue #53 - Int now implements true division with the / operator.
Similar to Python, this returns a 64-bit floating point number.
The corresponding in-place operator, /=, has the same semantics
as //=.
July 2023
2023-07-26
[?] New
* Types that define both __getitem__ and __setitem__ (i.e. where
sub-scripting instances creates computed LValues) can now be
indexed in parameter expressions.
* Unroll decorator for loops with constant bounds and steps:
+ @unroll: Fully unroll a loop.
+ @unroll(n): Unroll a loop by factor of n, where n is a
positive integer.
+ Unroll decorator requires loop bounds and iteration step to
be compiler time constant value, otherwise unrolling will
fail with compilation error. This also doesn't make loop
induction variable a parameter.
# Fully unroll the loop.
@unroll
for i in range(5):
print(i)
# Unroll the loop by a factor of 4 (with remainder iterations of 2).
@unroll(4)
for i in range(10):
print(i)
* The Mojo REPL now prints the values of variables defined in the
REPL. There is full support for scalars and structs. Non-scalar
SIMD vectors are not supported at this time.
[?] Fixed
* Issue #437 - Range can now be instantiated with a PythonObject.
* Issue #288 - Python strings can now be safely copied.
2023-07-20
[?] New
* Mojo now includes a Limits module, which contains functions to
get the max and min values representable by a type, as requested
in Issue #51. The following functions moved from Math to Limits:
inf(), neginf(), isinf(), isfinite().
* Mojo decorators are now distinguished between "signature" and
"body" decorators and are ordered. Signature decorators, like
@register_passable and @parameter, modify the type of declaration
before the body is parsed. Body decorators, like @value, modify
the body of declaration after it is fully parsed. Due to
ordering, a signature decorator cannot be applied after a body
decorator. That means the following is now invalid:
@register_passable # error: cannot apply signature decorator after a body one!
@value
struct Foo:
pass
* Global variables can now be exported in Mojo compiled archives,
using the @export decorator. Exported global variables are public
symbols in compiled archives and use the variable name as its
linkage name, by default. A custom linkage name can be specified
with @export("new_name"). This does not affect variable names in
Mojo code.
* Mojo now supports packages! A Mojo package is defined by placing
an __init__.mojo or __init__. within a directory. Other files in
the same directory form modules within the package (this works
exactly like it does in Python). Example:
main.
my_package/
__init__.
module.
my_other_package/
__init__.
stuff.
# main.
from my_package.module import some_function
from my_package.my_other_package.stuff import SomeType
fn main():
var x: SomeType = some_function()
* Mojo now supports direct module and package imports! Modules and
packages can be imported and bound to names. Module and package
elements, like functions, types, global variables, and other
modules, can be accessed using attribute references, like
my_module.foo. Note that modules lack runtime representations,
meaning module references cannot be instantiated.
import builtin.io as io
import SIMD
io.print("hello world")
var x: SIMD.Float32 = 1.2
Changed
* Reverted the feature from 2023-02-13 that allowed unqualified
struct members. Use the Self keyword to conveniently access
struct members with bound parameters instead. This was required
to fix Issue #260.
* Updated the RayTracing notebook: added step 5 to create specular
lighting for more realistic images and step 6 to add a background
image.
[?] Fixed
* Issue #260 - Definitions inside structs no longer shadow
definitions outside of struct definitions.
2023-07-12
[?] New
* Mojo now has support for global variables! This enables var and
let 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 to
async fns can be stored into a Coroutine, from which they can be
resumed, awaited upon, and have their results retrieved upon
completion.
Changed
* simd_bit_width in the TargetInfo module has been renamed to
simdbitwidth to better align with simdwidthof, bitwidthof, etc.
[?] Fixed
* The walrus operator now works in if/while statements without
parentheses, e.g. if x := function():.
* Issue #428 - The FloatLiteral and SIMD types now support
conversion to Int via the to_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 and dtype_simd_width functions in the TargetInfo
module have been renamed to simdwidthof.
* The dtype_ prefix has been dropped from alignof, sizeof, and
bitwidthof. You can now use these functions (e.g. alignof) with
any argument type, including DType.
* The inf, neginf, nan, isinf, isfinite, and isnan functions were
moved from the Numerics module to the Math 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 the shared 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 the help directory, in the Mojo Playground
file browser.
Changed
* The unroll2() and unroll3() functions in the Functional module
have been renamed to overload the unroll() function. These
functions unroll 2D and 3D loops and unroll() 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 includes is_little_endian
() and is_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 Mojo Manual
section on parameters.
* A new cost_of() function has been added to Autotune. 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 constrained_msg() in the Assert module has been renamed to
constrained().
[?] 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 just Tuple, 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 of
TupleLiteral type (notably, it will not work with PythonObject
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 of SI32,
similarly using Float32 instead of F32. 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 an owned 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 the someValue value, and pass that
value instead of someValue, because there may be other uses of
someValue 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 like
takeValueAsOwned(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 on try statements. In addition,
try statements no longer require except clauses, allowing
try-finally blocks. finally clauses contain code that is always
executed from control-flow leaves any of the other clauses of a
try statement by any means.
Changed
* with statement emission changed to use the new finally logic so
that
with 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
* Issue #98: Incorrect error with lifetime tracking in loop.
* Issue #49: Type inference issue (?) in 'ternary assignment'
operation (FloatLiteral vs. 'SIMD[f32, 1]').
* Issue #48: and/or don't work with memory-only types.
* Issue #11: setitem Support for PythonObject.
2023-05-11
[?] New
* NDBuffer and Buffer are now constructable via Pointer and
DTypePointer.
* 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 an inout
keyword to be more similar to the borrowed and owned 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
* Fixed missing overloads for Testing.assertEqual so that they work
on Integer and String values.
* Issue #6: Playground stops evaluating cells when a simple generic
is defined.
* Issue #18: Memory leak in Python interoperability was removed.
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
with foo bound to the self 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 and
let declarations inside functions can shadow declarations from
higher "scopes", where a scope is defined as any new indentation
block. In addition, the for loop iteration variable is now scoped
to the loop body, so it is finally possible to write
for 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,
and var declarations that are never mutated get a warning
suggesting they be converted to a let declaration. Lazy
initialization allows more flexible patterns of initialization
than requiring the initializer be inline, e.g.:
let x : Int
if cond:
x = foo()
else:
x = bar()
use(x)
* Functions defined with def now return object by default,
instead of None. This means you can return values (convertible to
object) inside def functions without specifying a return type.
* The @raises decorator has been removed. Raising fn should be
declared by specifying raises after the function argument list.
The rationale is that raises is part of the type system, instead
of a function modifier.
* The BoolLiteral type has been removed. Mojo now emits True and
False directly as Bool.
* Syntax for function types has been added. You can now write
function types with fn(Int) -> String or async def(&String, *Int)
-> None. No more writing !kgen.signature types by hand!
* Float literals are not emitted as FloatLiteral instead of an
MLIR f64 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 the with 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 !kgen.pointer type)
and returns an owned 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.
__get_address_as_lvalue(somePointer.value) = SomeHeavy(4, 5)
# 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.
__get_address_as_uninit_lvalue(somePointer.value) = SomeHeavy(4, 5)
# 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
the SomeHeavy 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:
1. fn __moveinit__(inout self, owned existing: Self):
Traditional Rust style moving constructors that shuffles data
around while taking ownership of the source binding.
2. 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 take
self by borrow, allow non-copyable callees to be called.
* Implicit conversions are now invoked in raise statements
properly, allowing converting strings to Error type.
* Automatic destructors are turned on for __del__ instead of
__del___.
* Add the builtin FloatLiteral type.
* Add integral floordiv and mod 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 with a
[1:2:3] now emits calls to __setitem__ and __getitem__ with a
slice object.
* Call syntax has been wired up to __call__. You can now f() 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 generic 2D tiling
optimizations.
* Add the slice struct to enable getting/setting spans of
elements via getitem/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 and NoneType aliases were added and auto-imported in
all files.
* The Mojo VS Code extension 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 new Builtin module and is
auto-imported in all code. The type of integer literals has been
changed from the MLIR index type to the Int 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, like foo(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
initialization x = 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:. The T{} 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 like x.foo
() where foo is not a member of x.
* 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[
func: __mlir_type[`!kgen.signature<`, Int, `>() -> !kgen.none`]
]():
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 !
kgen.pointer types (respectively). This is most useful when using
the Pointer[T] library type. The Pointer.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].
__get_address_as_lvalue(somePointer.value) = SomeHeavy(4, 5)
Note that __get_address_as_lvalue assumes that there is already a
value at the specified address, so the assignment above will run
the SomeHeavy 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 like Int.
* You can now write back to attributes of structs that are produced
by a computed lvalue expression. For example a[i].x = .. works
when a[i] is produced with a __getitem__/__setitem__ call. This
is implemented by performing a read of a[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 like Int
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
foo(1, 1.2, True, "hello")
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&:
1. A future borrow checker will make sure there are no mutable
aliases with an immutable borrow.
2. @register_passable values are passed directly in an SSA
register (and thus, usually in a machine register) instead of
using an extra reference wrapper. This is more efficient and
is the 'right default' for @register_passable values like
integers and pointers.
This also paves the way to remove the reference requirement from
__clone__ method arguments, which will allow us to fill in more
support for them.
* Support for variadic pack arguments has been added to Mojo. You
can now write heterogeneous variadic packs like:
fn foo[*Ts: AnyType](args*: Ts): pass
foo[Int, F32, String, Bool](1, 1.5, "hello", True)
* The owned argument convention has been added. This argument
convention indicates that the function takes ownership of the
argument and is responsible for managing its lifetime.
* The borrowed argument convention has been added. This convention
signifies the callee gets an immutable shared reference to a
value in the caller's context.
* Add the getenv function to the OS module to enable getting
environment variables.
* Enable the use of dynamic strides in NDBuffer.
Week of 2023-03-06
* Support added for using capturing async functions as
parameters.
* Returning result parameters has been moved from return
statements to a new param_return statement. This allows returning
result parameters from throwing functions:
@raises
fn foo[() -> out: Int]():
param_return[42]
raise Error()
And returning different parameters along @parameter if branches:
fn bar[in: Bool -> out: Int]():
@parameter
if in:
param_return[1]
else:
param_return[2]
* Mojo now supports omitting returns at the end of functions when
they would not reachable. For instance,
fn foo(cond: Bool) -> Int:
if cond:
return 0
else:
return 1
fn bar() -> Int:
while True:
pass
* String literals now support concatenation, so "hello " "world" is
treated the same as "hello world".
* Empty bodies on functions, structs, and control flow statements
are no longer allowed. Please use pass in them to explicitly mark
that they are empty, just like in Python.
* Structs in Mojo now default to living in memory instead of
being passed around in registers. This is the right default for
generality (large structures, structures whose pointer identity
matters, etc) and is a key technology that enables the borrow
model. For simple types like Int and SIMD, they can be marked as
@register_passable.
Note that memory-only types currently have some limitations: they
cannot be used in generic algorithms that take and return a !
mlirtype argument, and they cannot be used in parameter
expressions. Because of this, a lot of types have to be marked
@register_passable just to work around the limitations. We expect
to enable these use-cases over time.
* Mojo now supports computed lvalues, which means you can finally
assign to subscript expressions instead of having to call
__setitem__ explicitly.
Some details on this: Mojo allows you to define multiple
__setitem__ overloads, but will pick the one that matches your
__getitem__ type if present. It allows you to pass computed
lvalues into inout arguments by introducing a temporary copy of
the value in question.
* Mojo now has much better support for using register-primary
struct types in parameter expressions and as the types of
parameter values. This will allow migration of many standard
library types away from using bare MLIR types like
__mlir_type.index and towards using Int. This moves us towards
getting rid of MLIR types everywhere and makes struct types
first-class citizens in the parameter system.
* Add a sort function.
* Add non-temporal store to enable cache bypass.
February 2023
Week of 2023-02-27
* The @interface, @implements, and @evaluator trio of decorators
have been removed, replaced by the @parameter if and @adaptive
features.
* Parameter inference can now infer the type of variadic lists.
* Memory primary types are now supported in function results. A
result slot is allocated in the caller, and the callee writes the
result of the function into that slow. This is more efficient for
large types that don't fit into registers neatly! And
initializers for memory-primary types now initialize the value
in-place, instead of emitting a copy!
* Support for let decls of memory primary types has been
implemented. These are constant, ready-only values of memory
primary types but which are allocated on the function stack.
* Overload conversion resolution and parameter inference has been
improved:
1. Inference now works with let decls in some scenarios that
weren't working before.
2. Parameter bindings can now infer types into parameter
expressions. This helps resolve higher-order functions in
parameter expressions.
* Optimize floor, ceil, and ldexp on X86 hardware.
* Implement the log math function.
Week of 2023-02-20
* A new @__memory_primary struct decorator has been introduced.
Memory primary types must always have an address. For instance,
they are always stack-allocated when declared in a function and
their values are passed into function calls by address instead of
copy. This is in contract with register primary types that may
not have an address, and which are passed by value in function
calls. Memory-primary fields are not allowed inside
register-primary structs, because struct elements are stored
in-line.
* A new _CompilerBuiltin module was added. This module defines
core types and functions of the language that are referenced by
the parser, and hence, is auto-imported by all other modules. For
example new types for literal values like the boolean True/False
will be included in _CompilerBuiltin.
* A special __adaptive_set property can be accessed on a function
reference marked as @adaptive. The property returns the adaptive
overload set of that function. The return type is a !
kgen.variadic. This feature is useful to implement a generic
evaluate function in the standard library.
* A new built-in literal type BoolLiteral was added in
_CompilerBuiltin. It represents the literal boolean values True
and False. This is the first Mojo literal to be emitted as a
standard library type!
* Add the prefetch intrinsic to enable HW prefetching a cache
line.
* Add the InlinedFixedVector, which is optimized for small
vectors and stores values on both the stack and the heap.
Week of 2023-02-13
* Unqualified lookups of struct members apply contextual
parameters. This means for instance that you can refer to static
methods without binding the struct parameters.
struct Foo[x: Int]:
@staticmethod
bar(): pass
foo(self):
bar() # implicitly binds to Foo[x].bar()
Foo[2].bar() # explicitly bind to another parameter
* A new Self type refers to the enclosing type with all
parameters bound to their current values. This is useful when
working with complex parametric types, e.g.:
struct MyArray[size: Int, element_type: type]:
fn __new__() -> Self:
return Self {...}
which is a lot nicer than having to say MyArray[size,
element_type] over and over again.
* Mojo now supports an @adaptive decorator. This decorator will
supersede interfaces, and it represents an overloaded function
that is allowed to resolve to multiple valid candidates. In that
case, the call is emitted as a fork, resulting in multiple
function candidates to search over.
@adaptive
fn sort(arr: ArraySlice[Int]):
bubble_sort(arr)
@adaptive
fn sort(arr: ArraySlice[Int]):
merge_sort(arr)
fn concat_and_sort(lhs: ArraySlice[Int], rhs: ArraySlice[Int]):
let arr = lhs + rhs
sort(arr) # this forks compilation, creating two instances
# of the surrounding function
* Mojo now requires that types implement the __clone__ special
member in order to copy them. This allows the safe definition of
non-copyable types like Atomic. Note that Mojo still doesn't
implement destructors, and (due to the absence of non-mutable
references) it doesn't actually invoke the __clone__ member when
copying a let value. As such, this forces to you as a Mojo user
to write maximal boilerplate without getting much value out of
it.
In the future, we will reduce the boilerplate with decorators,
and we will actually start using it. This will take some time to
build out though.
* A special __mlir_region statement was added to provide stronger
invariants around defining MLIR operation regions in Mojo. It
similar syntax to function declarations, except it there are no
results and no input conventions.
* Implement the log math function.
* Improve the DType struct to enable compile-time equality
checks.
* Add the Complex struct class.
Week of 2023-02-06
* The if statement now supports a @parameter decorator, which
requires its condition to be a parameter expression, but which
only emits the 'True' side of the condition to the binary,
providing a "static if" functionality. This should eliminate many
uses of @interface that are just used to provide different
constraint on the implementations.
* fn main(): is now automatically exported and directly runnable
by the command-line mojo tool. This is a stop-gap solution to
enable script-like use cases until we have more of the language
built out.
* The @nodebug_inline feature has been removed, please use
@alwaysinline("nodebug") for methods that must be inlined and
that we don't want to step into.
* Python chained comparisons, ex. a < b < c, are now supported in
Mojo.
* Functions can now be defined with default argument values, such
as def f(x: Int, y: Int = 5):. The default argument value is used
when callers do not provide a value for that argument: f(3), for
example, uses the default argument value of y = 5.
* Unused coroutine results are now nicely diagnosed as "missing
await" warnings.
* Introduce a vectorized reduction operations to the SIMD type.
January 2023
Week of 2023-01-30
* A basic Mojo language server has been added to the VS Code
extension, which parses your code as you write it, and provides
warnings, errors, and fix-it suggestions!
* The Mojo standard library is now implicitly imported by
default.
* The coroutine lowering support was reworked and a new Coroutine
[T] type was implemented. Now, the result of a call to an async
function MUST be wrapped in a Coroutine[T], or else memory will
leak. In the future, when Mojo supports destructors and library
types as literal types, the results of async function calls will
automatically wrapped in a Coroutine[T]. But today, it must be
done manually. This type implements all the expected hooks, such
as __await__, and get() to retrieve the result. Typical usage:
async fn add_three(a: Int, b: Int, c: Int) -> Int:
return a + b + c
async fn call_it():
let task: Coroutine[Int] = add_three(1, 2, 3)
print(await task)
* [?] We now diagnose unused expression values at statement context
in fn declarations (but not in defs). This catches bugs with
unused values, e.g. when you forget the parens to call a
function.
* An @always_inline("nodebug") function decorator can be used on
functions that need to be force inlined, but when they should not
have debug info in the result. This should be used on methods
like Int.__add__ which should be treated as builtin.
* The @export decorator now supports an explicit symbol name to
export to, for example:
@export("baz") # exported as 'baz'
fn some_mojo_fn_name():
* Subscript syntax is now wired up to the __getitem__ dunder
method.
This allows type authors to implement the __getitem__ method to
enable values to be subscripted. This is an extended version of
the Python semantics (given we support overloading) that allows
you to define N indices instead of a single version that takes a
tuple (also convenient because we don't have tuples yet).
Note that this has a very, very important limitation: subscripts
are NOT wired up to __setitem__ yet. This means that you can read
values with .. = v[i] but you cannot store to them with v[i] =
... For this, please continue to call __setitem__ directly.
* Function calls support parameter inference.
For calls to functions that have an insufficient number of
parameters specified at the callsite, we can now infer them from
the argument list. We do this by matching up the parallel type
structure to infer what the parameters must be.
Note that this works left to right in the parameter list,
applying explicitly specified parameters before trying to infer
new ones. This is similar to how C++ does things, which means
that you may want to reorder the list of parameters with this in
mind. For example, a dyn_cast-like function will be more elegant
when implemented as:
fn dyn_cast[DstType: type, SrcType: type](src: SrcType) ->
DstType:
Than with the SrcType/DstType parameters flipped around.
* Add the growable Dynamic vector struct.
Week of 2023-01-23
* Inplace operations like +=/__iadd__ may now take self by-val if
they want to, instead of requiring it to be by-ref.
* [?] Inplace operations are no longer allowed to return a non-None
value. The corresponding syntax is a statement, not an
expression.
* A new TaskGroup type was added to the standard library. This type
can be used to schedule multiple tasks on a multi-threaded
workqueue to be executed in parallel. An async function can await
all the tasks at once with the taskgroup.
* We now support for loops! A type that defines an __iter__
method that returns a type that defines __next__ and __len__
methods is eligible to be used in the statement for el in X().
Control flow exits the loop when the length is zero.
This means things like this now work:
for item in range(start, end, step):
print(item)
* Result parameters now have names. This is useful for referring to
result parameters in the return types of a function:
fn return_simd[() -> nelts: Int]() -> SIMD[f32, nelts]:
* We now support homogeneous variadics in value argument lists,
using the standard Python fn thing(*args: Int): syntax! Variadics
also have support in parameter lists:
fn variadic_params_and_args[*a: Int](*b: Int):
print(a[0])
print(b[1])
* Add the range struct to enable for ... range(...) loops.
* Introduce the unroll generator to allow one to unroll loops via
a library function.
Week of 2023-01-16
* Struct field references are now supported in parameter context,
so you can use someInt.value to get the underlying MLIR thing out
of it. This should allow using first-class types in parameters
more widely.
* We now support "pretty" initialization syntax for structs,
e.g.:
struct Int:
var value: __mlir_type.index
fn __new__(value: __mlir_type.index) -> Int:
return Int {value: value}
This eliminates the need to directly use the MLIR
lit.struct.create op in struct initializers. This syntax may
change in the future when ownership comes in, because we will be
able to support the standard __init__ model then.
* It is now possible to attach regions to __mlir_op operations.
This is done with a hack that allows an optional _region
attribute that lists references to the region bodies (max 1
region right now due to lack of list [] literal).
* Nested functions now parse, e.g.:
fn foo():
fn bar():
pass
bar()
* Python-style async functions should now work and the await
expression prefix is now supported. This provides the joy of
async/await syntactic sugar when working with asynchronous
functions. This is still somewhat dangerous to use because we
don't have proper memory ownership support yet.
* String literals are now supported.
* Return processing is now handled by a dataflow pass inside the
compiler, so it is possible to return early out of if statements.
* The parser now supports generating 'fixit' hints on diagnostics,
and uses them when a dictionary literal uses a colon instead of
equal, e.g.:
x.mojo:8:48: error: expected ':' in subscript slice, not '='
return __mlir_op.`lit.struct.create`[value = 42]()
^
:
* Add reduction methods which operate on buffers.
* Add more math functions like sigmoid, sqrt, rsqrt, etc.
* Add partial load / store which enable loads and stores that are
predicated on a condition.
Week of 2023-01-09
* The / and * markers in function signatures are now parsed and
their invariants are checked. We do not yet support keyword
arguments yet though, so they aren't very useful.
* Functions now support a new @nodebug_inline decorator.
(Historical note: this was later replaced with @alwaysinline
("nodebug")).
Many of the things at the bottom level of the Mojo stack are
trivial zero-abstraction wrappers around MLIR things, for
example, the + operator on Int or the __bool__ method on Bool
itself. These operators need to be force inlined even at -O0, but
they have some additional things that we need to wrestle with:
1. In no case would a user actually want to step into the
__bool__ method on Bool or the + method on Int. This would be
terrible debugger QoI for unless you're debugging Int itself.
We need something like __always_inline__, __nodebug__
attributes that clang uses in headers like xmmintrin.h.
2. Similarly, these "operators" should be treated by users as
primitives: they don't want to know about MLIR or internal
implementation details of Int.
3. These trivial zero abstraction things should be eliminated
early in the compiler pipeline so they don't slow down the
compiler, bloating out the call graph with trivial leaves.
Such thing slows down the elaborator, interferes with basic
MLIR things like fold(), bloats out the IR, or bloats out
generated debug info.
4. In a parameter context, we want some of these things to get
inlined so they can be simplified by the attribute logic and
play more nicely with canonical types. This is just a nice to
have thing those of us who have to stare at generated IR.
The solution to this is a new @nodebug_inline decorator. This
decorator causes the parser to force-inline the callee instead of
generating a call to it. While doing so, it gives the operations
the location of the call itself (that's the "nodebug" part) and
strips out let decls that were part of the internal
implementation details.
This is a super-power-user-feature intended for those building
the standard library itself, so it is intentionally limited in
power and scope: It can only be used on small functions, it
doesn't support regions, by-ref, throws, async, etc.
* Separately, we now support an @alwaysInline decorator on
functions. This is a general decorator that works on any
function, and indicates that the function must be inlined. Unlike
@nodebug_inline, this kind of inlining is performed later in the
compilation pipeline.
* The __include hack has been removed now that we have proper
import support.
* __mlir_op can now get address of l-value:
You can use magic (((x))) syntax in __mlir_op that forces the x
expression to be an lvalue, and yields its address. This provides
an escape hatch (isolated off in __mlir_op land) that allows
unsafe access to lvalue addresses.
* We now support __rlshift__ and __rtruediv__.
* The parser now resolves scoped alias references. This allows us
to support things like SomeType.someAlias, forward substituting
the value. This unblocks use of aliases in types like DType. We'd
like to eventually preserve the reference in the AST, but this
unblocks library development.
* Add a now function and Benchmark struct to enable timing and
benchmarking.
* Move more of the computation in NDBuffer from runtime to
compile time if possible (e.g. when the dimensions are known at
compile time).
Week of 2023-01-02
* Added the print function which works on Integers and SIMD
values.
* The frontend now has a new diagnostic subsystem used by the kgen
tool (but not by kgen-translate for tests) that supports source
ranges on diagnostics. Before we'd emit an error like:
x.mojo:13:3: error: invalid call to 'callee': in argument #0, value of type '$F32::F32' cannot be converted to expected type '$int::Int'
callee(1.0+F32(2.0))
^
x.lit:4:1: note: function declared here
fn callee(a: Int):
^
now we produce:
x.mojo:13:3: error: invalid call to 'callee': in argument #0, value of type '$F32::F32' cannot be converted to expected type '$int::Int'
callee(1.0+F32(2.0))
^ ~~~~~~~~~~~~
x.lit:4:1: note: function declared here
fn callee(a: Int):
^
* Parameter results are now supported in a proper way. They are
now forward declared with an alias declaration and then bound in
a call with an arrow, e.g.:
alias a : __mlir_type.index
alias b : __mlir_type.index
idx_result_params[xyz*2 -> a, b]()
* Various minor issues with implicit conversions are fixed. For
instances, implicit conversions are now supported in parameter
binding contexts and alias declarations with explicit types.
* Doc strings are allowed on functions and structs, but they are
currently discarded by the parser.
* Add a print method!!!
* Demonstrate a naive matmul in Mojo.
* Initial work on functions that depend on types (e.g. FPUtils,
nan, inf, etc.)
* Allow one to query hardware properties such as simd_width, os,
etc. via TargetInfo at compile time.
December 2022
Week of 2022-12-26
* You can now call functions in a parameter context! Calling a
function in a parameter context will evaluate the function at
compile time. The result can then be used as parameter values.
For example,
fn fma(x: Int, y: Int, z: Int) -> Int:
return a + b * c
fn parameter_call():
alias nelts = fma(32, 2, 16)
var x: SIMD[f32, nelts]
* You can now disable printing of types in an __mlir_attr
substitution by using unary + expression.
* let declarations are now supported in functions. let
declarations are local run-time constant values, which are always
rvalues. They complement 'var' decls (which are mutable lvalues)
and are the normal thing to use in most cases. They also generate
less IR and are always in SSA form when initialized.
We will want to extend this to support 'let' decls in structs at
some point and support lazy initialized 'let' declarations (using
dataflow analysis) but that isn't supported yet.
* Add the NDBuffer struct.
* Happy new year.
Week of 2022-12-19
* Start of the Standard library:
1. Added Integer and SIMD structs to bootstrap the standard
library.
2. Added very basic buffer data structure.
* We have basic support for parsing parameter results in function
calls! Result parameters are an important Mojo metaprogramming
feature. They allow functions to return compile-time constants.
fn get_preferred_simdwidthof[() -> nelts: Int]():
return[2]
fn vectorized_function():
get_preferred_simdwidthof[() -> nelts]()
var x: SIMD[f32, nelts]
* Types can now be used as parameters of !kgen.mlirtype in many
more cases.
* MLIR operations with zero results don't need to specify _type: []
anymore.
* We support parsing triple quoted strings, for writing docstrings
for your functions and structs!
* A new __mlir_type[a,b,c] syntax is available for substituting
into MLIR types and attributes is available, and the old
placeholder approach is removed. This approach has a few
advantages beyond what placeholders do:
1. It's simpler.
2. It doesn't form the intermediate result with placeholders,
which gets rejected by MLIR's semantic analysis, e.g. the
complex case couldn't be expressed before.
3. It provides a simple way to break long attrs/types across
multiple lines.
* We now support an @evaluator decorator on functions for KGEN
evaluators. This enables specifying user-defined interface
evaluators when performing search during compilation.
* import syntax is now supported!
This handles packaging imported modules into file ops, enables
effective isolation from the other decls. "import" into the
desired context is just aliasing decls, with the proper symbols
references handle automatically during IR generation. As a
starting point, this doesn't handle any notion of packages (as
those haven't been sketched out enough).
* Reversed binary operators (like __radd__) are now looked up and
used if the forward version (like __add__) doesn't work for some
reason.
* Implicit conversions are now generally available, e.g. in
assign statements, variable initializers etc. There are probably
a few more places they should work, but we can start eliminating
all the extraneous explicit casts from literals now.
* Happy Holidays
Week of 2022-12-12
* Function overloading now works. Call resolution filters
candidate list according to the actual parameter and value
argument specified at the site of the call, diagnosing an error
if none of the candidates are viable or if multiple are viable
and ambiguous. We also consider implicit conversions in overload
look:
fn foo(x: Int): pass
fn foo(x: F64): pass
foo(Int(1)) # resolves to the first overload
foo(1.0) # resolves to the second overload
foo(1) # error: both candidates viable with 1 implicit conversion!
* The short circuiting binary and and or expressions are now
supported.
* Unary operator processing is a lot more robust, now handling the
not expression and ~x on Bool.
* The compiler now generates debug information for use with GDB/
LLDB that describes variables and functions.
* The first version of the Mojo Visual Studio Code extension has
been released! It supports syntax highlighting for Mojo files.
* The first version of the Bool type has landed in the new Mojo
standard library!
* Implicit conversions are now supported in return statements.
Week of 2022-12-05
* "Discard" patterns are now supported, e.g. _ = foo()
* We now support implicit conversions in function call arguments,
e.g. converting an index value to Int automatically. This
eliminates a bunch of casts, e.g. the need to say F32(1.0)
everywhere.
This is limited for a few reasons that will be improved later:
1. We don't support overloading, so lots of types aren't
convertible from all the things they should be, e.g. you
can't pass "1" to something that expects F32, because F32
can't be created from index.
2. This doesn't "check to see if we can invoke __new__" it force
applies it on a mismatch, which leads to poor QoI.
3. This doesn't fix things that need radd.
November 2022
Week of 2022-11-28
* We support the True and False keywords as expressions.
* A new alias declaration is supported which allows defining
local parameter values. This will eventually subsume type aliases
and other things as it gets built out.
* We now have end-to-end execution of Mojo files using the kgen
tool! Functions exported with @export can be executed.
* We have try-except-else and raise statements and implicit error
propagation! The error semantics are that def can raise by
default, but fn must explicitly declare raising with a @raises
decorator. Stub out basic Error type.
* The & sigil for by-ref arguments is now specified after the
identifier. Postfix works better for ref and move operators on
the expression side because it chains an mentally associates
correctly: thing.method().result^. We don't do that yet, but
align param decl syntax to it so that things won't be odd looking
when we do. In practice this looks like:
def mutate_argument(a&: index):
a = 25
Week of 2022-11-21
* The magic index type is gone. Long live __mlir_type.index.
* Implement parameter substitution into parametric __mlir_type
decls. This allows us to define parametric opaque MLIR types with
exposed parameters using a new "placeholder" attribute. This
allows us to expose the power of the KGEN type parametric system
directly into Mojo.
* Fully-parametric custom types can now be defined and work in
Mojo, bringing together a lot of the recent work. We can write
the SIMD type directly as a wrapper around the KGEN type, for
example:
struct SIMD[dt: __mlir_type.`!kgen.dtype`, nelts: __mlir_type.index]:
var value:
__mlir_type.`!pop.simd<#lit,
#lit>`[nelts, dt]
fn __add__(self, rhs: SIMD[dt, nelts]) -> SIMD[dt, nelts]:
return __mlir_op.`pop.add`(self.value, rhs.value)
Week of 2022-11-14
* Implement a magic __mlir_type declaration that can be used to
access any MLIR type. E.g. __mlir_type.f64.
* Add an fn declaration. These are like def declarations, but are
more strict in a few ways: they require type annotations on
arguments, don't allow implicit variable declarations in their
body, and make their arguments rvalues instead of lvalues.
* Implemented Swift-style backtick identifiers, which are useful
for code migration where names may collide with new keywords.
* A new __include directive has been added that performs
source-level textual includes. This is temporary until we have an
import model.
* Implement IR generation for arithmetic operators like + and * in
terms of the __add__ and __mul__ methods.
* Added support for break and continue statements, as well as
early returns inside loops and conditionals!
* Implemented augmented assignment operators, like += and @=.
* Mojo now has access to generating any MLIR operations (without
regions) with a new __mlir_op magic declaration. We can start to
build out the language's builtin types with this:
struct Int:
var value: __mlir_type.index
fn __add__(self, rhs: Int) -> Int:
return __mlir_op.`index.add`(self.value, rhs.value)
Attributes can be attached to the declaration with subscript []
syntax, and an explicit result type can be specified with a
special _type attribute if it cannot be inferred. Attributes can
be accessed via the __mlir_attr magic decl:
__mlir_op.`index.cmp`[
_type: __mlir_type.i1,
pred: __mlir_attr.`#index`
](lhs, rhs)
* Improved diagnostics emissions with ranges! Now errors highlight
the whole section of code and not just the first character.
Week of 2022-11-07
* Implemented the @interface and @implements decorators, which
provide access to KGEN generator interfaces. A function marked as
an @interface has no body, but it can be implemented by multiple
other functions.
@interface
def add(lhs: index, rhs: index):
@implements(add)
def normal_add(lhs: index, rhs: index) -> index:
return lhs + rhs
@implements(add)
def slow_add(lhs: index, rhs: index) -> index:
wait(1000)
return normal_add(lhs, rhs)
* Support for static struct methods and initializer syntax has
been added. Initializing a struct with Foo() calls an implicitly
static __new__ method. This method should be used instead of
__init__ inside structs.
struct Foo:
var value: index
def __new__() -> Foo:
var result: Foo
result.value = Foo.return_a_number() # static method!
return result
@staticmethod
def return_a_number() -> index:
return 42
* Full by-ref argument support. It's now possible to define
in-place operators like __iadd__ and functions like swap(x, y)
correctly.
* Implemented support for field extract from rvalues, like
x.value where x is not an lvalue (var declaration or by-ref
function argument).
October 2022
Week of 2022-10-31
* Revised return handling so that a return statement with no
expression is syntax sugar for return None. This enables early
exits in functions that implicitly return None to be cleaner:
def just_return():
return
* Added support for parsing more expressions: if-else, bitwise
operators, shift operators, comparisons, floor division,
remainder, and matmul.
* The type of the self argument can now be omitted on member
methods.
Week of 2022-10-24
* Added parser support for right-associativity and unary ops, like
the power operator a ** b ** c and negation operator -a.
* Add support for &expr in Mojo, which allows denoting a by-ref
argument in functions. This is required because the self type of
a struct method is implicitly a pointer.
* Implemented support for parametric function declarations, such
as:
struct SIMD[dt: DType, width: index]:
fn struct_method(self: &SIMD[dt, width]):
pass
def fancy_add[dt: DType, width: index](
lhs: SIMD[dt, width], rhs: SIMD[dt, width]) -> index:
return width
Week of 2022-10-17
* Added explicit variable declarations with var, for declaring
variables both inside functions and structs, with support for
type references. Added index as a temporary built-in type.
def foo(lhs: index, rhs: index) -> index:
var result: index = lhs + rhs
return result
* Implemented support for parsing struct declarations and
references to type declarations in functions! In def, the type
can be omitted to signal an object type.
struct Foo:
var member: index
def bar(x: Foo, obj) -> index:
return x.member
* Implemented parser support for if statements and while loops!
def if_stmt(c: index, a: index, b: index) -> index:
var result: index = 0
if c:
result = a
else:
result = b
return result
def while_stmt(init: index):
while init > 1:
init = init - 1
* Significantly improved error emission and handling, allowing the
parser to emit multiple errors while parsing a file.
Week of 2022-10-10
* Added support for parsing integer, float, and string literals.
* Implemented parser support for function input parameters and
results. You can now write parametric functions like,
def foo[param: Int](arg: Int) -> Int:
result = param + arg
return result
Week of 2022-10-03
* Added some basic parser scaffolding and initial parser
productions, including trivial expressions and assignment parser
productions.
* Implemented basic scope handling and function IR generation, with
support for forward declarations. Simple functions like,
def foo(x: Int):
Now parse! But all argument types are hard-coded to the MLIR
index type.
* Added IR emission for simple arithmetic expressions on builtin
types, like x + y.
September 2022
Week of 2022-09-26
* Mojo's first patch to add a lexer was Sep 27, 2022.
* Settled on [] for Mojo generics instead of <>. Square brackets
are consistent with Python generics and don't have the less than
ambiguity other languages have.
Roadmap & sharp edges
FAQ
* (c) 2023 Modular Inc
* cookie
* Modular.com
* Terms
* Privacy
* Get started
*
*