Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stage2: @compileLog #7587

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1354,6 +1354,9 @@ pub fn totalErrorCount(self: *Compilation) usize {
module.failed_exports.items().len +
module.failed_files.items().len +
@boolToInt(module.failed_root_src_file != null);
for (module.compile_log_decls.items()) |entry| {
total += entry.value.items.len;
}
}

// The "no entry point found" error only counts if there are no other errors.
Expand Down Expand Up @@ -1404,6 +1407,15 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
});
try AllErrors.addPlain(&arena, &errors, msg);
}
for (module.compile_log_decls.items()) |entry| {
const decl = entry.key;
const path = decl.scope.subFilePath();
const source = try decl.scope.getSource(module);
for (entry.value.items) |src_loc| {
const err_msg = ErrorMsg{ .byte_offset = src_loc, .msg = "found compile log statement" };
try AllErrors.add(&arena, &errors, path, source, err_msg);
}
}
}

if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) {
Expand Down
45 changes: 45 additions & 0 deletions src/Module.zig
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_has
/// Note that a Decl can succeed but the Fn it represents can fail. In this case,
/// a Decl can have a failed_decls entry but have analysis status of success.
failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *Compilation.ErrorMsg) = .{},
/// A Decl can have multiple compileLogs, but only one error, so we map a Decl to a the src locs of all the compileLogs
compile_log_decls: std.AutoArrayHashMapUnmanaged(*Decl, ArrayListUnmanaged(usize)) = .{},
/// Using a map here for consistency with the other fields here.
/// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator.
failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *Compilation.ErrorMsg) = .{},
Expand Down Expand Up @@ -864,6 +866,11 @@ pub fn deinit(self: *Module) void {
}
self.failed_exports.deinit(gpa);

for (self.compile_log_decls.items()) |*entry| {
entry.value.deinit(gpa);
}
self.compile_log_decls.deinit(gpa);

for (self.decl_exports.items()) |entry| {
const export_list = entry.value;
gpa.free(export_list);
Expand Down Expand Up @@ -2948,6 +2955,44 @@ pub fn failNode(
return self.fail(scope, src, format, args);
}

fn addCompileLog(self: *Module, decl: *Decl, src: usize) error{OutOfMemory}!void {
const entry = try self.compile_log_decls.getOrPutValue(self.gpa, decl, .{});
try entry.value.append(self.gpa, src);
}

pub fn failCompileLog(
self: *Module,
scope: *Scope,
src: usize,
) InnerError!void {
switch (scope.tag) {
.decl => {
const decl = scope.cast(Scope.DeclAnalysis).?.decl;
try self.addCompileLog(decl, src);
},
.block => {
const block = scope.cast(Scope.Block).?;
try self.addCompileLog(block.decl, src);
},
.gen_zir => {
const gen_zir = scope.cast(Scope.GenZIR).?;
try self.addCompileLog(gen_zir.decl, src);
},
.local_val => {
const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir;
try self.addCompileLog(gen_zir.decl, src);
},
.local_ptr => {
const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir;
try self.addCompileLog(gen_zir.decl, src);
},
.zir_module,
.file,
.container,
=> unreachable,
}
}

fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Compilation.ErrorMsg) InnerError {
{
errdefer err_msg.destroy(self.gpa);
Expand Down
12 changes: 12 additions & 0 deletions src/astgen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2349,6 +2349,16 @@ fn typeOf(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCal
items[param_i] = try expr(mod, scope, .none, param);
return rlWrap(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.TypeOfPeer, .{ .items = items }, .{}));
}
fn compileLog(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
const tree = scope.tree();
const arena = scope.arena();
const src = tree.token_locs[call.builtin_token].start;
const params = call.params();
var targets = try arena.alloc(*zir.Inst, params.len);
for (params) |param, param_i|
targets[param_i] = try expr(mod, scope, .none, param);
return addZIRInst(mod, scope, src, zir.Inst.CompileLog, .{ .to_log = targets }, .{});
}

fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
const tree = scope.tree();
Expand Down Expand Up @@ -2378,6 +2388,8 @@ fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.Built
return rlWrap(mod, scope, rl, try import(mod, scope, call));
} else if (mem.eql(u8, builtin_name, "@compileError")) {
return compileError(mod, scope, call);
} else if (mem.eql(u8, builtin_name, "@compileLog")) {
return compileLog(mod, scope, call);
} else {
return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{}'", .{builtin_name});
}
Expand Down
17 changes: 17 additions & 0 deletions src/zir.zig
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ pub const Inst = struct {
coerce_to_ptr_elem,
/// Emit an error message and fail compilation.
compileerror,
/// Log compile time variables and emit an error message.
compilelog,
/// Conditional branch. Splits control flow based on a boolean condition value.
condbr,
/// Special case, has no textual representation.
Expand Down Expand Up @@ -392,6 +394,7 @@ pub const Inst = struct {
.declval => DeclVal,
.declval_in_module => DeclValInModule,
.coerce_result_block_ptr => CoerceResultBlockPtr,
.compilelog => CompileLog,
.loop => Loop,
.@"const" => Const,
.str => Str,
Expand Down Expand Up @@ -522,6 +525,7 @@ pub const Inst = struct {
.import,
.switch_range,
.typeof_peer,
.compilelog,
=> false,

.@"break",
Expand Down Expand Up @@ -705,6 +709,19 @@ pub const Inst = struct {
kw_args: struct {},
};

pub const CompileLog = struct {
pub const base_tag = Tag.compilelog;
base: Inst,

positionals: struct {
to_log: []*Inst,
},
kw_args: struct {
/// If we have seen it already so don't make another error
seen: bool = false,
},
};

pub const Const = struct {
pub const base_tag = Tag.@"const";
base: Inst,
Expand Down
22 changes: 22 additions & 0 deletions src/zir_sema.zig
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
.coerce_result_ptr => return analyzeInstCoerceResultPtr(mod, scope, old_inst.castTag(.coerce_result_ptr).?),
.coerce_to_ptr_elem => return analyzeInstCoerceToPtrElem(mod, scope, old_inst.castTag(.coerce_to_ptr_elem).?),
.compileerror => return analyzeInstCompileError(mod, scope, old_inst.castTag(.compileerror).?),
.compilelog => return analyzeInstCompileLog(mod, scope, old_inst.castTag(.compilelog).?),
.@"const" => return analyzeInstConst(mod, scope, old_inst.castTag(.@"const").?),
.dbg_stmt => return analyzeInstDbgStmt(mod, scope, old_inst.castTag(.dbg_stmt).?),
.declref => return analyzeInstDeclRef(mod, scope, old_inst.castTag(.declref).?),
Expand Down Expand Up @@ -502,6 +503,27 @@ fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) In
return mod.fail(scope, inst.base.src, "{}", .{msg});
}

fn analyzeInstCompileLog(mod: *Module, scope: *Scope, inst: *zir.Inst.CompileLog) InnerError!*Inst {
std.debug.print("| ", .{});
for (inst.positionals.to_log) |item, i| {
const to_log = try resolveInst(mod, scope, item);
if (to_log.value()) |val| {
std.debug.print("{}", .{val});
} else {
std.debug.print("(runtime value)", .{});
}
if (i != inst.positionals.to_log.len - 1) std.debug.print(", ", .{});
}
std.debug.print("\n", .{});
if (!inst.kw_args.seen) {

// so that we do not give multiple compile errors if it gets evaled twice
inst.kw_args.seen = true;
try mod.failCompileLog(scope, inst.base.src);
}
return mod.constVoid(scope, inst.base.src);
}

fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst {
const b = try mod.requireRuntimeBlock(scope, inst.base.src);
const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty;
Expand Down
18 changes: 18 additions & 0 deletions test/stage2/test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,24 @@ pub fn addCases(ctx: *TestContext) !void {
\\}
, &[_][]const u8{":3:9: error: redefinition of 'testing'"});
}
ctx.compileError("compileLog", linux_x64,
\\export fn _start() noreturn {
\\ const b = true;
\\ var f: u32 = 1;
\\ @compileLog(b, 20, f, x);
\\ @compileLog(1000);
\\ var bruh: usize = true;
\\ unreachable;
\\}
\\fn x() void {}
, &[_][]const u8{
":4:3: error: found compile log statement",
":5:3: error: found compile log statement",
":6:21: error: expected usize, found bool",
});
// TODO if this is here it invalidates the compile error checker:
// "| true, 20, (runtime value), (function)"
// "| 1000"

{
var case = ctx.obj("extern variable has no type", linux_x64);
Expand Down