stage2: comptime function calls

* Function calls that happen in a comptime scope get called at
   compile-time. We do this by putting the parameters in place as
   constant values and then running regular function analysis on the
   body.
 * Added `Scope.Block.dump()` for debugging purposes.
 * Fixed some code to call `identifierTokenString` rather than
   `tokenSlice`, making it work for `@""` syntax.
 * Implemented `Value.copy` for big integers.

Follow-up issues to tackle:
 * Adding compile errors to the callsite instead of the callee Decl.
 * Proper error notes for "called from here".
   - Related: #7555
 * Branch quotas.
 * ZIR support?
This commit is contained in:
Andrew Kelley
2021-01-01 19:24:02 -07:00
parent fb37c1b091
commit fea8659b82
6 changed files with 257 additions and 46 deletions

View File

@@ -268,6 +268,11 @@ pub const Decl = struct {
}
}
/// Asserts that the `Decl` is part of AST and not ZIRModule.
pub fn getFileScope(self: *Decl) *Scope.File {
return self.scope.cast(Scope.Container).?.file_scope;
}
fn removeDependant(self: *Decl, other: *Decl) void {
self.dependants.removeAssertDiscard(other);
}
@@ -776,6 +781,11 @@ pub const Scope = struct {
results: ArrayListUnmanaged(*Inst),
block_inst: *Inst.Block,
};
/// For debugging purposes.
pub fn dump(self: *Block, mod: Module) void {
zir.dumpBlock(mod, self);
}
};
/// This is a temporary structure, references to it are valid only
@@ -992,11 +1002,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
defer tracy.end();
const container_scope = decl.scope.cast(Scope.Container).?;
const tree = try self.getAstTree(container_scope);
const tree = try self.getAstTree(container_scope.file_scope);
const ast_node = tree.root_node.decls()[decl.src_index];
switch (ast_node.tag) {
.FnProto => {
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", ast_node);
const fn_proto = ast_node.castTag(.FnProto).?;
decl.analysis = .in_progress;
@@ -1131,7 +1141,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
for (fn_proto.params()) |param, i| {
const name_token = param.name_token.?;
const src = tree.token_locs[name_token].start;
const param_name = tree.tokenSlice(name_token); // TODO: call identifierTokenString
const param_name = try self.identifierTokenString(&gen_scope.base, name_token);
const arg = try gen_scope_arena.allocator.create(zir.Inst.Arg);
arg.* = .{
.base = .{
@@ -1496,12 +1506,10 @@ fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
}
}
fn getAstTree(self: *Module, container_scope: *Scope.Container) !*ast.Tree {
pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
const tracy = trace(@src());
defer tracy.end();
const root_scope = container_scope.file_scope;
switch (root_scope.status) {
.never_loaded, .unloaded_success => {
try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
@@ -1549,7 +1557,7 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void
// We may be analyzing it for the first time, or this may be
// an incremental update. This code handles both cases.
const tree = try self.getAstTree(container_scope);
const tree = try self.getAstTree(container_scope.file_scope);
const decls = tree.root_node.decls();
try self.comp.work_queue.ensureUnusedCapacity(decls.len);
@@ -3427,3 +3435,23 @@ pub fn validateVarType(mod: *Module, scope: *Scope, src: usize, ty: Type) !void
return mod.fail(scope, src, "variable of type '{}' must be const or comptime", .{ty});
}
}
/// Identifier token -> String (allocated in scope.arena())
pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 {
const tree = scope.tree();
const ident_name = tree.tokenSlice(token);
if (mem.startsWith(u8, ident_name, "@")) {
const raw_string = ident_name[1..];
var bad_index: usize = undefined;
return std.zig.parseStringLiteral(scope.arena(), raw_string, &bad_index) catch |err| switch (err) {
error.InvalidCharacter => {
const bad_byte = raw_string[bad_index];
const src = tree.token_locs[token].start;
return mod.fail(scope, src + 1 + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
},
else => |e| return e,
};
}
return ident_name;
}