| author | |
| committer | |
| log | 4976b58ab16069f8d3267b69ed030f29685c1abe |
| tree | 400f632d11eec4f3c330bee15d59f8fd6219c20f |
| parent | b5fad3a40a86eb379903d6a803bdbe66dcaa5487 |
| signature |
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: #1535333 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. |
| 13 | 13 | ||
| 14 | comptime { | 14 | comptime { |
| 15 | _ = clear_cache; | 15 | _ = &clear_cache; |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | fn clear_cache(start: usize, end: usize) callconv(.C) void { | 18 | fn 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; |
| 1959 | pub const ParseFloatError = @import("fmt/parse_float.zig").ParseFloatError; | 1959 | pub const ParseFloatError = @import("fmt/parse_float.zig").ParseFloatError; |
| 1960 | 1960 | ||
| 1961 | test { | 1961 | test { |
| 1962 | _ = parseFloat; | 1962 | _ = &parseFloat; |
| 1963 | } | 1963 | } |
| 1964 | 1964 | ||
| 1965 | pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { | 1965 | pub 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 |
| 3150 | 3150 | ||
| 3151 | test { | 3151 | test { |
| 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( |
| 1605 | 1605 | ||
| 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 { |
| 532 | 532 | ||
| 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 | } |
| 709 | 709 | ||
| 710 | test "sync" { | 710 | test "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 |
| 1116 | pub fn refAllDecls(comptime T: type) void { | 1116 | pub 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 | } |
| 1122 | 1122 | ||
| ... | @@ -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 = .{}, |
| 1639 | 1639 | ||
| 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 marked | 1646 | /// 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 this | 4332 | .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 | } |
| 4428 | 4432 | ||
| 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`. | ||
| 4440 | pub 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 | |||
| 4429 | pub fn updateEmbedFile(mod: *Module, embed_file: *EmbedFile) SemaError!void { | 4487 | pub 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; |
| 4735 | 4793 | ||
| 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; |
| 3711 | 3712 | ||
| 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 the | 3811 | // 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_const | 3816 | // 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( |
| 5747 | 5750 | ||
| 5748 | // This decl is alive no matter what, since it's being exported | 5751 | // 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); | ||
| 5750 | 5754 | ||
| 5751 | const gpa = mod.gpa; | 5755 | const gpa = mod.gpa; |
| 5752 | 5756 | ||
| ... | @@ -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 | } |
| 7070 | 7074 | ||
| 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 | } |
| 7587 | 7597 | ||
| 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 | } |
| 9144 | 9156 | ||
| 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; |
| 9147 | 9159 | ||
| 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 and | 24296 | 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 alignment | 28981 | 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 | } |
| 28983 | 28994 | ||
| 28984 | fn analyzeDeclRef(sema: *Sema, decl_index: Decl.Index) CompileError!Air.Inst.Ref { | 28995 | fn 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. | ||
| 29003 | fn 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); |
| 28987 | 29006 | ||
| ... | @@ -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 | } |
| 29010 | 29032 | ||
| 29033 | fn 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 | |||
| 29011 | fn analyzeRef( | 29042 | fn 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 { |
| 6802 | 6802 | ||
| 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 { |
| 5709 | 5709 | ||
| 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 | } |
| 49 | 49 | ||
| 50 | test "lazy @sizeOf result is checked for definedness" { | 50 | test "lazy @sizeOf result is checked for definedness" { |
| 51 | _ = fn1; | 51 | _ = &fn1; |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | const A = struct { | 54 | const 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 {} |
| 3 | pub inline fn requestAdapter( | 3 | pub 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 @@ |
| 1 | const Bar = union(enum(u32)) { | 1 | const Bar = union(enum(u32)) { |
| 2 | X: i32 = 1 | 2 | X: i32 = 1, |
| 3 | }; | 3 | }; |
| 4 | 4 | ||
| 5 | fn testCompileLog(x: Bar) void { | 5 | fn testCompileLog(x: Bar) void { |
| ... | @@ -7,7 +7,8 @@ fn testCompileLog(x: Bar) void { | ... | @@ -7,7 +7,8 @@ fn testCompileLog(x: Bar) void { |
| 7 | } | 7 | } |
| 8 | 8 | ||
| 9 | pub export fn entry() void { | 9 | pub export fn entry() void { |
| 10 | comptime testCompileLog(Bar{.X = 123}); | 10 | comptime testCompileLog(Bar{ .X = 123 }); |
| 11 | _ = &testCompileLog; | ||
| 11 | } | 12 | } |
| 12 | 13 | ||
| 13 | // error | 14 | // error |
test/cases/compile_errors/compile_log.zig+6-5| ... | @@ -1,10 +1,11 @@ | ... | @@ -1,10 +1,11 @@ |
| 1 | export fn foo() void { | 1 | export fn foo() void { |
| 2 | comptime bar(12, "hi",); | 2 | comptime bar(12, "hi"); |
| 3 | _ = &bar; | ||
| 3 | } | 4 | } |
| 4 | fn bar(a: i32, b: []const u8) void { | 5 | fn 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 | } |
| 9 | export fn baz() void { | 10 | export 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=llvm | 16 | // backend=llvm |
| 16 | // target=native | 17 | // target=native |
| 17 | // | 18 | // |
| 18 | // :5:5: error: found compile log statement | 19 | // :6:5: error: found compile log statement |
| 19 | // :11:5: note: also here | 20 | // :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 | } |
| 4 | comptime { | 4 | comptime { |
| 5 | _ = entry; | 5 | _ = &entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // error | 8 | // 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 | } |
| 5 | pub extern fn entry1(b: u32, comptime a: [2]u8, c: i32) void; | 5 | pub extern fn entry1(b: u32, comptime a: [2]u8, c: i32) void; |
| 6 | pub extern fn entry2(b: u32, noalias a: anytype, i43) void; | 6 | pub extern fn entry2(b: u32, noalias a: anytype, i43) void; |
| 7 | comptime { _ = f; } | 7 | comptime { _ = &f; } |
| 8 | comptime { _ = entry1; } | 8 | comptime { _ = &entry1; } |
| 9 | comptime { _ = entry2; } | 9 | comptime { _ = &entry2; } |
| 10 | 10 | ||
| 11 | // error | 11 | // error |
| 12 | // backend=stage2 | 12 | // 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 | } |
| 4 | pub fn main() void { | 4 | pub fn main() void { |
| 5 | _ = entry; | 5 | _ = &entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // error | 8 | // 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 | } |
| 4 | pub fn main() void { | 4 | pub fn main() void { |
| 5 | _ = entry; | 5 | _ = &entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // error | 8 | // 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; } |
| 2 | export fn entry() void { f(1234); } | 2 | export fn entry() void { f(1234); } |
| 3 | 3 | ||
| 4 | fn generic(comptime T: type, noalias _: [*]T, noalias _: [*]const T, _: usize) void {} | 4 | fn generic(comptime T: type, noalias _: [*]T, noalias _: [*]const T, _: usize) void {} |
| 5 | comptime { _ = generic; } | 5 | comptime { _ = &generic; } |
| 6 | 6 | ||
| 7 | fn slice(noalias _: []u8) void {} | 7 | fn slice(noalias _: []u8) void {} |
| 8 | comptime { _ = slice; } | 8 | comptime { _ = &slice; } |
| 9 | 9 | ||
| 10 | // error | 10 | // error |
| 11 | // backend=stage2 | 11 | // 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 | } |
| 4 | export fn entry2() void { | 4 | export fn entry2() void { |
| 5 | _ = entry; | 5 | _ = &entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // error | 8 | // 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 | } |
| 4 | pub fn main() void { | 4 | pub fn main() void { |
| 5 | _ = entry; | 5 | _ = &entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // error | 8 | // 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 | } |
| 5 | comptime { _ = foo; } | 5 | comptime { _ = &foo; } |
| 6 | 6 | ||
| 7 | // error | 7 | // error |
| 8 | // backend=stage2 | 8 | // 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 | } |
| 4 | pub fn main() void { | 4 | pub fn main() void { |
| 5 | _ = entry; | 5 | _ = &entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // compile | 8 | // 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 | } |
| 4 | pub fn main() void { | 4 | pub fn main() void { |
| 5 | _ = entry; | 5 | _ = &entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // compile | 8 | // 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 | } |
| 5 | pub fn main() void { | 5 | pub fn main() void { |
| 6 | _ = entry; | 6 | _ = &entry; |
| 7 | } | 7 | } |
| 8 | 8 | ||
| 9 | // compile | 9 | // 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 | } |
| 5 | pub fn main() void { | 5 | pub fn main() void { |
| 6 | _ = entry; | 6 | _ = &entry; |
| 7 | } | 7 | } |
| 8 | 8 | ||
| 9 | // compile | 9 | // 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 | } |
| 4 | pub fn main() void { | 4 | pub fn main() void { |
| 5 | _ = entry; | 5 | _ = &entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // compile | 8 | // 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 | } |
| 4 | pub fn main() void { | 4 | pub fn main() void { |
| 5 | _ = entry; | 5 | _ = &entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // compile | 8 | // 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 | } |
| 4 | pub fn main() void { | 4 | pub fn main() void { |
| 5 | _ = entry; | 5 | _ = &entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // compile | 8 | // 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 | } |
| 4 | pub fn main() void { | 4 | pub fn main() void { |
| 5 | _ = entry; | 5 | _ = &entry; |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | // compile | 8 | // 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 |
| 26 | 26 | ||
| 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 signature | 30 | // 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"); |