| author | |
| committer | |
| log | ab5f7b51566ad2c8e635e81b8515a97206daf8ab |
| tree | 16b0fd8057df6d52f3b67ab56a12175847f1bdb9 |
| parent | 38572ee89492d8499a5881d12c401d9bb0925167 |
6 files changed, 135 insertions(+), 3 deletions(-)
src/Compilation.zig+12| ... | ... | @@ -1351,6 +1351,9 @@ pub fn totalErrorCount(self: *Compilation) usize { |
| 1351 | 1351 | module.failed_exports.items().len + |
| 1352 | 1352 | module.failed_files.items().len + |
| 1353 | 1353 | @boolToInt(module.failed_root_src_file != null); |
| 1354 | for (module.compile_log_decls.items()) |entry| { | |
| 1355 | total += entry.value.items.len; | |
| 1356 | } | |
| 1354 | 1357 | } |
| 1355 | 1358 | |
| 1356 | 1359 | // The "no entry point found" error only counts if there are no other errors. |
| ... | ... | @@ -1407,6 +1410,15 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 1407 | 1410 | }); |
| 1408 | 1411 | try AllErrors.addPlain(&arena, &errors, msg); |
| 1409 | 1412 | } |
| 1413 | for (module.compile_log_decls.items()) |entry| { | |
| 1414 | const decl = entry.key; | |
| 1415 | const path = decl.scope.subFilePath(); | |
| 1416 | const source = try decl.scope.getSource(module); | |
| 1417 | for (entry.value.items) |src_loc| { | |
| 1418 | const err_msg = ErrorMsg{ .byte_offset = src_loc, .msg = "found compile log statement" }; | |
| 1419 | try AllErrors.add(&arena, &errors, path, source, err_msg); | |
| 1420 | } | |
| 1421 | } | |
| 1410 | 1422 | } |
| 1411 | 1423 | |
| 1412 | 1424 | if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) { |
src/Module.zig+51| ... | ... | @@ -61,6 +61,8 @@ failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *Compilation.ErrorMsg) = .{}, |
| 61 | 61 | /// emit-h failing for that Decl. This table is also how we tell if a Decl has |
| 62 | 62 | /// failed emit-h or succeeded. |
| 63 | 63 | emit_h_failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *Compilation.ErrorMsg) = .{}, |
| 64 | /// A Decl can have multiple compileLogs, but only one error, so we map a Decl to a the src locs of all the compileLogs | |
| 65 | compile_log_decls: std.AutoArrayHashMapUnmanaged(*Decl, ArrayListUnmanaged(usize)) = .{}, | |
| 64 | 66 | /// Using a map here for consistency with the other fields here. |
| 65 | 67 | /// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator. |
| 66 | 68 | failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *Compilation.ErrorMsg) = .{}, |
| ... | ... | @@ -936,6 +938,11 @@ pub fn deinit(self: *Module) void { |
| 936 | 938 | } |
| 937 | 939 | self.failed_exports.deinit(gpa); |
| 938 | 940 | |
| 941 | for (self.compile_log_decls.items()) |*entry| { | |
| 942 | entry.value.deinit(gpa); | |
| 943 | } | |
| 944 | self.compile_log_decls.deinit(gpa); | |
| 945 | ||
| 939 | 946 | for (self.decl_exports.items()) |entry| { |
| 940 | 947 | const export_list = entry.value; |
| 941 | 948 | gpa.free(export_list); |
| ... | ... | @@ -1881,6 +1888,9 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void { |
| 1881 | 1888 | if (self.emit_h_failed_decls.remove(decl)) |entry| { |
| 1882 | 1889 | entry.value.destroy(self.gpa); |
| 1883 | 1890 | } |
| 1891 | if (self.compile_log_decls.remove(decl)) |*entry| { | |
| 1892 | entry.value.deinit(self.gpa); | |
| 1893 | } | |
| 1884 | 1894 | self.deleteDeclExports(decl); |
| 1885 | 1895 | self.comp.bin_file.freeDecl(decl); |
| 1886 | 1896 | |
| ... | ... | @@ -1971,6 +1981,9 @@ fn markOutdatedDecl(self: *Module, decl: *Decl) !void { |
| 1971 | 1981 | if (self.emit_h_failed_decls.remove(decl)) |entry| { |
| 1972 | 1982 | entry.value.destroy(self.gpa); |
| 1973 | 1983 | } |
| 1984 | if (self.compile_log_decls.remove(decl)) |*entry| { | |
| 1985 | entry.value.deinit(self.gpa); | |
| 1986 | } | |
| 1974 | 1987 | decl.analysis = .outdated; |
| 1975 | 1988 | } |
| 1976 | 1989 | |
| ... | ... | @@ -3151,6 +3164,44 @@ pub fn failNode( |
| 3151 | 3164 | return self.fail(scope, src, format, args); |
| 3152 | 3165 | } |
| 3153 | 3166 | |
| 3167 | fn addCompileLog(self: *Module, decl: *Decl, src: usize) error{OutOfMemory}!void { | |
| 3168 | const entry = try self.compile_log_decls.getOrPutValue(self.gpa, decl, .{}); | |
| 3169 | try entry.value.append(self.gpa, src); | |
| 3170 | } | |
| 3171 | ||
| 3172 | pub fn failCompileLog( | |
| 3173 | self: *Module, | |
| 3174 | scope: *Scope, | |
| 3175 | src: usize, | |
| 3176 | ) InnerError!void { | |
| 3177 | switch (scope.tag) { | |
| 3178 | .decl => { | |
| 3179 | const decl = scope.cast(Scope.DeclAnalysis).?.decl; | |
| 3180 | try self.addCompileLog(decl, src); | |
| 3181 | }, | |
| 3182 | .block => { | |
| 3183 | const block = scope.cast(Scope.Block).?; | |
| 3184 | try self.addCompileLog(block.decl, src); | |
| 3185 | }, | |
| 3186 | .gen_zir => { | |
| 3187 | const gen_zir = scope.cast(Scope.GenZIR).?; | |
| 3188 | try self.addCompileLog(gen_zir.decl, src); | |
| 3189 | }, | |
| 3190 | .local_val => { | |
| 3191 | const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir; | |
| 3192 | try self.addCompileLog(gen_zir.decl, src); | |
| 3193 | }, | |
| 3194 | .local_ptr => { | |
| 3195 | const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir; | |
| 3196 | try self.addCompileLog(gen_zir.decl, src); | |
| 3197 | }, | |
| 3198 | .zir_module, | |
| 3199 | .file, | |
| 3200 | .container, | |
| 3201 | => unreachable, | |
| 3202 | } | |
| 3203 | } | |
| 3204 | ||
| 3154 | 3205 | fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Compilation.ErrorMsg) InnerError { |
| 3155 | 3206 | { |
| 3156 | 3207 | errdefer err_msg.destroy(self.gpa); |
src/astgen.zig+12| ... | ... | @@ -2346,6 +2346,16 @@ fn typeOf(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCal |
| 2346 | 2346 | items[param_i] = try expr(mod, scope, .none, param); |
| 2347 | 2347 | return rlWrap(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.TypeOfPeer, .{ .items = items }, .{})); |
| 2348 | 2348 | } |
| 2349 | fn compileLog(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { | |
| 2350 | const tree = scope.tree(); | |
| 2351 | const arena = scope.arena(); | |
| 2352 | const src = tree.token_locs[call.builtin_token].start; | |
| 2353 | const params = call.params(); | |
| 2354 | var targets = try arena.alloc(*zir.Inst, params.len); | |
| 2355 | for (params) |param, param_i| | |
| 2356 | targets[param_i] = try expr(mod, scope, .none, param); | |
| 2357 | return addZIRInst(mod, scope, src, zir.Inst.CompileLog, .{ .to_log = targets }, .{}); | |
| 2358 | } | |
| 2349 | 2359 | |
| 2350 | 2360 | fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst { |
| 2351 | 2361 | const tree = scope.tree(); |
| ... | ... | @@ -2377,6 +2387,8 @@ fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.Built |
| 2377 | 2387 | return compileError(mod, scope, call); |
| 2378 | 2388 | } else if (mem.eql(u8, builtin_name, "@setEvalBranchQuota")) { |
| 2379 | 2389 | return setEvalBranchQuota(mod, scope, call); |
| 2390 | } else if (mem.eql(u8, builtin_name, "@compileLog")) { | |
| 2391 | return compileLog(mod, scope, call); | |
| 2380 | 2392 | } else { |
| 2381 | 2393 | return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{s}'", .{builtin_name}); |
| 2382 | 2394 | } |
src/zir.zig+20-3| ... | ... | @@ -127,9 +127,8 @@ pub const Inst = struct { |
| 127 | 127 | coerce_to_ptr_elem, |
| 128 | 128 | /// Emit an error message and fail compilation. |
| 129 | 129 | compileerror, |
| 130 | /// Changes the maximum number of backwards branches that compile-time | |
| 131 | /// code execution can use before giving up and making a compile error. | |
| 132 | set_eval_branch_quota, | |
| 130 | /// Log compile time variables and emit an error message. | |
| 131 | compilelog, | |
| 133 | 132 | /// Conditional branch. Splits control flow based on a boolean condition value. |
| 134 | 133 | condbr, |
| 135 | 134 | /// Special case, has no textual representation. |
| ... | ... | @@ -223,6 +222,9 @@ pub const Inst = struct { |
| 223 | 222 | @"return", |
| 224 | 223 | /// Same as `return` but there is no operand; the operand is implicitly the void value. |
| 225 | 224 | returnvoid, |
| 225 | /// Changes the maximum number of backwards branches that compile-time | |
| 226 | /// code execution can use before giving up and making a compile error. | |
| 227 | set_eval_branch_quota, | |
| 226 | 228 | /// Integer shift-left. Zeroes are shifted in from the right hand side. |
| 227 | 229 | shl, |
| 228 | 230 | /// Integer shift-right. Arithmetic or logical depending on the signedness of the integer type. |
| ... | ... | @@ -407,6 +409,7 @@ pub const Inst = struct { |
| 407 | 409 | .declval => DeclVal, |
| 408 | 410 | .declval_in_module => DeclValInModule, |
| 409 | 411 | .coerce_result_block_ptr => CoerceResultBlockPtr, |
| 412 | .compilelog => CompileLog, | |
| 410 | 413 | .loop => Loop, |
| 411 | 414 | .@"const" => Const, |
| 412 | 415 | .str => Str, |
| ... | ... | @@ -540,6 +543,7 @@ pub const Inst = struct { |
| 540 | 543 | .typeof_peer, |
| 541 | 544 | .resolve_inferred_alloc, |
| 542 | 545 | .set_eval_branch_quota, |
| 546 | .compilelog, | |
| 543 | 547 | => false, |
| 544 | 548 | |
| 545 | 549 | .@"break", |
| ... | ... | @@ -723,6 +727,19 @@ pub const Inst = struct { |
| 723 | 727 | kw_args: struct {}, |
| 724 | 728 | }; |
| 725 | 729 | |
| 730 | pub const CompileLog = struct { | |
| 731 | pub const base_tag = Tag.compilelog; | |
| 732 | base: Inst, | |
| 733 | ||
| 734 | positionals: struct { | |
| 735 | to_log: []*Inst, | |
| 736 | }, | |
| 737 | kw_args: struct { | |
| 738 | /// If we have seen it already so don't make another error | |
| 739 | seen: bool = false, | |
| 740 | }, | |
| 741 | }; | |
| 742 | ||
| 726 | 743 | pub const Const = struct { |
| 727 | 744 | pub const base_tag = Tag.@"const"; |
| 728 | 745 | base: Inst, |
src/zir_sema.zig+22| ... | ... | @@ -57,6 +57,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! |
| 57 | 57 | .coerce_result_ptr => return analyzeInstCoerceResultPtr(mod, scope, old_inst.castTag(.coerce_result_ptr).?), |
| 58 | 58 | .coerce_to_ptr_elem => return analyzeInstCoerceToPtrElem(mod, scope, old_inst.castTag(.coerce_to_ptr_elem).?), |
| 59 | 59 | .compileerror => return analyzeInstCompileError(mod, scope, old_inst.castTag(.compileerror).?), |
| 60 | .compilelog => return analyzeInstCompileLog(mod, scope, old_inst.castTag(.compilelog).?), | |
| 60 | 61 | .@"const" => return analyzeInstConst(mod, scope, old_inst.castTag(.@"const").?), |
| 61 | 62 | .dbg_stmt => return analyzeInstDbgStmt(mod, scope, old_inst.castTag(.dbg_stmt).?), |
| 62 | 63 | .declref => return analyzeInstDeclRef(mod, scope, old_inst.castTag(.declref).?), |
| ... | ... | @@ -630,6 +631,27 @@ fn analyzeInstCompileError(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) In |
| 630 | 631 | return mod.fail(scope, inst.base.src, "{s}", .{msg}); |
| 631 | 632 | } |
| 632 | 633 | |
| 634 | fn analyzeInstCompileLog(mod: *Module, scope: *Scope, inst: *zir.Inst.CompileLog) InnerError!*Inst { | |
| 635 | std.debug.print("| ", .{}); | |
| 636 | for (inst.positionals.to_log) |item, i| { | |
| 637 | const to_log = try resolveInst(mod, scope, item); | |
| 638 | if (to_log.value()) |val| { | |
| 639 | std.debug.print("{}", .{val}); | |
| 640 | } else { | |
| 641 | std.debug.print("(runtime value)", .{}); | |
| 642 | } | |
| 643 | if (i != inst.positionals.to_log.len - 1) std.debug.print(", ", .{}); | |
| 644 | } | |
| 645 | std.debug.print("\n", .{}); | |
| 646 | if (!inst.kw_args.seen) { | |
| 647 | ||
| 648 | // so that we do not give multiple compile errors if it gets evaled twice | |
| 649 | inst.kw_args.seen = true; | |
| 650 | try mod.failCompileLog(scope, inst.base.src); | |
| 651 | } | |
| 652 | return mod.constVoid(scope, inst.base.src); | |
| 653 | } | |
| 654 | ||
| 633 | 655 | fn analyzeInstArg(mod: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst { |
| 634 | 656 | const tracy = trace(@src()); |
| 635 | 657 | defer tracy.end(); |
test/stage2/test.zig+18| ... | ... | @@ -1243,6 +1243,24 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1243 | 1243 | \\} |
| 1244 | 1244 | , &[_][]const u8{":3:9: error: redefinition of 'testing'"}); |
| 1245 | 1245 | } |
| 1246 | ctx.compileError("compileLog", linux_x64, | |
| 1247 | \\export fn _start() noreturn { | |
| 1248 | \\ const b = true; | |
| 1249 | \\ var f: u32 = 1; | |
| 1250 | \\ @compileLog(b, 20, f, x); | |
| 1251 | \\ @compileLog(1000); | |
| 1252 | \\ var bruh: usize = true; | |
| 1253 | \\ unreachable; | |
| 1254 | \\} | |
| 1255 | \\fn x() void {} | |
| 1256 | , &[_][]const u8{ | |
| 1257 | ":4:3: error: found compile log statement", | |
| 1258 | ":5:3: error: found compile log statement", | |
| 1259 | ":6:21: error: expected usize, found bool", | |
| 1260 | }); | |
| 1261 | // TODO if this is here it invalidates the compile error checker: | |
| 1262 | // "| true, 20, (runtime value), (function)" | |
| 1263 | // "| 1000" | |
| 1246 | 1264 | |
| 1247 | 1265 | { |
| 1248 | 1266 | var case = ctx.obj("extern variable has no type", linux_x64); |