authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-05-29 05:07:17+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-05-29 23:06:08+01:00
log4976b58ab16069f8d3267b69ed030f29685c1abe
tree400f632d11eec4f3c330bee15d59f8fd6219c20f
parentb5fad3a40a86eb379903d6a803bdbe66dcaa5487
signaturelock-open Commit is signed but in an unrecognized format.

Prevent analysis of functions only referenced at comptime

The idea here is that there are two ways we can reference a function at runtime: * Through a direct call, i.e. where the function is comptime-known * Through a function pointer This means we can easily perform a form of rudimentary escape analysis on functions. If we ever see a `decl_ref` or `ref` of a function, we have a function pointer, which could "leak" into runtime code, so we emit the function; but for a plain `decl_val`, there's no need to. This change means that `comptime { _ = f; }` no longer forces a function to be emitted, which was used for some things (mainly tests). These use sites have been replaced with `_ = &f;`, which still triggers analysis of the function body, since you're taking a pointer to the function. Resolves: #6256 Resolves: #15353

33 files changed, 149 insertions(+), 65 deletions(-)

lib/compiler_rt/clear_cache.zig+1-1
...@@ -12,7 +12,7 @@ pub const panic = @import("common.zig").panic;...@@ -12,7 +12,7 @@ pub const panic = @import("common.zig").panic;
12// specified range.12// specified range.
1313
14comptime {14comptime {
15 _ = clear_cache;15 _ = &clear_cache;
16}16}
1717
18fn clear_cache(start: usize, end: usize) callconv(.C) void {18fn clear_cache(start: usize, end: usize) callconv(.C) void {
lib/std/fmt.zig+1-1
...@@ -1959,7 +1959,7 @@ pub const parseFloat = @import("fmt/parse_float.zig").parseFloat;...@@ -1959,7 +1959,7 @@ pub const parseFloat = @import("fmt/parse_float.zig").parseFloat;
1959pub const ParseFloatError = @import("fmt/parse_float.zig").ParseFloatError;1959pub const ParseFloatError = @import("fmt/parse_float.zig").ParseFloatError;
19601960
1961test {1961test {
1962 _ = parseFloat;1962 _ = &parseFloat;
1963}1963}
19641964
1965pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {1965pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {
lib/std/fs.zig+5-5
...@@ -3150,12 +3150,12 @@ fn copy_file(fd_in: os.fd_t, fd_out: os.fd_t, maybe_size: ?u64) CopyFileRawError...@@ -3150,12 +3150,12 @@ fn copy_file(fd_in: os.fd_t, fd_out: os.fd_t, maybe_size: ?u64) CopyFileRawError
31503150
3151test {3151test {
3152 if (builtin.os.tag != .wasi) {3152 if (builtin.os.tag != .wasi) {
3153 _ = makeDirAbsolute;3153 _ = &makeDirAbsolute;
3154 _ = makeDirAbsoluteZ;3154 _ = &makeDirAbsoluteZ;
3155 _ = copyFileAbsolute;3155 _ = &copyFileAbsolute;
3156 _ = updateFileAbsolute;3156 _ = &updateFileAbsolute;
3157 }3157 }
3158 _ = Dir.copyFile;3158 _ = &Dir.copyFile;
3159 _ = @import("fs/test.zig");3159 _ = @import("fs/test.zig");
3160 _ = @import("fs/path.zig");3160 _ = @import("fs/path.zig");
3161 _ = @import("fs/file.zig");3161 _ = @import("fs/file.zig");
lib/std/hash_map.zig+1-1
...@@ -1605,7 +1605,7 @@ pub fn HashMapUnmanaged(...@@ -1605,7 +1605,7 @@ pub fn HashMapUnmanaged(
16051605
1606 comptime {1606 comptime {
1607 if (builtin.mode == .Debug) {1607 if (builtin.mode == .Debug) {
1608 _ = dbHelper;1608 _ = &dbHelper;
1609 }1609 }
1610 }1610 }
1611 };1611 };
lib/std/multi_array_list.zig+2-2
...@@ -532,8 +532,8 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -532,8 +532,8 @@ pub fn MultiArrayList(comptime T: type) type {
532532
533 comptime {533 comptime {
534 if (builtin.mode == .Debug) {534 if (builtin.mode == .Debug) {
535 _ = dbHelper;535 _ = &dbHelper;
536 _ = Slice.dbHelper;536 _ = &Slice.dbHelper;
537 }537 }
538 }538 }
539 };539 };
lib/std/os/test.zig+1-1
...@@ -704,7 +704,7 @@ test "signalfd" {...@@ -704,7 +704,7 @@ test "signalfd" {
704 .linux, .solaris => {},704 .linux, .solaris => {},
705 else => return error.SkipZigTest,705 else => return error.SkipZigTest,
706 }706 }
707 _ = os.signalfd;707 _ = &os.signalfd;
708}708}
709709
710test "sync" {710test "sync" {
lib/std/testing.zig+2-2
...@@ -1116,7 +1116,7 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime...@@ -1116,7 +1116,7 @@ pub fn checkAllAllocationFailures(backing_allocator: std.mem.Allocator, comptime
1116pub fn refAllDecls(comptime T: type) void {1116pub fn refAllDecls(comptime T: type) void {
1117 if (!builtin.is_test) return;1117 if (!builtin.is_test) return;
1118 inline for (comptime std.meta.declarations(T)) |decl| {1118 inline for (comptime std.meta.declarations(T)) |decl| {
1119 if (decl.is_pub) _ = @field(T, decl.name);1119 if (decl.is_pub) _ = &@field(T, decl.name);
1120 }1120 }
1121}1121}
11221122
...@@ -1132,7 +1132,7 @@ pub fn refAllDeclsRecursive(comptime T: type) void {...@@ -1132,7 +1132,7 @@ pub fn refAllDeclsRecursive(comptime T: type) void {
1132 else => {},1132 else => {},
1133 }1133 }
1134 }1134 }
1135 _ = @field(T, decl.name);1135 _ = &@field(T, decl.name);
1136 }1136 }
1137 }1137 }
1138}1138}
src/Compilation.zig+7
...@@ -3193,6 +3193,13 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v...@@ -3193,6 +3193,13 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v
3193 error.OutOfMemory => return error.OutOfMemory,3193 error.OutOfMemory => return error.OutOfMemory,
3194 error.AnalysisFail => return,3194 error.AnalysisFail => return,
3195 };3195 };
3196 const decl = module.declPtr(decl_index);
3197 if (decl.kind == .@"test" and comp.bin_file.options.is_test) {
3198 // Tests are always emitted in test binaries. The decl_refs are created by
3199 // Module.populateTestFunctions, but this will not queue body analysis, so do
3200 // that now.
3201 try module.ensureFuncBodyAnalysisQueued(decl.val.castTag(.function).?.data);
3202 }
3196 },3203 },
3197 .update_embed_file => |embed_file| {3204 .update_embed_file => |embed_file| {
3198 const named_frame = tracy.namedFrame("update_embed_file");3205 const named_frame = tracy.namedFrame("update_embed_file");
src/Module.zig+59-15
...@@ -1638,6 +1638,10 @@ pub const Fn = struct {...@@ -1638,6 +1638,10 @@ pub const Fn = struct {
1638 inferred_error_sets: InferredErrorSetList = .{},1638 inferred_error_sets: InferredErrorSetList = .{},
16391639
1640 pub const Analysis = enum {1640 pub const Analysis = enum {
1641 /// This function has not yet undergone analysis, because we have not
1642 /// seen a potential runtime call. It may be analyzed in future.
1643 none,
1644 /// Analysis for this function has been queued, but not yet completed.
1641 queued,1645 queued,
1642 /// This function intentionally only has ZIR generated because it is marked1646 /// This function intentionally only has ZIR generated because it is marked
1643 /// inline, which means no runtime version of the function will be generated.1647 /// inline, which means no runtime version of the function will be generated.
...@@ -4323,7 +4327,7 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {...@@ -4323,7 +4327,7 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {
4323 .complete, .codegen_failure_retryable => {4327 .complete, .codegen_failure_retryable => {
4324 switch (func.state) {4328 switch (func.state) {
4325 .sema_failure, .dependency_failure => return error.AnalysisFail,4329 .sema_failure, .dependency_failure => return error.AnalysisFail,
4326 .queued => {},4330 .none, .queued => {},
4327 .in_progress => unreachable,4331 .in_progress => unreachable,
4328 .inline_only => unreachable, // don't queue work for this4332 .inline_only => unreachable, // don't queue work for this
4329 .success => return,4333 .success => return,
...@@ -4426,6 +4430,60 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {...@@ -4426,6 +4430,60 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {
4426 }4430 }
4427}4431}
44284432
4433/// Ensure this function's body is or will be analyzed and emitted. This should
4434/// be called whenever a potential runtime call of a function is seen.
4435///
4436/// The caller is responsible for ensuring the function decl itself is already
4437/// analyzed, and for ensuring it can exist at runtime (see
4438/// `sema.fnHasRuntimeBits`). This function does *not* guarantee that the body
4439/// will be analyzed when it returns: for that, see `ensureFuncBodyAnalyzed`.
4440pub fn ensureFuncBodyAnalysisQueued(mod: *Module, func: *Fn) !void {
4441 const decl_index = func.owner_decl;
4442 const decl = mod.declPtr(decl_index);
4443
4444 switch (decl.analysis) {
4445 .unreferenced => unreachable,
4446 .in_progress => unreachable,
4447 .outdated => unreachable,
4448
4449 .file_failure,
4450 .sema_failure,
4451 .liveness_failure,
4452 .codegen_failure,
4453 .dependency_failure,
4454 .sema_failure_retryable,
4455 .codegen_failure_retryable,
4456 // The function analysis failed, but we've already emitted an error for
4457 // that. The callee doesn't need the function to be analyzed right now,
4458 // so its analysis can safely continue.
4459 => return,
4460
4461 .complete => {},
4462 }
4463
4464 assert(decl.has_tv);
4465
4466 switch (func.state) {
4467 .none => {},
4468 .queued => return,
4469 // As above, we don't need to forward errors here.
4470 .sema_failure, .dependency_failure => return,
4471 .in_progress => return,
4472 .inline_only => unreachable, // don't queue work for this
4473 .success => return,
4474 }
4475
4476 // Decl itself is safely analyzed, and body analysis is not yet queued
4477
4478 try mod.comp.work_queue.writeItem(.{ .codegen_func = func });
4479 if (mod.emit_h != null) {
4480 // TODO: we ideally only want to do this if the function's type changed
4481 // since the last update
4482 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index });
4483 }
4484 func.state = .queued;
4485}
4486
4429pub fn updateEmbedFile(mod: *Module, embed_file: *EmbedFile) SemaError!void {4487pub fn updateEmbedFile(mod: *Module, embed_file: *EmbedFile) SemaError!void {
4430 const tracy = trace(@src());4488 const tracy = trace(@src());
4431 defer tracy.end();4489 defer tracy.end();
...@@ -4733,20 +4791,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {...@@ -4733,20 +4791,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
4733 decl.analysis = .complete;4791 decl.analysis = .complete;
4734 decl.generation = mod.generation;4792 decl.generation = mod.generation;
47354793
4736 const has_runtime_bits = try sema.fnHasRuntimeBits(decl.ty);
4737
4738 if (has_runtime_bits) {
4739 // We don't fully codegen the decl until later, but we do need to reserve a global
4740 // offset table index for it. This allows us to codegen decls out of dependency
4741 // order, increasing how many computations can be done in parallel.
4742 try mod.comp.work_queue.writeItem(.{ .codegen_func = func });
4743 if (type_changed and mod.emit_h != null) {
4744 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index });
4745 }
4746 } else if (!prev_is_inline and prev_type_has_bits) {
4747 mod.comp.bin_file.freeDecl(decl_index);
4748 }
4749
4750 const is_inline = decl.ty.fnCallingConvention() == .Inline;4794 const is_inline = decl.ty.fnCallingConvention() == .Inline;
4751 if (decl.is_exported) {4795 if (decl.is_exported) {
4752 const export_src: LazySrcLoc = .{ .token_offset = @boolToInt(decl.is_pub) };4796 const export_src: LazySrcLoc = .{ .token_offset = @boolToInt(decl.is_pub) };
src/Sema.zig+36-5
...@@ -2452,6 +2452,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE...@@ -2452,6 +2452,7 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
2452 .@"align" = iac.data.alignment,2452 .@"align" = iac.data.alignment,
2453 .@"addrspace" = addr_space,2453 .@"addrspace" = addr_space,
2454 });2454 });
2455 try sema.maybeQueueFuncBodyAnalysis(iac.data.decl_index);
2455 return sema.addConstant(2456 return sema.addConstant(
2456 ptr_ty,2457 ptr_ty,
2457 try Value.Tag.decl_ref_mut.create(sema.arena, .{2458 try Value.Tag.decl_ref_mut.create(sema.arena, .{
...@@ -3709,6 +3710,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com...@@ -3709,6 +3710,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
3709 const final_ptr_ty_inst = try sema.addType(final_ptr_ty);3710 const final_ptr_ty_inst = try sema.addType(final_ptr_ty);
3710 sema.air_instructions.items(.data)[ptr_inst].ty_pl.ty = final_ptr_ty_inst;3711 sema.air_instructions.items(.data)[ptr_inst].ty_pl.ty = final_ptr_ty_inst;
37113712
3713 try sema.maybeQueueFuncBodyAnalysis(decl_index);
3712 if (var_is_mut) {3714 if (var_is_mut) {
3713 sema.air_values.items[value_index] = try Value.Tag.decl_ref_mut.create(sema.arena, .{3715 sema.air_values.items[value_index] = try Value.Tag.decl_ref_mut.create(sema.arena, .{
3714 .decl_index = decl_index,3716 .decl_index = decl_index,
...@@ -3809,6 +3811,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com...@@ -3809,6 +3811,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
3809 // Even though we reuse the constant instruction, we still remove it from the3811 // Even though we reuse the constant instruction, we still remove it from the
3810 // block so that codegen does not see it.3812 // block so that codegen does not see it.
3811 block.instructions.shrinkRetainingCapacity(search_index);3813 block.instructions.shrinkRetainingCapacity(search_index);
3814 try sema.maybeQueueFuncBodyAnalysis(new_decl_index);
3812 sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, new_decl_index);3815 sema.air_values.items[value_index] = try Value.Tag.decl_ref.create(sema.arena, new_decl_index);
3813 // if bitcast ty ref needs to be made const, make_ptr_const3816 // if bitcast ty ref needs to be made const, make_ptr_const
3814 // ZIR handles it later, so we can just use the ty ref here.3817 // ZIR handles it later, so we can just use the ty ref here.
...@@ -5747,6 +5750,7 @@ pub fn analyzeExport(...@@ -5747,6 +5750,7 @@ pub fn analyzeExport(
57475750
5748 // This decl is alive no matter what, since it's being exported5751 // This decl is alive no matter what, since it's being exported
5749 mod.markDeclAlive(exported_decl);5752 mod.markDeclAlive(exported_decl);
5753 try sema.maybeQueueFuncBodyAnalysis(exported_decl_index);
57505754
5751 const gpa = mod.gpa;5755 const gpa = mod.gpa;
57525756
...@@ -7068,6 +7072,12 @@ fn analyzeCall(...@@ -7068,6 +7072,12 @@ fn analyzeCall(
7068 sema.owner_func.?.calls_or_awaits_errorable_fn = true;7072 sema.owner_func.?.calls_or_awaits_errorable_fn = true;
7069 }7073 }
70707074
7075 if (try sema.resolveMaybeUndefVal(func)) |func_val| {
7076 if (func_val.castTag(.function)) |func_obj| {
7077 try sema.mod.ensureFuncBodyAnalysisQueued(func_obj.data);
7078 }
7079 }
7080
7071 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +7081 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +
7072 args.len);7082 args.len);
7073 const func_inst = try block.addInst(.{7083 const func_inst = try block.addInst(.{
...@@ -7585,6 +7595,8 @@ fn instantiateGenericCall(...@@ -7585,6 +7595,8 @@ fn instantiateGenericCall(
7585 sema.owner_func.?.calls_or_awaits_errorable_fn = true;7595 sema.owner_func.?.calls_or_awaits_errorable_fn = true;
7586 }7596 }
75877597
7598 try sema.mod.ensureFuncBodyAnalysisQueued(callee);
7599
7588 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len +7600 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len +
7589 runtime_args_len);7601 runtime_args_len);
7590 const result = try block.addInst(.{7602 const result = try block.addInst(.{
...@@ -9143,7 +9155,7 @@ fn funcCommon(...@@ -9143,7 +9155,7 @@ fn funcCommon(
9143 }9155 }
91449156
9145 const is_inline = fn_ty.fnCallingConvention() == .Inline;9157 const is_inline = fn_ty.fnCallingConvention() == .Inline;
9146 const anal_state: Module.Fn.Analysis = if (is_inline) .inline_only else .queued;9158 const anal_state: Module.Fn.Analysis = if (is_inline) .inline_only else .none;
91479159
9148 const comptime_args: ?[*]TypedValue = if (sema.comptime_args_fn_inst == func_inst) blk: {9160 const comptime_args: ?[*]TypedValue = if (sema.comptime_args_fn_inst == func_inst) blk: {
9149 break :blk if (sema.comptime_args.len == 0) null else sema.comptime_args.ptr;9161 break :blk if (sema.comptime_args.len == 0) null else sema.comptime_args.ptr;
...@@ -24279,9 +24291,7 @@ fn fieldCallBind(...@@ -24279,9 +24291,7 @@ fn fieldCallBind(
24279 if (concrete_ty.getNamespace()) |namespace| {24291 if (concrete_ty.getNamespace()) |namespace| {
24280 if (try sema.namespaceLookup(block, src, namespace, field_name)) |decl_idx| {24292 if (try sema.namespaceLookup(block, src, namespace, field_name)) |decl_idx| {
24281 try sema.addReferencedBy(block, src, decl_idx);24293 try sema.addReferencedBy(block, src, decl_idx);
24282 const inst = try sema.analyzeDeclRef(decl_idx);24294 const decl_val = try sema.analyzeDeclVal(block, src, decl_idx);
24283
24284 const decl_val = try sema.analyzeLoad(block, src, inst, src);
24285 const decl_type = sema.typeOf(decl_val);24295 const decl_type = sema.typeOf(decl_val);
24286 if (decl_type.zigTypeTag() == .Fn and24296 if (decl_type.zigTypeTag() == .Fn and
24287 decl_type.fnParamLen() >= 1)24297 decl_type.fnParamLen() >= 1)
...@@ -28911,7 +28921,7 @@ fn analyzeDeclVal(...@@ -28911,7 +28921,7 @@ fn analyzeDeclVal(
28911 if (sema.decl_val_table.get(decl_index)) |result| {28921 if (sema.decl_val_table.get(decl_index)) |result| {
28912 return result;28922 return result;
28913 }28923 }
28914 const decl_ref = try sema.analyzeDeclRef(decl_index);28924 const decl_ref = try sema.analyzeDeclRefInner(decl_index, false);
28915 const result = try sema.analyzeLoad(block, src, decl_ref, src);28925 const result = try sema.analyzeLoad(block, src, decl_ref, src);
28916 if (Air.refToIndex(result)) |index| {28926 if (Air.refToIndex(result)) |index| {
28917 if (sema.air_instructions.items(.tag)[index] == .constant and !block.is_typeof) {28927 if (sema.air_instructions.items(.tag)[index] == .constant and !block.is_typeof) {
...@@ -28970,6 +28980,7 @@ fn refValue(sema: *Sema, block: *Block, ty: Type, val: Value) !Value {...@@ -28970,6 +28980,7 @@ fn refValue(sema: *Sema, block: *Block, ty: Type, val: Value) !Value {
28970 try val.copy(anon_decl.arena()),28980 try val.copy(anon_decl.arena()),
28971 0, // default alignment28981 0, // default alignment
28972 );28982 );
28983 try sema.maybeQueueFuncBodyAnalysis(decl);
28973 try sema.mod.declareDeclDependency(sema.owner_decl_index, decl);28984 try sema.mod.declareDeclDependency(sema.owner_decl_index, decl);
28974 return try Value.Tag.decl_ref.create(sema.arena, decl);28985 return try Value.Tag.decl_ref.create(sema.arena, decl);
28975}28986}
...@@ -28982,6 +28993,14 @@ fn optRefValue(sema: *Sema, block: *Block, ty: Type, opt_val: ?Value) !Value {...@@ -28982,6 +28993,14 @@ fn optRefValue(sema: *Sema, block: *Block, ty: Type, opt_val: ?Value) !Value {
28982}28993}
2898328994
28984fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref {28995fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref {
28996 return sema.analyzeDeclRefInner(decl_index, true);
28997}
28998
28999/// Analyze a reference to the decl at the given index. Ensures the underlying decl is analyzed, but
29000/// only triggers analysis for function bodies if `analyze_fn_body` is true. If it's possible for a
29001/// decl_ref to end up in runtime code, the function body must be analyzed: `analyzeDeclRef` wraps
29002/// this function with `analyze_fn_body` set to true.
29003fn analyzeDeclRefInner(sema: *Sema, decl_index: Decl.Index, analyze_fn_body: bool) CompileError!Air.Inst.Ref {
28985 try sema.mod.declareDeclDependency(sema.owner_decl_index, decl_index);29004 try sema.mod.declareDeclDependency(sema.owner_decl_index, decl_index);
28986 try sema.ensureDeclAnalyzed(decl_index);29005 try sema.ensureDeclAnalyzed(decl_index);
2898729006
...@@ -28997,6 +29016,9 @@ fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref...@@ -28997,6 +29016,9 @@ fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref
28997 });29016 });
28998 return sema.addConstant(ty, try Value.Tag.decl_ref.create(sema.arena, decl_index));29017 return sema.addConstant(ty, try Value.Tag.decl_ref.create(sema.arena, decl_index));
28999 }29018 }
29019 if (analyze_fn_body) {
29020 try sema.maybeQueueFuncBodyAnalysis(decl_index);
29021 }
29000 return sema.addConstant(29022 return sema.addConstant(
29001 try Type.ptr(sema.arena, sema.mod, .{29023 try Type.ptr(sema.arena, sema.mod, .{
29002 .pointee_type = decl_tv.ty,29024 .pointee_type = decl_tv.ty,
...@@ -29008,6 +29030,15 @@ fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref...@@ -29008,6 +29030,15 @@ fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref
29008 );29030 );
29009}29031}
2901029032
29033fn maybeQueueFuncBodyAnalysis(sema: *Sema, decl_index: Decl.Index) !void {
29034 const decl = sema.mod.declPtr(decl_index);
29035 const tv = try decl.typedValue();
29036 if (tv.ty.zigTypeTag() != .Fn) return;
29037 if (!try sema.fnHasRuntimeBits(tv.ty)) return;
29038 const func = tv.val.castTag(.function) orelse return; // undef or extern_fn
29039 try sema.mod.ensureFuncBodyAnalysisQueued(func.data);
29040}
29041
29011fn analyzeRef(29042fn analyzeRef(
29012 sema: *Sema,29043 sema: *Sema,
29013 block: *Block,29044 block: *Block,
src/type.zig+1-1
...@@ -6802,7 +6802,7 @@ pub const Type = extern union {...@@ -6802,7 +6802,7 @@ pub const Type = extern union {
68026802
6803 comptime {6803 comptime {
6804 if (builtin.mode == .Debug) {6804 if (builtin.mode == .Debug) {
6805 _ = dbHelper;6805 _ = &dbHelper;
6806 }6806 }
6807 }6807 }
6808};6808};
src/value.zig+1-1
...@@ -5709,7 +5709,7 @@ pub const Value = extern union {...@@ -5709,7 +5709,7 @@ pub const Value = extern union {
57095709
5710 comptime {5710 comptime {
5711 if (builtin.mode == .Debug) {5711 if (builtin.mode == .Debug) {
5712 _ = dbHelper;5712 _ = &dbHelper;
5713 }5713 }
5714 }5714 }
5715};5715};
test/behavior/sizeof_and_typeof.zig+1-1
...@@ -48,7 +48,7 @@ fn fn1(alpha: bool) void {...@@ -48,7 +48,7 @@ fn fn1(alpha: bool) void {
48}48}
4949
50test "lazy @sizeOf result is checked for definedness" {50test "lazy @sizeOf result is checked for definedness" {
51 _ = fn1;51 _ = &fn1;
52}52}
5353
54const A = struct {54const A = struct {
test/cases/compile_errors/closure_get_depends_on_failed_decl.zig+1-1
...@@ -3,7 +3,7 @@ pub inline fn instanceRequestAdapter() void {}...@@ -3,7 +3,7 @@ pub inline fn instanceRequestAdapter() void {}
3pub inline fn requestAdapter(3pub inline fn requestAdapter(
4 comptime callbackArg: fn () callconv(.Inline) void,4 comptime callbackArg: fn () callconv(.Inline) void,
5) void {5) void {
6 _ = (struct {6 _ = &(struct {
7 pub fn callback() callconv(.C) void {7 pub fn callback() callconv(.C) void {
8 callbackArg();8 callbackArg();
9 }9 }
test/cases/compile_errors/compileLog_of_tagged_enum_doesnt_crash_the_compiler.zig+3-2
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1const Bar = union(enum(u32)) {1const Bar = union(enum(u32)) {
2 X: i32 = 12 X: i32 = 1,
3};3};
44
5fn testCompileLog(x: Bar) void {5fn testCompileLog(x: Bar) void {
...@@ -7,7 +7,8 @@ fn testCompileLog(x: Bar) void {...@@ -7,7 +7,8 @@ fn testCompileLog(x: Bar) void {
7}7}
88
9pub export fn entry() void {9pub export fn entry() void {
10 comptime testCompileLog(Bar{.X = 123});10 comptime testCompileLog(Bar{ .X = 123 });
11 _ = &testCompileLog;
11}12}
1213
13// error14// error
test/cases/compile_errors/compile_log.zig+6-5
...@@ -1,10 +1,11 @@...@@ -1,10 +1,11 @@
1export fn foo() void {1export fn foo() void {
2 comptime bar(12, "hi",);2 comptime bar(12, "hi");
3 _ = &bar;
3}4}
4fn bar(a: i32, b: []const u8) void {5fn bar(a: i32, b: []const u8) void {
5 @compileLog("begin",);6 @compileLog("begin");
6 @compileLog("a", a, "b", b);7 @compileLog("a", a, "b", b);
7 @compileLog("end",);8 @compileLog("end");
8}9}
9export fn baz() void {10export fn baz() void {
10 const S = struct { a: u32 };11 const S = struct { a: u32 };
...@@ -15,8 +16,8 @@ export fn baz() void {...@@ -15,8 +16,8 @@ export fn baz() void {
15// backend=llvm16// backend=llvm
16// target=native17// target=native
17//18//
18// :5:5: error: found compile log statement19// :6:5: error: found compile log statement
19// :11:5: note: also here20// :12:5: note: also here
20//21//
21// Compile Log Output:22// Compile Log Output:
22// @as(*const [5:0]u8, "begin")23// @as(*const [5:0]u8, "begin")
test/cases/compile_errors/dereference_slice.zig+1-1
...@@ -2,7 +2,7 @@ fn entry(x: []i32) i32 {...@@ -2,7 +2,7 @@ fn entry(x: []i32) i32 {
2 return x.*;2 return x.*;
3}3}
4comptime {4comptime {
5 _ = entry;5 _ = &entry;
6}6}
77
8// error8// error
test/cases/compile_errors/extern_function_with_comptime_parameter.zig+3-3
...@@ -4,9 +4,9 @@ fn f() i32 {...@@ -4,9 +4,9 @@ fn f() i32 {
4}4}
5pub extern fn entry1(b: u32, comptime a: [2]u8, c: i32) void;5pub extern fn entry1(b: u32, comptime a: [2]u8, c: i32) void;
6pub extern fn entry2(b: u32, noalias a: anytype, i43) void;6pub extern fn entry2(b: u32, noalias a: anytype, i43) void;
7comptime { _ = f; }7comptime { _ = &f; }
8comptime { _ = entry1; }8comptime { _ = &entry1; }
9comptime { _ = entry2; }9comptime { _ = &entry2; }
1010
11// error11// error
12// backend=stage212// backend=stage2
test/cases/compile_errors/invalid_address_space_coercion.zig+1-1
...@@ -2,7 +2,7 @@ fn entry(a: *addrspace(.gs) i32) *i32 {...@@ -2,7 +2,7 @@ fn entry(a: *addrspace(.gs) i32) *i32 {
2 return a;2 return a;
3}3}
4pub fn main() void {4pub fn main() void {
5 _ = entry;5 _ = &entry;
6}6}
77
8// error8// error
test/cases/compile_errors/invalid_pointer_keeps_address_space_when_taking_address_of_dereference.zig+1-1
...@@ -2,7 +2,7 @@ fn entry(a: *addrspace(.gs) i32) *i32 {...@@ -2,7 +2,7 @@ fn entry(a: *addrspace(.gs) i32) *i32 {
2 return &a.*;2 return &a.*;
3}3}
4pub fn main() void {4pub fn main() void {
5 _ = entry;5 _ = &entry;
6}6}
77
8// error8// error
test/cases/compile_errors/noalias_on_non_pointer_param.zig+2-2
...@@ -2,10 +2,10 @@ fn f(noalias x: i32) void { _ = x; }...@@ -2,10 +2,10 @@ fn f(noalias x: i32) void { _ = x; }
2export fn entry() void { f(1234); }2export fn entry() void { f(1234); }
33
4fn generic(comptime T: type, noalias _: [*]T, noalias _: [*]const T, _: usize) void {}4fn generic(comptime T: type, noalias _: [*]T, noalias _: [*]const T, _: usize) void {}
5comptime { _ = generic; }5comptime { _ = &generic; }
66
7fn slice(noalias _: []u8) void {}7fn slice(noalias _: []u8) void {}
8comptime { _ = slice; }8comptime { _ = &slice; }
99
10// error10// error
11// backend=stage211// backend=stage2
test/cases/compile_errors/pointer_with_different_address_spaces.zig+1-1
...@@ -2,7 +2,7 @@ fn entry(a: *addrspace(.gs) i32) *addrspace(.fs) i32 {...@@ -2,7 +2,7 @@ fn entry(a: *addrspace(.gs) i32) *addrspace(.fs) i32 {
2 return a;2 return a;
3}3}
4export fn entry2() void {4export fn entry2() void {
5 _ = entry;5 _ = &entry;
6}6}
77
8// error8// error
test/cases/compile_errors/pointers_with_different_address_spaces.zig+1-1
...@@ -2,7 +2,7 @@ fn entry(a: ?*addrspace(.gs) i32) *i32 {...@@ -2,7 +2,7 @@ fn entry(a: ?*addrspace(.gs) i32) *i32 {
2 return a.?;2 return a.?;
3}3}
4pub fn main() void {4pub fn main() void {
5 _ = entry;5 _ = &entry;
6}6}
77
8// error8// error
test/cases/compile_errors/slice_sentinel_mismatch-2.zig+1-1
...@@ -2,7 +2,7 @@ fn foo() [:0]u8 {...@@ -2,7 +2,7 @@ fn foo() [:0]u8 {
2 var x: []u8 = undefined;2 var x: []u8 = undefined;
3 return x;3 return x;
4}4}
5comptime { _ = foo; }5comptime { _ = &foo; }
66
7// error7// error
8// backend=stage28// backend=stage2
test/cases/llvm/address_space_pointer_access_chaining_pointer_to_optional_array.zig+1-1
...@@ -2,7 +2,7 @@ fn entry(a: *addrspace(.gs) ?[1]i32) *addrspace(.gs) i32 {...@@ -2,7 +2,7 @@ fn entry(a: *addrspace(.gs) ?[1]i32) *addrspace(.gs) i32 {
2 return &a.*.?[0];2 return &a.*.?[0];
3}3}
4pub fn main() void {4pub fn main() void {
5 _ = entry;5 _ = &entry;
6}6}
77
8// compile8// compile
test/cases/llvm/address_spaces_pointer_access_chaining_array_pointer.zig+1-1
...@@ -2,7 +2,7 @@ fn entry(a: *addrspace(.gs) [1]i32) *addrspace(.gs) i32 {...@@ -2,7 +2,7 @@ fn entry(a: *addrspace(.gs) [1]i32) *addrspace(.gs) i32 {
2 return &a[0];2 return &a[0];
3}3}
4pub fn main() void {4pub fn main() void {
5 _ = entry;5 _ = &entry;
6}6}
77
8// compile8// compile
test/cases/llvm/address_spaces_pointer_access_chaining_complex.zig+1-1
...@@ -3,7 +3,7 @@ fn entry(a: *addrspace(.gs) [1]A) *addrspace(.gs) i32 {...@@ -3,7 +3,7 @@ fn entry(a: *addrspace(.gs) [1]A) *addrspace(.gs) i32 {
3 return &a[0].a.?[0];3 return &a[0].a.?[0];
4}4}
5pub fn main() void {5pub fn main() void {
6 _ = entry;6 _ = &entry;
7}7}
88
9// compile9// compile
test/cases/llvm/address_spaces_pointer_access_chaining_struct_pointer.zig+1-1
...@@ -3,7 +3,7 @@ fn entry(a: *addrspace(.gs) A) *addrspace(.gs) i32 {...@@ -3,7 +3,7 @@ fn entry(a: *addrspace(.gs) A) *addrspace(.gs) i32 {
3 return &a.a;3 return &a.a;
4}4}
5pub fn main() void {5pub fn main() void {
6 _ = entry;6 _ = &entry;
7}7}
88
9// compile9// compile
test/cases/llvm/dereferencing_though_multiple_pointers_with_address_spaces.zig+1-1
...@@ -2,7 +2,7 @@ fn entry(a: *addrspace(.fs) *addrspace(.gs) *i32) *i32 {...@@ -2,7 +2,7 @@ fn entry(a: *addrspace(.fs) *addrspace(.gs) *i32) *i32 {
2 return a.*.*;2 return a.*.*;
3}3}
4pub fn main() void {4pub fn main() void {
5 _ = entry;5 _ = &entry;
6}6}
77
8// compile8// compile
test/cases/llvm/pointer_keeps_address_space.zig+1-1
...@@ -2,7 +2,7 @@ fn entry(a: *addrspace(.gs) i32) *addrspace(.gs) i32 {...@@ -2,7 +2,7 @@ fn entry(a: *addrspace(.gs) i32) *addrspace(.gs) i32 {
2 return a;2 return a;
3}3}
4pub fn main() void {4pub fn main() void {
5 _ = entry;5 _ = &entry;
6}6}
77
8// compile8// compile
test/cases/llvm/pointer_keeps_address_space_when_taking_address_of_dereference.zig+1-1
...@@ -2,7 +2,7 @@ fn entry(a: *addrspace(.gs) i32) *addrspace(.gs) i32 {...@@ -2,7 +2,7 @@ fn entry(a: *addrspace(.gs) i32) *addrspace(.gs) i32 {
2 return &a.*;2 return &a.*;
3}3}
4pub fn main() void {4pub fn main() void {
5 _ = entry;5 _ = &entry;
6}6}
77
8// compile8// compile
test/cases/llvm/pointer_to_explicit_generic_address_space_coerces_to_implicit_pointer.zig+1-1
...@@ -2,7 +2,7 @@ fn entry(a: *addrspace(.generic) i32) *i32 {...@@ -2,7 +2,7 @@ fn entry(a: *addrspace(.generic) i32) *i32 {
2 return a;2 return a;
3}3}
4pub fn main() void {4pub fn main() void {
5 _ = entry;5 _ = &entry;
6}6}
77
8// compile8// compile
test/link/wasm/type/build.zig+2-2
...@@ -26,10 +26,10 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize...@@ -26,10 +26,10 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize
2626
27 const check_lib = lib.checkObject();27 const check_lib = lib.checkObject();
28 check_lib.checkStart("Section type");28 check_lib.checkStart("Section type");
29 // only 3 entries, although we have more functions.29 // only 2 entries, although we have more functions.
30 // This is to test functions with the same function signature30 // This is to test functions with the same function signature
31 // have their types deduplicated.31 // have their types deduplicated.
32 check_lib.checkNext("entries 3");32 check_lib.checkNext("entries 2");
33 check_lib.checkNext("params 1");33 check_lib.checkNext("params 1");
34 check_lib.checkNext("type i32");34 check_lib.checkNext("type i32");
35 check_lib.checkNext("returns 1");35 check_lib.checkNext("returns 1");