Commit 4296727050

mlugg <mlugg@mlugg.co.uk>
2025-05-10 19:22:43
Sema: improve "called from here" notes
To an average user, it may be unclear why these notes are not just in the reference trace; that's because they are more important, because they are inline calls through which comptime values may propagate. There are now 3 possible wordings for this note: * "called at comptime here" * "called inline here" * "generic function instantiated here" An alternative could be these wordings: * "while analyzing comptime call here" * "while analyzing inline call here" * "while analyzing generic instantiation here" I'm not sure which is better -- but this commit is certainly better than status quo.
1 parent d717c96
Changed files (1)
src/Sema.zig
@@ -444,13 +444,19 @@ pub const Block = struct {
     pub const Inlining = struct {
         call_block: *Block,
         call_src: LazySrcLoc,
-        has_comptime_args: bool,
         func: InternPool.Index,
-        comptime_result: Air.Inst.Ref,
-        merges: Merges,
+
         /// Populated lazily by `refFrame`.
         ref_frame: Zcu.InlineReferenceFrame.Index.Optional = .none,
 
+        /// If `true`, the following fields are `undefined`. This doesn't represent a true inline
+        /// call, but rather a generic call analyzing the instantiation's generic type bodies.
+        is_generic_instantiation: bool,
+
+        has_comptime_args: bool,
+        comptime_result: Air.Inst.Ref,
+        merges: Merges,
+
         fn refFrame(inlining: *Inlining, zcu: *Zcu) Allocator.Error!Zcu.InlineReferenceFrame.Index {
             if (inlining.ref_frame == .none) {
                 inlining.ref_frame = (try zcu.addInlineReferenceFrame(.{
@@ -2604,12 +2610,12 @@ pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg
     if (block) |start_block| {
         var block_it = start_block;
         while (block_it.inlining) |inlining| {
-            try sema.errNote(
-                inlining.call_src,
-                err_msg,
-                "called from here",
-                .{},
-            );
+            const note_str = note: {
+                if (inlining.is_generic_instantiation) break :note "generic function instantiated here";
+                if (inlining.call_block.isComptime()) break :note "called at comptime here";
+                break :note "called inline here";
+            };
+            try sema.errNote(inlining.call_src, err_msg, "{s}", .{note_str});
             block_it = inlining.call_block;
         }
     }
@@ -7736,9 +7742,10 @@ fn analyzeCall(
         .call_block = block,
         .call_src = call_src,
         .func = func_val.?.toIntern(),
-        .has_comptime_args = false, // unused by error reporting
-        .comptime_result = .none, // unused by error reporting
-        .merges = undefined, // unused because we'll never `return`
+        .is_generic_instantiation = true, // this allows the following fields to be `undefined`
+        .has_comptime_args = undefined,
+        .comptime_result = undefined,
+        .merges = undefined,
     } else undefined;
 
     // This is the block in which we evaluate generic function components: that is, generic parameter
@@ -8216,10 +8223,11 @@ fn analyzeCall(
     var inlining: Block.Inlining = .{
         .call_block = block,
         .call_src = call_src,
+        .func = func_val.?.toIntern(),
+        .is_generic_instantiation = false,
         .has_comptime_args = for (args) |a| {
             if (try sema.isComptimeKnown(a)) break true;
         } else false,
-        .func = func_val.?.toIntern(),
         .comptime_result = undefined,
         .merges = .{
             .block_inst = block_inst,
@@ -8250,7 +8258,10 @@ fn analyzeCall(
     if (!inlining.has_comptime_args) {
         var block_it = block;
         while (block_it.inlining) |parent_inlining| {
-            if (!parent_inlining.has_comptime_args and parent_inlining.func == func_val.?.toIntern()) {
+            if (!parent_inlining.is_generic_instantiation and
+                !parent_inlining.has_comptime_args and
+                parent_inlining.func == func_val.?.toIntern())
+            {
                 return sema.fail(block, call_src, "inline call is recursive", .{});
             }
             block_it = parent_inlining.call_block;
@@ -19454,6 +19465,7 @@ fn analyzeRet(
     };
 
     if (block.inlining) |inlining| {
+        assert(!inlining.is_generic_instantiation); // can't `return` in a generic param/ret ty expr
         if (block.isComptime()) {
             const ret_val = try sema.resolveConstValue(block, operand_src, operand, null);
             inlining.comptime_result = operand;