Why can't @import take a string literal at compile-time?

Somehow I felt this should work:

const STR = "std";

const std = @import(STR);

pub fn main() void {
    std.debug.assert(true);
}

Since @import can’t take a comptime string, instead expecting a string literal defined directly within its call, it must be that it has to resolve the given path and import the respective source file strictly before compile-time evaluation phase, but I don’t see why it can’t complete all the comptime stuff first and then run all the @import calls.

3 Likes

My understanding is that this is a very deliberate design decision.

@import is actually a bit of syntax mascaraing as a built-in function call. It is a reverse syntax sugar of sorts, as the semantics are as if you’d have to write import "std" as std;.

This is needed for reasonable compilation model. The set of @import calls reachable from the root file determine the set of files that must be compiled. It is not absolutely required, but very much desirable that you can compute this set of files without actually doing any language analysis.

So, in Zig as it is, you can compute this set after just parsing files individually. I think that not even full parsing is required, just line-by-line lexing would be enough.

That means that Zig can quickly and discover the full set of files, so that parsing can be parallelised.

You could allow some amount of comptime evaluation here, but that would introduce a sequence point into the compiler’s pipeline — before starting to parse the next file, it must do some comptime eval of the current file.

Rust works that way! There, discovering files included through mod foo;, expanding macros, and resolving imports all happen at the same time. This creates massive problems for incremental compilation, which can be surmounted only with a huge increase in compiler’s complexity, and, even then, the result is not as parallel and as incremental as the Zig’s version, it’s just a touch better than trivial sequential compillation.

10 Likes

Yep, makes sense, thanks!

That’s what I thought really, and I’m both hands for the idea of enabling, in the most elegant way, the ultimate compiler superpower that is incremental compilation.

But, I guess, I just thought this could be done at compile-time because the @import’s signature and documentation don’t mention that @import is a special builtin, a call to which must be resolved even before semantic analysis, that is during parsing. That distinction isn’t mentioned, so it threw me off.

I wonder how it can be added, because the current signature says the path parameter is just a comptime string. If this is an exceptional case, I guess it could be explained as a sidenote, but I wonder if there’re more things to play around with at parsetime :joy: