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 aCoroutine[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 aCoroutine[T]. But today, it must be done manually. This type implements all the expected hooks, such as__await__, andget()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
fndeclarations (but not indefs). 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 likeInt.__add__which should be treated as builtin. -
π’ The
@exportdecorator 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 withv[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/DstTypeparameters flipped around. -
π Add the growable Dynamic vector struct.
Week of 2023-01-23β
-
Inplace operations like
+=/__iadd__may now takeselfby-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
TaskGrouptype 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 canawaitall 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 statementfor 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.valueto 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.createop 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_opoperations. This is done with a hack that allows an optional_regionattribute 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
asyncfunctions should now work and theawaitexpression 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_inlinedecorator. (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:-
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. -
Similarly, these "operators" should be treated by users as primitives: they don't want to know about MLIR or internal implementation details of Int.
-
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.
-
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_inlinedecorator. 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
@alwaysInlinedecorator 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
__includehack has been removed now that we have proper import support. -
__mlir_opcan now get address of l-value:You can use magic
(((x)))syntax in __mlir_op that forces thexexpression to be an lvalue, and yields its address. This provides an escape hatch (isolated off in__mlir_opland) 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 likeDType. We'd like to eventually preserve the reference in the AST, but this unblocks library development. -
π Add a
nowfunction andBenchmarkstruct to enable timing and benchmarking. -
π Move more of the computation in
NDBufferfrom runtime to compile time if possible (e.g. when the dimensions are known at compile time).
Week of 2023-01-02β
-
π Added the
printfunction which works on Integers and SIMD values. -
The frontend now has a new diagnostic subsystem used by the
kgentool (but not bykgen-translatefor 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
aliasdeclarations with explicit types. -
Doc strings are allowed on functions and structs, but they are currently discarded by the parser.
-
π Add a
printmethod!!! -
π 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.
Was this page helpful?
Thank you! We'll create more content like this.
Thank you for helping us improve!