From c9930375afecb987c0f0370791b2e1d984486dee Mon Sep 17 00:00:00 2001 From: Corendos Date: Wed, 3 Jan 2024 14:06:21 +0100 Subject: [PATCH] feat: @callerSrc() now returns an array This array contains the SourceLocation for the whole callstack. --- src/InternPool.zig | 22 ++++++ src/Sema.zig | 138 +++++++++++++++-------------------- test/behavior/caller_src.zig | 13 ++-- 3 files changed, 88 insertions(+), 85 deletions(-) diff --git a/src/InternPool.zig b/src/InternPool.zig index e06597373e41..50633af5d358 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -7692,6 +7692,28 @@ pub fn getOrPutStringOpt( return interned.toOptional(); } +pub fn getOrPutStringExtern(ip: *InternPool, gpa: Allocator, s: []const u8) Allocator.Error!Index { + const array_ty = try ip.get(gpa, .{ .array_type = .{ + .len = s.len, + .sentinel = .zero_u8, + .child = .u8_type, + } }); + return try ip.get(gpa, .{ .ptr = .{ + .ty = .slice_const_u8_sentinel_0_type, + .len = try ip.get(gpa, .{ .int = .{ + .ty = .usize_type, + .storage = .{ .u64 = s.len }, + } }), + .addr = .{ .anon_decl = .{ + .orig_ty = .slice_const_u8_sentinel_0_type, + .val = try ip.get(gpa, .{ .aggregate = .{ + .ty = array_ty, + .storage = .{ .bytes = s }, + } }), + } }, + } }); +} + /// Uses the last len bytes of ip.string_bytes as the key. pub fn getOrPutTrailingString( ip: *InternPool, diff --git a/src/Sema.zig b/src/Sema.zig index 69ef1d2d757d..5d614e98db5f 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -16889,104 +16889,84 @@ fn zirBuiltinCallerSrc( const ip = &mod.intern_pool; const gpa = sema.gpa; - // Declare default values for the case where we are not able to retrieve caller location. - var filename: []const u8 = ""; - var function_name: []const u8 = ""; - var line: usize = 0; - var column: usize = 0; - - const referenced_by = if (sema.owner_func_index != .none) + var referenced_by = if (sema.owner_func_index != .none) mod.funcOwnerDeclIndex(sema.owner_func_index) else sema.owner_decl_index; - // Try to get our caller block. - if (mod.reference_table.get(referenced_by)) |caller| ref: { - // Retrieve the assocaited declaration. - const decl = mod.declPtr(caller.referencer); + // This will contain the interned SourceLocation values. + var reference_stack = std.ArrayList(InternPool.Index).init(gpa); + defer reference_stack.deinit(); + + // Avoid infinite loops. + var seen = std.AutoHashMap(InternPool.DeclIndex, void).init(gpa); + defer seen.deinit(); + + const src_loc_ty = try sema.getBuiltinType("SourceLocation"); + + // Iterate blocks references. + while (mod.reference_table.get(referenced_by)) |ref| { + const gop = try seen.getOrPut(ref.referencer); + // Break if we have a loop. + if (gop.found_existing) break; + + // Retrieve the associated declaration. + const decl = mod.declPtr(ref.referencer); // Get the function name // NOTE(Corentin,@Hack): This might not be a function but SourceLocation is expecting a function name. - function_name = try sema.arena.dupe(u8, ip.stringToSlice(decl.name)); + const function_name = try sema.arena.dupeZ(u8, ip.stringToSlice(decl.name)); // Get the file name const file_scope = decl.getFileScope(mod); - filename = try file_scope.fullPathZ(sema.arena); + const filename = try file_scope.fullPathZ(sema.arena); - // Load or get the file source. - const file_source = file_scope.getSource(gpa) catch break :ref; + // Get the associated line and column. + const line, const column = b: { + // Load or get the file source. + const file_source = file_scope.getSource(gpa) catch break :b .{ 0, 0 }; - // Retrieve the location of the Decl as a Span. - const source_loc = caller.src.toSrcLoc(decl, mod); - const source_span = source_loc.span(gpa) catch break :ref; + // Retrieve the location of the Decl as a Span. + const source_loc = ref.src.toSrcLoc(decl, mod); + const source_span = source_loc.span(gpa) catch break :b .{ 0, 0 }; - // Compute the associated line and columm. - const reference_location = std.zig.findLineColumn(file_source.bytes, source_span.main); + // Compute the associated line and columm. + const reference_location = std.zig.findLineColumn(file_source.bytes, source_span.main); - // Update the line and column values. - line = reference_location.line + 1; - column = reference_location.column + 1; - } + const line = reference_location.line + 1; + const column = reference_location.column + 1; + break :b .{ line, column }; + }; - // Produce the SourceLocation - const func_name_val = v: { - // This dupe prevents InternPool string pool memory from being reallocated - // while a reference exists. - const bytes = function_name; - const array_ty = try ip.get(gpa, .{ .array_type = .{ - .len = bytes.len, - .sentinel = .zero_u8, - .child = .u8_type, - } }); - break :v try ip.get(gpa, .{ .ptr = .{ - .ty = .slice_const_u8_sentinel_0_type, - .len = (try mod.intValue(Type.usize, bytes.len)).toIntern(), - .addr = .{ .anon_decl = .{ - .orig_ty = .slice_const_u8_sentinel_0_type, - .val = try ip.get(gpa, .{ .aggregate = .{ - .ty = array_ty, - .storage = .{ .bytes = bytes }, - } }), - } }, - } }); - }; + // Append the interned SourceLocation to the stack. + const fields = .{ + // file: [:0]const u8, + try ip.getOrPutStringExtern(gpa, filename), + // fn_name: [:0]const u8, + try ip.getOrPutStringExtern(gpa, function_name), + // line: u32, + (try mod.intValue(Type.u32, line)).toIntern(), + // column: u32, + (try mod.intValue(Type.u32, column)).toIntern(), + }; - const file_name_val = v: { - // The compiler must not call realpath anywhere. - const bytes = filename; - const array_ty = try ip.get(gpa, .{ .array_type = .{ - .len = bytes.len, - .sentinel = .zero_u8, - .child = .u8_type, - } }); - break :v try ip.get(gpa, .{ .ptr = .{ - .ty = .slice_const_u8_sentinel_0_type, - .len = (try mod.intValue(Type.usize, bytes.len)).toIntern(), - .addr = .{ .anon_decl = .{ - .orig_ty = .slice_const_u8_sentinel_0_type, - .val = try ip.get(gpa, .{ .aggregate = .{ - .ty = array_ty, - .storage = .{ .bytes = bytes }, - } }), - } }, - } }); - }; + try reference_stack.append(try mod.intern(.{ .aggregate = .{ + .ty = src_loc_ty.toIntern(), + .storage = .{ .elems = &fields }, + } })); - const src_loc_ty = try sema.getBuiltinType("SourceLocation"); - const fields = .{ - // file: [:0]const u8, - file_name_val, - // fn_name: [:0]const u8, - func_name_val, - // line: u32, - (try mod.intValue(Type.u32, line)).toIntern(), - // column: u32, - (try mod.intValue(Type.u32, column)).toIntern(), - }; + referenced_by = ref.referencer; + } + + // Return the stack as an array. + const locations_ty = try mod.arrayType(.{ + .len = reference_stack.items.len, + .child = src_loc_ty.toIntern(), + }); return Air.internedToRef((try mod.intern(.{ .aggregate = .{ - .ty = src_loc_ty.toIntern(), - .storage = .{ .elems = &fields }, + .ty = locations_ty.toIntern(), + .storage = .{ .elems = try reference_stack.toOwnedSlice() }, } }))); } diff --git a/test/behavior/caller_src.zig b/test/behavior/caller_src.zig index 42ada49a00b1..0f36dde23092 100644 --- a/test/behavior/caller_src.zig +++ b/test/behavior/caller_src.zig @@ -1,14 +1,15 @@ const std = @import("std"); -fn callerSrcReporter() std.builtin.SourceLocation { +fn callerSrcReporter() []const std.builtin.SourceLocation { const caller = @callerSrc(); - return caller; + return caller[0..]; } test "@callerSrc() behavior" { - const loc = callerSrcReporter(); + const locs = callerSrcReporter(); - try std.testing.expectEqual(@as(usize, 9), loc.line); - try std.testing.expectEqual(@as(usize, 17), loc.column); - try std.testing.expectEqualStrings("test.@callerSrc() behavior", loc.fn_name); + try std.testing.expectEqual(@as(usize, 1), locs.len); + try std.testing.expectEqual(@as(usize, 9), locs[0].line); + try std.testing.expectEqual(@as(usize, 18), locs[0].column); + try std.testing.expectEqualStrings("test.@callerSrc() behavior", locs[0].fn_name); }