While most code can run at compile time, Mojo won’t run code that depends on the execution environment. The following are examples of code that Mojo won’t run at compile time:
File I/O.
Foreign function calls (for example, to external libraries).
Sure, those are examples of functions that won’t run at compile time. But how does the compiler know to disallow them?
It can’t just be about purity and side-effect-freeness, since the compiler has to have some notion of “default architecture”, and that’s obviously not a pure function.
So something is decorating functions as “poisoned” for compile-time use. This is the structure I’m curious about.
The way comptime works is that it Mojo’s front-end can be hooked up to:
An interpreter (mojo run)
A JIT compiler (what MAX uses)
A full AOT compiler (mojo build)
At comptime, Mojo uses the interpreter to run code as part of handling the type system, and the one which runs at comptime disallows:
inline assembly
C ffi
Mechanisms to make system calls
It then uses a hook in the allocator to allow that to continue to work. Additionally, the compiler only implements a subset of LLVM IR. If you find yourself trying to use non-portable intrinsics or even just intrinsics that Modular hasn’t plumbed in, you get errors from the interpreter. When in mojo run mode, it can just compile the function to native code and run it there when it sees problems.
+1, the comptime interpreter doesn’t proactively disable things, it is more about what is can support. Early in Mojo existence it had a number of limitations (eg couldn’t handle raising functions) but today it is pretty complete except FFI and asm. As a consequence of those two limitations, it can’t do external syscalls and IO stuff.
I see, so it’s just that certain functions are unavailable at compile-time. I can see not supporting asm as it makes cross-compilation even more challenging. I suppose general FFI also has that issue.
I would think certain IO stuff could be bridged at some point to allow, for example, a “include a hex blob as bytes” type of meta-programming. Of course, the more functionality we allow at compile-time, the more security risk there is in compiling unaudited code, so this bridging ought to be judiciously chosen.
Mojo’s comptime runs code during compilation, but it’s not a free-for-all. The compiler only allows things it can fully figure out at compile time, like creating constants, inspecting types, or generating code. If your code depends on runtime data, mutable state, or side effects, it won’t work at comptime. Basically, the compiler checks if it can safely run the code early without surprises later.