v0.25.7 (2025-11-20)
✨ Highlights
-
A new, improved
UnsafePointertype has been added, and the old version renamed toLegacyUnsafePointer. The new version fixes several issues with the oldLegacyUnsafePointer.LegacyUnsafePointerhad constructors that allowed unsafe implicit mutability and origin casts, making it easy to make unsafe changes by accident. The newUnsafePointereliminates these.- The new
UnsafePointernow has an inferred mutability parameter, simplifying the API. UnsafePointerdoes not have a default value for its origin parameter, so it must be explicitly specified or unbound.LegacyUnsafePointerdefaults toMutAnyOrigin, which results in the lifetimes of many values being extended needlessly.
For more information, see Pointer and memory changes.
-
Many enhancements to compile-time errors, including suppressing extraneous messages and preserving more human-readable display for many aliases and parameters. For details, see Tooling changes.
-
Added a new document on GPU block and warp operations and synchronization.
Language enhancements
-
Mojo now supports the
comptimekeyword as a synonym foralias. Thecomptimekeyword can be used interchangeably withaliasfor compile-time declarations. Both keywords are fully supported and produce identical behavior. For example:comptime x = 5 # New preferred syntax
alias y = 10 # Still fully supported
comptime MyType[T: AnyType] = T # Works with parametric declarationsNote: Future updates will migrate error messages and internal terminology to use "comptime". The
aliaskeyword will remain supported for backward compatibility for now. -
Mojo now supports unpacking an alias/comptime tuple with a single statement when it is not inside a struct or trait. For example:
comptime i, f = (1, 3.0)
comptime q, v = divmod(4, 5) -
Issue #3925: Mojo now allows methods to be overloaded based on "owned" vs "by-ref" argument conventions, selecting the owned overload when given an owned value, and selecting the by-ref version otherwise. This allows somewhat more efficient algorithms, e.g. consuming vs borrowing iterators:
struct MyCollection:
fn __iter__(var self) -> Self.ConsumingIterator: ...
fn __iter__(self) -> Self.BorrowingIterator: ... -
Collection literals now have a default type. For example, you can now bind
[1,2,3]toTin a call to a function defined asfn zip[T: Iterable](impl:T)because it will default to the standard library'sListtype. -
Mojo now has a
__functions_in_module()experimental intrinsic that allows reflection over the functions declared in the module where it is called. For example:fn foo(): pass
def bar(x: Int): pass
def main():
alias funcs = __functions_in_module()
# equivalent to:
alias same_funcs = Tuple(foo, bar)The intrinsic is currently limited for use from within the
main()function. For an example of using__functions_in_module()in a test suite, see Running tests with TestSuite -
The
@implicitdecorator now accepts an optionaldeprecatedkeyword argument. This can be used to phase out implicit conversions instead of just removing the decorator (which can result in another, unintended implicit conversion path). For example, the compiler now warns about the following:struct MyStuff:
@implicit(deprecated=True)
fn __init__(out self, value: Int):
pass
fn deprecated_implicit_conversion():
# warning: deprecated implicit conversion from 'IntLiteral[1]' to 'MyStuff'
_: MyStuff = 1
_ = MyStuff(1) # this is okay, because the conversion is already explicit. -
The
@deprecateddecorator can now take a target symbol with theusekeyword argument. This is mutually exclusive with the existing positional string argument. A deprecation warning will be automatically generated.@deprecated(use=new)
fn old():
pass
fn new():
pass
fn main():
old() # 'old' is deprecated, use 'new' instead -
In struct instances that declare a parametric
__call__()method, but not one of the subscript methods (__getitem__(),__setitem__(), or__getattr__()), the__call__()method can now be invoked with parameters:struct Callable:
fn __init__(out self):
pass
fn __call__[x: Int](self, y: Int) -> Int:
return x + y
fn main():
var c = Callable()
print(c[1](2)) # 3Previously you would have needed to explicitly look up
__call__():print(c.__call__[1](2)) -
Added
DType.float4_e2m1fnas the 4bit floate2m1format. ThisFloat4_e2m1type is defined by the Open Compute MX Specification. -
deinitmethods may now transfer all ofselfto anotherdeinitmethod. -
Mojo now uses system allocators in programs built with
mojo build --sanitize address. This means ASan can see Mojo heap allocations and should now be able to detect many more heap memory errors.
Language changes
-
The
__type_of()magic function has been renamed totype_of(). Using the old spelling will yield an error. Similarly,__origin_of()has been renamed toorigin_of(). -
An expression like
(Int, Float64)is no longer syntax sugar for a tuple types likeTuple[Int, Float64].
Library changes
Pointer and memory changes
-
UnsafePointerhas been renamed toLegacyUnsafePointerand a newUnsafePointerhas taken its place. Similarly,OpaquePointerhas been renamed toLegacyOpaquePointerand a newOpaquePointerhas taken its place. The primary difference is the ordering of parameters, which now looks like this:struct UnsafePointer[
mut: Bool, //, # Inferred mutability
type: AnyType,
origin: Origin[mut], # Non-defaulted origin
*,
address_space: AddressSpace = AddressSpace.GENERIC,
]
alias OpaquePointer[
mut: Bool, //, # Inferred mutability
origin: Origin[mut], # Non-defaulted origin
*,
address_space: AddressSpace = AddressSpace.GENERIC,
] = UnsafePointer[NoneType, origin, address_space=address_space]Its implicit constructors now no longer allow for unsafe casting between pointers with different mutability and origin values. Code will need to update to the new
UnsafePointer, however, in the interim, users can find-and-replace their current usages ofUnsafePointerand rename them toLegacyUnsafePointer. Another option is users can add the following import statement to the beginning of any files relying on the old pointer type:from memory import LegacyUnsafePointer as UnsafePointer, **_]
# and/or if you use OpaquePointer
from memory import LegacyOpaquePointer as OpaquePointerUsers can also use the
as_legacy_pointer()andas_unsafe_pointer()conversion methods to convert between the two pointer types during this migration period.Note:
LegacyUnsafePointerandLegacyOpaquePointerwill eventually be deprecated and removed in a future version of Mojo.There are a few new helpful type aliases for
UnsafePointer:MutUnsafePointerImmutUnsafePointerMutOpaquePointerImmutOpaquePointer
Lastly,
alloc()has been moved from a static method onUnsafePointerto a free standingalloc()function. Therefore, code that was written as:var ptr = UnsafePointer[Int].alloc(3)Must be changed to:
var ptr = alloc[Int](3)For more details on migrating to the new
UnsafePointer, see the migration guide provided in the -
Added a
swap_pointees()function toUnsafePointeras an alternative toswap()when the pointers may potentially alias each other. -
origin_cast()forLayoutTensor,NDBufferandUnsafePointerhas been deprecated and removed.LayoutTensorandNDBuffernow support a saferas_any_origin()origin casting method.UnsafePointerhas the same safe alternative and in addition, it has a safeas_immutable()casting function and explicitly unsafeunsafe_mut_cast()andunsafe_origin_cast()casting methods. -
The
emptyorigin has been renamed toexternal. This origin represents a value that's not tracked by the Mojo lifetime checker. Newly-allocated memory fromalloc()is returned with the originMutOrigin.external. -
Renamed
MutableOrigintoMutOriginandImmutableOrigintoImmutOrigin. -
Renamed
MutableAnyOrigintoMutAnyOriginandImmutableAnyOrigintoImmutAnyOrigin. -
memcpy()andparallel_memcpy()without keyword arguments are deprecated.
Collections and iterators
-
SpanandStringSliceconstructors now acceptIntfor length parameters instead ofUInt. This change makes these types more ergonomic to use with integer literals and otherInt-based APIs. -
Added
Span.binary_search_by()which allows binary searching with a custom comparator function. -
Added
unsafe_get(),unsafe_swap_elements()andunsafe_subspan()to theSpanstruct. -
Optionalnow conforms toIterableandIteratoracting as a collection of size 1 or 0. -
New
ContiguousSliceandStridedSlicetypes were added to thebuiltin_slicemodule to support specialization for slicing without strides. -
Listslicing without a stride now returns aSpan, instead of aListand no longer allocates memory. -
Several standard library APIs have been updated to use
Intinstead ofUIntfor improved ergonomics, eliminating the need for explicit casts when usingIntvalues (the default type for integer literals and loop indices):BitSet[size: Int]- Changed parameter fromUInttoIntBitSet.set(idx: Int),BitSet.clear(idx: Int),BitSet.toggle(idx: Int),BitSet.test(idx: Int)- Changed fromUInttoIntString(unsafe_uninit_length: Int)- Changed fromUInttoIntString.capacity() -> Int- Changed return type fromUInttoIntString.reserve(new_capacity: Int)- Changed fromUInttoIntList(length: Int, fill: T)- Changed fromUInttoIntCodepoint.unsafe_write_utf8() -> Int- Changed return type fromUInttoIntCodepoint.utf8_byte_length() -> Int- Changed return type fromUInttoInt
-
Added
repeat()function to theitertoolsmodule that creates an iterator which repeats an element a specified number of times. Unlike Python'sitertools.repeat(), infinite iteration is not currently supported - thetimesparameter is required. Example usage:from itertools import repeat
for val in repeat(42, times=3):
print(val) # Prints: 42, 42, 42 -
Tuples now support comparison operations if the element types are also comparable. For example, one can now write
(1, "a") == (1, "a")or(1, "a") < (1, "b").
String and text
-
Codepointnow conforms toComparableadding__le__(),__lt__(),__ge__(), and__gt__()implementations.
Math and numeric types
-
The deprecated
DType.indexis now removed in favor of theDType.int. -
Added
DType.float4_e2m1fnas the 4bit floate2m1format. ThisFloat4_e2m1type is defined by the Open Compute MX Specification. -
math.isqrt()has been renamed torsqrt()since it performs reciprocal square root functionality. -
The
mathpackage now has a Mojo native implementation ofacos(),asin(),cbrt(), anderfc(). -
SIMDnow implements theDivModabletrait. -
Implicit conversions between
IntandUIntare now deprecated.The
@implicitdecorator onInt.__init__(UInt)andUInt.__init__(Int)will be removed in a future version of Mojo. Code that currently performs implicit conversions betweenIntandUIntwill issue a deprecation warning, and should be updated to explicitly readInt(uint_val)orUInt(int_val)respectively. -
The
ImplicitlyIntabletrait has been removed. Types implementing this trait could be implicitly converted toInt.Boolwas the only Mojo standard library type to implementImplicitlyIntable. Conversions fromBooltoIntcan now be performed explicitly, usingInt(bool-val)(via the remainingIntabletrait, which only supports explicit conversions).
GPU support
-
Added support for NVIDIA GeForce GTX 970.
-
gpu.sync.syncwarp()now supports Apple GPUs viaSIMDGROUPbarrier implementation. On Apple GPUs, this provides execution synchronization for all active lanes using aSIMDGROUPbarrier with no memory fence. For threadgroup memory ordering, usebarrier()instead. Note that lane masks are not supported on Apple GPUs, so the mask argument is ignored. -
gpu.primitives.warpnow supports Apple GPUs with native SIMD-group shuffle operations. This enablesshuffle_idx(),shuffle_up(),shuffle_down(), andshuffle_xor()on Apple hardware by mapping Metalsimd_shuffle*intrinsics to AIR (llvm.air.simd_shuffle[_up/_down/_xor]) instructions, achieving feature parity with NVIDIA and AMD backends. -
gpu.intrinsics.store_release()andgpu.intrinsics.load_acquire()now support Apple silicon GPUs, expanding support for proper memory synchronization on these devices. -
The
gpupackage has been reorganized into logical subdirectories for better code organization:gpu/primitives/- Low-level GPU execution primitives (warp, block, cluster, id, grid_controls)gpu/memory/- Memory operations (async_copy, TMA, address spaces)gpu/sync/- Synchronization primitives (barriers, semaphores)gpu/compute/- Compute operations (mma, tensor cores, tcgen05)
Backward compatibility: All existing imports continue to work unchanged. Deprecated import paths (
gpu.id,gpu.mma,gpu.cluster,gpu.grid_controls,gpu.warp,gpu.semaphore,gpu.mma_sm100,gpu.tcgen05,gpu.mma_util,gpu.mma_operand_descriptor, andgpu.tensor_ops) are preserved as re-export wrappers with deprecation notices. Users can migrate to the new recommended import patterns at their own pace:# Old (deprecated but still works):
from gpu.id import block_idx, thread_idx
from gpu.mma import mma
from gpu.mma_sm100 import UMMAKind
from gpu.tcgen05 import tcgen05_alloc
from gpu.semaphore import Semaphore
from gpu.cluster import cluster_sync
# New (recommended):
from gpu import block_idx, thread_idx, cluster_sync
from gpu.compute.mma import mma
from gpu.compute.mma_sm100 import UMMAKind
from gpu.compute.tcgen05 import tcgen05_alloc
from gpu.sync.semaphore import Semaphore -
The
_GPUAddressSpacetype has been removed and consolidated intoAddressSpace. GPU-specific address space constants (GLOBAL,SHARED,CONSTANT,LOCAL,SHARED_CLUSTER) are now available as aliases on the unifiedAddressSpacetype. TheGPUAddressSpacealias has also been removed in favor of usingAddressSpacedirectly. SinceAddressSpaceis part of the prelude, it no longer needs to be explicitly imported in most code. -
TMA (Tensor Memory Accelerator) types have been moved to a dedicated module. The types
TMADescriptor,TensorMapSwizzle,TensorMapDataType,TensorMapInterleave,TensorMapL2Promotion,TensorMapFloatOOBFill, and the functionscreate_tma_descriptor()andprefetch_tma_descriptor()are now available fromgpu.host.nvidia.tmainstead ofgpu.host._nvidia_cuda.
Testing
-
assert_equal()now displays colored character-by-character diffs when string comparisons fail, making it easier to spot differences. Differing characters are highlighted in red for the left string and green for the right string. -
The
mojo testcommand has been removed. The recommended testing strategy is to define test functions, call them explicitly frommain()(or use the newTestSuiteframework), and run withmojo run. -
TestSuitenow can generate test reports with.generate_report(). Also,TestReportandTestSuiteReportstructs were added. -
TestSuitenow allows explicitly skipping registered tests using theTestSuite.skip()method. -
TestSuitenow allows basic control from CLI arguments. Tests can be skipped from the CLI by passing test function names after a--skipflag, e.g.mojo run test_my_stuff.mojo --skip test_currently_failing test_also_failingSimilarly, the
--onlyflag enables the specification of an allowlist, e.g. the following will skip any other registered test cases:mojo run test_my_stuff.mojo --only test_only_this test_this_as_wellThe
--skip-allflag will skip all registered test cases in the suite. Note that--onlyrespects skipped tests, i.e. it does not run tests that are skipped usingTestSuite.skip().
System and OS
-
Added
os.isatty()function to check whether a file descriptor refers to a terminal. This function accepts anIntfile descriptor. If you have aFileDescriptorobject, use itsisatty()method instead. -
Added
sys.compile.SanitizeAddressproviding a way for Mojo code to detect--sanitize addressat compile time. -
DLHandleis no longer part of the public API. To access a dynamically-linked library from Mojo, useOwnedDLHandleinstead. The new type provides RAII-based automatic resource management for dynamically-linked libraries.DLHandlehas been renamed to_DLHandleand remains available internally for use by the standard library.
Performance optimizations
-
Optimized float-to-string formatting performance by eliminating unnecessary stack allocations. Internal lookup tables used for float formatting (
cache_f32andcache_f64) are now stored as global constants instead of being materialized on the stack for each conversion. This reduces stack overhead by ~10KB forFloat64and ~600 bytes forFloat32operations, improving performance for all float formatting operations includingprint(), string interpolation, andstr()conversions. -
Optimized number parsing performance by eliminating stack allocations for large lookup tables. Internal lookup tables used for number parsing (
powers_of_5_tableandPOWERS_OF_10) are now stored as global constants using theglobal_constantfunction instead of being materialized on the stack for each parsing operation. This reduces stack overhead by ~10.6KB for number parsing operations, improving performance for string-to-number conversions includingatof()and related float parsing operations.
Other library changes
-
The
Hashertrait's_update_with_bytes()method now takesSpan[Byte]instead ofUnsafePointer[UInt8]and a separate length parameter. This change applies to all hasher implementations includingAHasherandFnv1a. -
The Philox random number generator (
RandomandNormalRandom) has been moved fromgpu.randomtorandom.philox. These types now work on both CPU and GPU. Import them usingfrom random import Random, NormalRandomorfrom random.philox import Random, NormalRandom.
Tooling changes
-
Error and warning messages now preserve
comptime/alias names in many cases, to prevent extremely long type names for complex types. The compiler will expand these when necessary to understand the type based on a simple heuristic, for example:struct Dep[T: AnyType, v: T]: pass
alias MyDep[T: AnyType, v: T] = Dep[T, v]
alias MyDepGetAlias0 = MyDep.helloproduces:
$ mojo t.mojo
t.mojo:10:29: error: 'MyDep' needs more parameters bound before accessing attributes
alias MyDepGetAlias0 = MyDep.hello
^
t.mojo:10:29: note: 'MyDep' is aka 'alias[T: AnyType, v: T] Dep[T, v]'Please file issues in cases where more information needs to be exposed.
-
Error messages now preserve symbolic calls to
always_inline("builtin")functions rather than inlining them into the error message. -
This release includes a number of improvements to elaboration errors. The elaborator is the compiler pass where final parameter values are determined and code is transformed from parametric to concrete. Common errors from elaboration include "function instantiation failed" and "call expansion failed" messages, when it's determined that the actual parameter values don't match any viable function overload.
The elaborator also checks constraints (defined using the
constrained()function), so constraint failures are generated during this pass.-
Elaboration errors now report the full call instantiation failure path. For this Mojo file:
fn fn1[T: ImplicitlyCopyable, //] (a: T):
constrained[False]()
fn fn2[T: ImplicitlyCopyable, //] (a: T):
return fn1(a)
fn main():
fn2(1)Now the error prints the path of
main -> fn2 -> fn1 -> constrained[False]instead of justconstrained[False]. -
Elaboration errors now print out trivial parameter values with call expansion failures. For this simple Mojo program:
fn fn1[a: Int, b: Int]():
constrained[a < b]()
fn fn2[a: Int, b: Int]():
fn1[a, b]()
fn main():
fn2[4, 2]()Now the error message shows
parameter value(s): ("a": 4, "b": 2):test.mojo:6:14: note: call expansion failed with parameter value(s): ("a": 4, "b": 2)
fn1[a, b]()Only string and numerical values are printed out by default now, other values are shown as
.... Use--elaboration-error-verboseto show all parameter values. -
Elaboration error messages related to the prelude are omitted by default. The prelude is the set of APIs exported from
std/builtin/_startup.mojo. These APIs persist in all call expansion paths but are rarely the source of reported errors. These APIs are now omitted by default to de-clutter elaboration errors.Use
--elaboration-error-include-preludeto include prelude.-
By default (without prelude):
test.mojo:43:4: error: function instantiation failed
fn main():
^
test.mojo:45:12: note: call expansion failed
my_func()
... -
With prelude:
oss/modular/mojo/stdlib/std/builtin/_startup.mojo:119:4: error: function instantiation failed
fn __mojo_main_prototype(
^
oss/modular/mojo/stdlib/std/builtin/_startup.mojo:119:4: note: call expansion failed with parameter value(s): (...)
oss/modular/mojo/stdlib/std/builtin/_startup.mojo:42:4: note: function instantiation failed
fn __wrap_and_execute_main[
^
oss/modular/mojo/stdlib/std/builtin/_startup.mojo:68:14: note: call expansion failed
main_func()
^
test.mojo:43:4: note: function instantiation failed
fn main():
^
test:45:12: note: call expansion failed
my_func()
...
-
-
An
--elaboration-error-limitoption has been added to themojo runandmojo buildcommands. This option sets a limit to the number of elaboration errors that get printed. The default value is 20. To change the limit, use--elaboration-error-limit=limit, where a limit of0means unlimited.
-
-
--help-hiddenoption has been added to all Mojo CLI commands to show hidden options. -
mojo debugnow rejects unknown options betweendebugand the target. -
The Mojo language server will now report more coherent code actions.
-
The
mojoCLI now has an--experimental-fixitflag that automatically applies FixIt hints emitted by the parser. This feature is highly experimental, and users should ensure they back up their files (or check them into source control) before using it.
❌ Removed
-
mojo testhas been deprecated and removed as described in the deprecation proposal. For more information, see the Testing section. -
LayoutTensorBuildtype has been removed. UseLayoutTensorwith parameters directly instead. -
The following traits have been removed:
LessThanComparable,GreaterThanComparable,LessThanOrEqualComparable,GreaterThanOrEqualComparable. It is extremely rare that a type would only implement one of these, so one can just useComparableinstead. -
All telemetry-related code has been removed from the Mojo compiler. This should eliminate the source of some hangs and misbehavior on poor internet connections.
🛠️ Fixed
-
Issue #5111: The
math.cos()andmath.sin()function can now be evaluated at compile time. -
Fixed
IntTuple.value(i)method returning incorrect values when elements are stored as nested single-element tuples. Previously, callingLayout.row_major(M, N).stride.value(i)would return negative offset values (e.g., -65536, -65537) instead of the actual stride values. This affected any code that accessed layout stride or shape values using thevalue()method. -
Fixed
LayoutTensor.shape[idx]()method returning incorrect values for nested layouts. The bug occurred when accessing shape dimensions of tensors with nested layouts like((32, 2), (32, 4)), where the method would return garbage values instead of the correct product (e.g., 64). -
Fixed
LayoutTensorelement-wise arithmetic operations (+,-,*,/) between tensors with different memory layouts. Previously, operations likea.transpose() - bwould produce incorrect results when the operands had different layouts, because the same layout index was incorrectly used for both operands. This now correctly computes separate indices for each tensor based on its layout. -
Fixed
LayoutTensor.shape[idx]()method returning incorrect values for nested layouts. The bug occurred when accessing shape dimensions of tensors with nested layouts like((32, 2), (32, 4)), where the method would return garbage values instead of the correct product (e.g., 64). -
Fixed
arange()function inlayout._fillersto properly handle nested layout structures. Previously, the function would fail when filling tensors with nested layouts likeLayout(IntTuple(IntTuple(16, 8), IntTuple(32, 2)), ...)because it attempted to extract shape values from nested tuples incorrectly. -
Issue #5479: Mojo crashes when compiling standalone
__del__function without struct context. -
Issue #5500: Added comprehensive documentation to
gpu/host/info.mojoexplaining GPU target configuration and LLVM data layout strings. The documentation now includes detailed explanations of all MLIR target components, vendor-specific patterns for NVIDIA/AMD/Apple GPUs, step-by-step guides for adding new GPU architectures, and practical methods for obtaining data layout strings. -
Issue #5492: Fixed
FileHandle"rw" mode unexpectedly truncating file contents. Opening a file withopen(path, "rw")now correctly preserves existing file content and allows both reading and writing, similar to Python's "r+" mode. Previously, "rw" mode would immediately truncate the file, making it impossible to read existing content and causing potential data loss. -
Issue #3849: Added support for append mode ("a") when opening files. The
open()function now accepts "a" as a valid mode, which opens a file for appending. Content written to a file opened in append mode is added to the end of the file without truncating existing content. If the file doesn't exist, it will be created. -
Issue #3208: Fixed
FileHandleraising "unable to remove existing file" error when opening a FIFO (named pipe) in write mode. Opening special files like FIFOs, devices, and sockets withopen(path, "w")now works correctly. Previously, write mode would attempt to remove the existing file before opening it, which failed for special files that should not be removed. -
Issue #5142: The
sys.intrinsics.compressed_storefunction now includes adebug_assertto catch null pointer usage, providing a clear error message instead of crashing with a segmentation fault. -
The
sys.intrinsics.strided_load(),sys.intrinsics.strided_store(),sys.intrinsics.masked_load(), andsys.intrinsics.masked_store()functions now include adebug_assert()to catch null pointer usage, providing a clear error message instead of crashing with a segmentation fault. -
The
loggerpackage now prints its levels in color. -
Throwing
deinitmethods now understand thatselfis deinitialized in error paths, avoiding redundant calls to implicit destructors and improving linear type support.
Special thanks
Special thanks to our community contributors: Brian Grenier (@bgreni), c-pozzi (@c-pozzi), Christoph Schlumpf (@christoph-schlumpf), cudawarped (@cudawarped), David Faden (revfad.com) (@fadend), Ethan Wu (@YichengDWu), Hardik Gupta (@hardikkgupta), j_rutzmoser (@Rutzmoser), Johnny Lin (@johnny19436), Jose (@josetorrs), josiahls (@josiahls), Luis Chamberlain (@mcgrof), Manuel Saelices (@msaelices), Marius S (@winding-lines), martinvuyk (@martinvuyk), MaxMeyberg (@MaxMeyberg), Monal (@Monal-Patel), skrript (@skrript), soraros (@soraros), and Thomas Mader (@ThomasMader).