Commit 89d660c3eb

Jacob Young <jacobly0@users.noreply.github.com>
2023-08-02 21:17:51
Sema: improve new error messages related to naked functions
* pass a source location to all safety checks * add notes about what is disallowed in naked functions Closes #16651
1 parent 9e19969
src/Sema.zig
@@ -745,9 +745,9 @@ pub const Block = struct {
         return result_index;
     }
 
-    fn addUnreachable(block: *Block, safety_check: bool) !void {
+    fn addUnreachable(block: *Block, src: LazySrcLoc, safety_check: bool) !void {
         if (safety_check and block.wantSafety()) {
-            try block.sema.safetyPanic(block, .unreach);
+            try block.sema.safetyPanic(block, src, .unreach);
         } else {
             _ = try block.addNoOp(.unreach);
         }
@@ -4304,7 +4304,7 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
             if (arg_len == .none) continue;
             if (i == len_idx) continue;
             const ok = try block.addBinOp(.cmp_eq, len, arg_len);
-            try sema.addSafetyCheck(block, ok, .for_len_mismatch);
+            try sema.addSafetyCheck(block, src, ok, .for_len_mismatch);
         }
     }
 
@@ -5442,7 +5442,7 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.I
     if (block.is_comptime) {
         return sema.fail(block, src, "encountered @panic at comptime", .{});
     }
-    try sema.panicWithMsg(block, msg_inst);
+    try sema.panicWithMsg(block, src, msg_inst, .@"@panic");
     return always_noreturn;
 }
 
@@ -6605,7 +6605,7 @@ fn zirCall(
         !block.is_comptime and !block.is_typeof and (input_is_error or pop_error_return_trace))
     {
         const call_inst: Air.Inst.Ref = if (modifier == .always_tail) undefined else b: {
-            break :b try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node);
+            break :b try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node, .call);
         };
 
         const return_ty = sema.typeOf(call_inst);
@@ -6635,11 +6635,11 @@ fn zirCall(
         }
 
         if (modifier == .always_tail) // Perform the call *after* the restore, so that a tail call is possible.
-            return sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node);
+            return sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node, .call);
 
         return call_inst;
     } else {
-        return sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node);
+        return sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, resolved_args, bound_arg_src, call_dbg_node, .call);
     }
 }
 
@@ -6719,9 +6719,11 @@ fn checkCallArgumentCount(
 fn callBuiltin(
     sema: *Sema,
     block: *Block,
+    call_src: LazySrcLoc,
     builtin_fn: Air.Inst.Ref,
     modifier: std.builtin.CallModifier,
     args: []const Air.Inst.Ref,
+    operation: CallOperation,
 ) !void {
     const mod = sema.mod;
     const callee_ty = sema.typeOf(builtin_fn);
@@ -6744,9 +6746,17 @@ fn callBuiltin(
     if (args.len != fn_params_len or (func_ty_info.is_var_args and args.len < fn_params_len)) {
         std.debug.panic("parameter count mismatch calling builtin fn, expected {d}, found {d}", .{ fn_params_len, args.len });
     }
-    _ = try sema.analyzeCall(block, builtin_fn, func_ty, sema.src, sema.src, modifier, false, args, null, null);
+    _ = try sema.analyzeCall(block, builtin_fn, func_ty, call_src, call_src, modifier, false, args, null, null, operation);
 }
 
+const CallOperation = enum {
+    call,
+    @"@call",
+    @"@panic",
+    @"safety check",
+    @"error return",
+};
+
 fn analyzeCall(
     sema: *Sema,
     block: *Block,
@@ -6759,6 +6769,7 @@ fn analyzeCall(
     uncasted_args: []const Air.Inst.Ref,
     bound_arg_src: ?LazySrcLoc,
     call_dbg_node: ?Zir.Inst.Index,
+    operation: CallOperation,
 ) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
     const ip = &mod.intern_pool;
@@ -6830,7 +6841,17 @@ fn analyzeCall(
         func_ty_info.cc == .Inline;
 
     if (sema.func_is_naked and !is_inline_call and !is_comptime_call) {
-        return sema.fail(block, call_src, "runtime call not allowed in naked function", .{});
+        const msg = msg: {
+            const msg = try sema.errMsg(block, call_src, "runtime {s} not allowed in naked function", .{@tagName(operation)});
+            errdefer msg.destroy(sema.gpa);
+
+            switch (operation) {
+                .call, .@"@call", .@"@panic", .@"error return" => {},
+                .@"safety check" => try sema.errNote(block, call_src, msg, "use @setRuntimeSafety to disable runtime safety", .{}),
+            }
+            break :msg msg;
+        };
+        return sema.failWithOwnedErrorMsg(msg);
     }
 
     if (!is_inline_call and is_generic_call) {
@@ -7281,7 +7302,7 @@ fn analyzeCall(
                     else => {},
                 }
             }
-            try sema.safetyPanic(block, .noreturn_returned);
+            try sema.safetyPanic(block, call_src, .noreturn_returned);
             return Air.Inst.Ref.unreachable_value;
         }
         if (func_ty_info.return_type == .noreturn_type) {
@@ -7939,7 +7960,7 @@ fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD
         const zero_val = try sema.addConstant(try mod.intValue(Type.err_int, 0));
         const is_non_zero = try block.addBinOp(.cmp_neq, operand, zero_val);
         const ok = try block.addBinOp(.bit_and, is_lt_len, is_non_zero);
-        try sema.addSafetyCheck(block, ok, .invalid_error_code);
+        try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
     }
     return block.addInst(.{
         .tag = .bitcast,
@@ -8131,7 +8152,7 @@ fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
         mod.backendSupportsFeature(.is_named_enum_value))
     {
         const ok = try block.addUnOp(.is_named_enum_value, result);
-        try sema.addSafetyCheck(block, ok, .invalid_enum_value);
+        try sema.addSafetyCheck(block, src, ok, .invalid_enum_value);
     }
     return result;
 }
@@ -8207,7 +8228,7 @@ fn analyzeOptionalPayloadPtr(
     try sema.requireRuntimeBlock(block, src, null);
     if (safety_check and block.wantSafety()) {
         const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr);
-        try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
+        try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
     }
     const air_tag: Air.Inst.Tag = if (initializing)
         .optional_payload_ptr_set
@@ -8264,7 +8285,7 @@ fn zirOptionalPayload(
     try sema.requireRuntimeBlock(block, src, null);
     if (safety_check and block.wantSafety()) {
         const is_non_null = try block.addUnOp(.is_non_null, operand);
-        try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
+        try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
     }
     return block.addTyOp(.optional_payload, result_ty, operand);
 }
@@ -8318,7 +8339,7 @@ fn analyzeErrUnionPayload(
     if (safety_check and block.wantSafety() and
         !err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod))
     {
-        try sema.panicUnwrapError(block, operand, .unwrap_errunion_err, .is_non_err);
+        try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);
     }
 
     return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand);
@@ -8399,7 +8420,7 @@ fn analyzeErrUnionPayloadPtr(
     if (safety_check and block.wantSafety() and
         !err_union_ty.errorUnionSet(mod).errorSetIsEmpty(mod))
     {
-        try sema.panicUnwrapError(block, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);
+        try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);
     }
 
     const air_tag: Air.Inst.Tag = if (initializing)
@@ -9637,7 +9658,7 @@ fn intCast(
                     const is_in_range = try block.addBinOp(.cmp_lte, operand, zero_inst);
                     break :ok is_in_range;
                 };
-                try sema.addSafetyCheck(block, ok, .cast_truncated_data);
+                try sema.addSafetyCheck(block, src, ok, .cast_truncated_data);
             }
         }
 
@@ -9691,7 +9712,7 @@ fn intCast(
                     break :ok is_in_range;
                 };
                 // TODO negative_to_unsigned?
-                try sema.addSafetyCheck(block, ok, .cast_truncated_data);
+                try sema.addSafetyCheck(block, src, ok, .cast_truncated_data);
             } else {
                 const ok = if (is_vector) ok: {
                     const is_in_range = try block.addCmpVector(diff, dest_max, .lte);
@@ -9707,7 +9728,7 @@ fn intCast(
                     const is_in_range = try block.addBinOp(.cmp_lte, diff, dest_max);
                     break :ok is_in_range;
                 };
-                try sema.addSafetyCheck(block, ok, .cast_truncated_data);
+                try sema.addSafetyCheck(block, src, ok, .cast_truncated_data);
             }
         } else if (actual_info.signedness == .signed and wanted_info.signedness == .unsigned) {
             // no shrinkage, yes sign loss
@@ -9730,7 +9751,7 @@ fn intCast(
                 const is_in_range = try block.addBinOp(.cmp_gte, operand, zero_inst);
                 break :ok is_in_range;
             };
-            try sema.addSafetyCheck(block, ok, .negative_to_unsigned);
+            try sema.addSafetyCheck(block, src, ok, .negative_to_unsigned);
         }
     }
     return block.addTyOp(.intcast, dest_ty, operand);
@@ -10319,7 +10340,7 @@ const SwitchProngAnalysis = struct {
                 .ErrorSet => if (spa.else_error_ty) |ty| {
                     return sema.bitCast(block, ty, spa.operand, operand_src, null);
                 } else {
-                    try block.addUnreachable(false);
+                    try block.addUnreachable(operand_src, false);
                     return Air.Inst.Ref.unreachable_value;
                 },
                 else => return spa.operand,
@@ -11475,7 +11496,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
         if (special_prong == .none) {
             return sema.fail(block, src, "switch must handle all possibilities", .{});
         }
-        if (err_set and try sema.maybeErrorUnwrap(block, special.body, operand)) {
+        if (err_set and try sema.maybeErrorUnwrap(block, special.body, operand, operand_src)) {
             return Air.Inst.Ref.unreachable_value;
         }
         if (mod.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and operand_ty.zigTypeTag(mod) == .Enum and
@@ -11483,7 +11504,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
         {
             try sema.zirDbgStmt(block, cond_dbg_node_index);
             const ok = try block.addUnOp(.is_named_enum_value, operand);
-            try sema.addSafetyCheck(block, ok, .corrupt_switch);
+            try sema.addSafetyCheck(block, src, ok, .corrupt_switch);
         }
 
         return spa.resolveProngComptime(
@@ -11543,7 +11564,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
             break :blk field_ty.zigTypeTag(mod) != .NoReturn;
         } else true;
 
-        if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
+        if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src)) {
             // nothing to do here
         } else if (analyze_body) {
             try spa.analyzeProngRuntime(
@@ -11725,7 +11746,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
 
             const body = sema.code.extra[extra_index..][0..info.body_len];
             extra_index += info.body_len;
-            if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
+            if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src)) {
                 // nothing to do here
             } else if (analyze_body) {
                 try spa.analyzeProngRuntime(
@@ -11812,7 +11833,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
 
             const body = sema.code.extra[extra_index..][0..info.body_len];
             extra_index += info.body_len;
-            if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand)) {
+            if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src)) {
                 // nothing to do here
             } else {
                 try spa.analyzeProngRuntime(
@@ -12045,7 +12066,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
         {
             try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
             const ok = try case_block.addUnOp(.is_named_enum_value, operand);
-            try sema.addSafetyCheck(&case_block, ok, .corrupt_switch);
+            try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch);
         }
 
         const analyze_body = if (union_originally and !special.is_inline)
@@ -12058,7 +12079,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
         else
             true;
         if (special.body.len != 0 and err_set and
-            try sema.maybeErrorUnwrap(&case_block, special.body, operand))
+            try sema.maybeErrorUnwrap(&case_block, special.body, operand, operand_src))
         {
             // nothing to do here
         } else if (special.body.len != 0 and analyze_body and !special.is_inline) {
@@ -12077,7 +12098,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
             // that it is unreachable.
             if (case_block.wantSafety()) {
                 try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
-                try sema.safetyPanic(&case_block, .corrupt_switch);
+                try sema.safetyPanic(&case_block, src, .corrupt_switch);
             } else {
                 _ = try case_block.addNoOp(.unreach);
             }
@@ -12420,7 +12441,7 @@ fn validateSwitchNoRange(
     return sema.failWithOwnedErrorMsg(msg);
 }
 
-fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref) !bool {
+fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref, operand_src: LazySrcLoc) !bool {
     const mod = sema.mod;
     if (!mod.backendSupportsFeature(.panic_unwrap_error)) return false;
 
@@ -12459,14 +12480,14 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op
             .field_val => try sema.zirFieldVal(block, inst),
             .@"unreachable" => {
                 if (!mod.comp.formatted_panics) {
-                    try sema.safetyPanic(block, .unwrap_error);
+                    try sema.safetyPanic(block, operand_src, .unwrap_error);
                     return true;
                 }
 
                 const panic_fn = try sema.getBuiltin("panicUnwrapError");
                 const err_return_trace = try sema.getErrorReturnTrace(block);
                 const args: [2]Air.Inst.Ref = .{ err_return_trace, operand };
-                try sema.callBuiltin(block, panic_fn, .auto, &args);
+                try sema.callBuiltin(block, operand_src, panic_fn, .auto, &args, .@"safety check");
                 return true;
             },
             .panic => {
@@ -12476,7 +12497,7 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op
                 const panic_fn = try sema.getBuiltin("panic");
                 const err_return_trace = try sema.getErrorReturnTrace(block);
                 const args: [3]Air.Inst.Ref = .{ msg_inst, err_return_trace, .null_value };
-                try sema.callBuiltin(block, panic_fn, .auto, &args);
+                try sema.callBuiltin(block, operand_src, panic_fn, .auto, &args, .@"safety check");
                 return true;
             },
             else => unreachable,
@@ -12851,7 +12872,7 @@ fn zirShl(
                 const bit_count_inst = try sema.addConstant(bit_count_val);
                 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst);
             };
-            try sema.addSafetyCheck(block, ok, .shift_rhs_too_big);
+            try sema.addSafetyCheck(block, src, ok, .shift_rhs_too_big);
         }
 
         if (air_tag == .shl_exact) {
@@ -12880,7 +12901,7 @@ fn zirShl(
             const zero_ov = try sema.addConstant(try mod.intValue(Type.u1, 0));
             const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov);
 
-            try sema.addSafetyCheck(block, no_ov, .shl_overflow);
+            try sema.addSafetyCheck(block, src, no_ov, .shl_overflow);
             return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty);
         }
     }
@@ -13001,7 +13022,7 @@ fn zirShr(
                 const bit_count_inst = try sema.addConstant(bit_count_val);
                 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst);
             };
-            try sema.addSafetyCheck(block, ok, .shift_rhs_too_big);
+            try sema.addSafetyCheck(block, src, ok, .shift_rhs_too_big);
         }
 
         if (air_tag == .shr_exact) {
@@ -13017,7 +13038,7 @@ fn zirShr(
                     } },
                 });
             } else try block.addBinOp(.cmp_eq, lhs, back);
-            try sema.addSafetyCheck(block, ok, .shr_overflow);
+            try sema.addSafetyCheck(block, src, ok, .shr_overflow);
         }
     }
     return result;
@@ -13894,8 +13915,8 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
     try sema.requireRuntimeBlock(block, src, runtime_src);
 
     if (block.wantSafety()) {
-        try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
-        try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
+        try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
+        try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
     }
 
     const air_tag = if (is_int) blk: {
@@ -14025,8 +14046,8 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     // div_trunc and check for remainder.
 
     if (block.wantSafety()) {
-        try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
-        try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
+        try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
+        try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
 
         const result = try block.addBinOp(.div_trunc, casted_lhs, casted_rhs);
         const ok = if (!is_int) ok: {
@@ -14076,7 +14097,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 break :ok is_in_range;
             }
         };
-        try sema.addSafetyCheck(block, ok, .exact_division_remainder);
+        try sema.addSafetyCheck(block, src, ok, .exact_division_remainder);
         return result;
     }
 
@@ -14191,8 +14212,8 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     try sema.requireRuntimeBlock(block, src, runtime_src);
 
     if (block.wantSafety()) {
-        try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
-        try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
+        try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
+        try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
     }
 
     return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs);
@@ -14308,8 +14329,8 @@ fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     try sema.requireRuntimeBlock(block, src, runtime_src);
 
     if (block.wantSafety()) {
-        try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
-        try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
+        try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
+        try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
     }
 
     return block.addBinOp(airTag(block, is_int, .div_trunc, .div_trunc_optimized), casted_lhs, casted_rhs);
@@ -14318,6 +14339,7 @@ fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
 fn addDivIntOverflowSafety(
     sema: *Sema,
     block: *Block,
+    src: LazySrcLoc,
     resolved_type: Type,
     lhs_scalar_ty: Type,
     maybe_lhs_val: ?Value,
@@ -14391,12 +14413,13 @@ fn addDivIntOverflowSafety(
         }
         assert(ok != .none);
     }
-    try sema.addSafetyCheck(block, ok, .integer_overflow);
+    try sema.addSafetyCheck(block, src, ok, .integer_overflow);
 }
 
 fn addDivByZeroSafety(
     sema: *Sema,
     block: *Block,
+    src: LazySrcLoc,
     resolved_type: Type,
     maybe_rhs_val: ?Value,
     casted_rhs: Air.Inst.Ref,
@@ -14429,7 +14452,7 @@ fn addDivByZeroSafety(
         const zero = try sema.addConstant(scalar_zero);
         break :ok try block.addBinOp(if (is_int) .cmp_neq else .cmp_neq_optimized, casted_rhs, zero);
     };
-    try sema.addSafetyCheck(block, ok, .divide_by_zero);
+    try sema.addSafetyCheck(block, src, ok, .divide_by_zero);
 }
 
 fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst.Tag) Air.Inst.Tag {
@@ -14569,7 +14592,7 @@ fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
     try sema.requireRuntimeBlock(block, src, runtime_src);
 
     if (block.wantSafety()) {
-        try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
+        try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
     }
 
     const air_tag = airTag(block, is_int, .rem, .rem_optimized);
@@ -14720,7 +14743,7 @@ fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
     try sema.requireRuntimeBlock(block, src, runtime_src);
 
     if (block.wantSafety()) {
-        try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
+        try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
     }
 
     const air_tag = airTag(block, is_int, .mod, .mod_optimized);
@@ -14820,7 +14843,7 @@ fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
     try sema.requireRuntimeBlock(block, src, runtime_src);
 
     if (block.wantSafety()) {
-        try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
+        try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
     }
 
     const air_tag = airTag(block, is_int, .rem, .rem_optimized);
@@ -15558,7 +15581,7 @@ fn analyzeArithmetic(
                 const zero_ov = try sema.addConstant(try mod.intValue(Type.u1, 0));
                 const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, zero_ov);
 
-                try sema.addSafetyCheck(block, no_ov, .integer_overflow);
+                try sema.addSafetyCheck(block, src, no_ov, .integer_overflow);
                 return sema.tupleFieldValByIndex(block, src, op_ov, 0, op_ov_tuple_ty);
             }
         }
@@ -17979,7 +18002,7 @@ fn zirCondbr(
         break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand);
     };
 
-    if (err_cond != null and try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?)) {
+    if (err_cond != null and try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?, cond_src)) {
         // nothing to do
     } else {
         try sema.analyzeBodyRuntimeBreak(&sub_block, else_body);
@@ -18171,7 +18194,15 @@ fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
         return sema.fail(block, src, "reached unreachable code", .{});
     }
     // TODO Add compile error for @optimizeFor occurring too late in a scope.
-    try block.addUnreachable(true);
+    block.addUnreachable(src, true) catch |err| switch (err) {
+        error.AnalysisFail => {
+            const msg = sema.err orelse return err;
+            if (!mem.eql(u8, msg.msg, "runtime safety check not allowed in naked function")) return err;
+            try sema.errNote(block, src, msg, "the end of a naked function is implicitly unreachable", .{});
+            return err;
+        },
+        else => |e| return e,
+    };
     return always_noreturn;
 }
 
@@ -18202,21 +18233,21 @@ fn zirRetImplicit(
     const tracy = trace(@src());
     defer tracy.end();
 
+    const mod = sema.mod;
+    const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
+    const r_brace_src = inst_data.src();
     if (block.inlining == null and sema.func_is_naked) {
         assert(!block.is_comptime);
         if (block.wantSafety()) {
             // Calling a safety function from a naked function would not be legal.
             _ = try block.addNoOp(.trap);
         } else {
-            try block.addUnreachable(false);
+            try block.addUnreachable(r_brace_src, false);
         }
         return always_noreturn;
     }
 
-    const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
     const operand = try sema.resolveInst(inst_data.operand);
-    const r_brace_src = inst_data.src();
     const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
     const base_tag = sema.fn_ret_ty.baseZigTypeTag(mod);
     if (base_tag == .NoReturn) {
@@ -18270,7 +18301,7 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir
 
     if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) {
         const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr);
-        return sema.retWithErrTracing(block, is_non_err, .ret_load, ret_ptr);
+        return sema.retWithErrTracing(block, src, is_non_err, .ret_load, ret_ptr);
     }
 
     _ = try block.addUnOp(.ret_load, ret_ptr);
@@ -18280,6 +18311,7 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir
 fn retWithErrTracing(
     sema: *Sema,
     block: *Block,
+    src: LazySrcLoc,
     is_non_err: Air.Inst.Ref,
     ret_tag: Air.Inst.Tag,
     operand: Air.Inst.Ref,
@@ -18302,7 +18334,7 @@ fn retWithErrTracing(
     const args: [1]Air.Inst.Ref = .{err_return_trace};
 
     if (!need_check) {
-        try sema.callBuiltin(block, return_err_fn, .never_inline, &args);
+        try sema.callBuiltin(block, src, return_err_fn, .never_inline, &args, .@"error return");
         _ = try block.addUnOp(ret_tag, operand);
         return always_noreturn;
     }
@@ -18313,7 +18345,7 @@ fn retWithErrTracing(
 
     var else_block = block.makeSubBlock();
     defer else_block.instructions.deinit(gpa);
-    try sema.callBuiltin(&else_block, return_err_fn, .never_inline, &args);
+    try sema.callBuiltin(&else_block, src, return_err_fn, .never_inline, &args, .@"error return");
     _ = try else_block.addUnOp(ret_tag, operand);
 
     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len +
@@ -18470,7 +18502,14 @@ fn analyzeRet(
     } else if (block.is_comptime) {
         return sema.fail(block, src, "function called at runtime cannot return value at comptime", .{});
     } else if (sema.func_is_naked) {
-        return sema.fail(block, src, "cannot return from naked function", .{});
+        const msg = msg: {
+            const msg = try sema.errMsg(block, src, "cannot return from naked function", .{});
+            errdefer msg.destroy(sema.gpa);
+
+            try sema.errNote(block, src, msg, "can only return using assembly", .{});
+            break :msg msg;
+        };
+        return sema.failWithOwnedErrorMsg(msg);
     }
 
     try sema.resolveTypeLayout(sema.fn_ret_ty);
@@ -18479,7 +18518,7 @@ fn analyzeRet(
         // Avoid adding a frame to the error return trace in case the value is comptime-known
         // to be not an error.
         const is_non_err = try sema.analyzeIsNonErr(block, src, operand);
-        return sema.retWithErrTracing(block, is_non_err, .ret, operand);
+        return sema.retWithErrTracing(block, src, is_non_err, .ret, operand);
     }
 
     _ = try block.addUnOp(.ret, operand);
@@ -19624,7 +19663,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
     try sema.requireRuntimeBlock(block, src, operand_src);
     if (block.wantSafety() and sema.mod.backendSupportsFeature(.is_named_enum_value)) {
         const ok = try block.addUnOp(.is_named_enum_value, casted_operand);
-        try sema.addSafetyCheck(block, ok, .invalid_enum_value);
+        try sema.addSafetyCheck(block, src, ok, .invalid_enum_value);
     }
     // In case the value is runtime-known, we have an AIR instruction for this instead
     // of trying to lower it in Sema because an optimization pass may result in the operand
@@ -20769,7 +20808,7 @@ fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
     if (dest_ty.intInfo(mod).bits == 0) {
         if (block.wantSafety()) {
             const ok = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, operand, try sema.addConstant(try mod.floatValue(operand_ty, 0.0)));
-            try sema.addSafetyCheck(block, ok, .integer_part_out_of_bounds);
+            try sema.addSafetyCheck(block, src, ok, .integer_part_out_of_bounds);
         }
         return sema.addConstant(try mod.intValue(dest_ty, 0));
     }
@@ -20780,7 +20819,7 @@ fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
         const ok_pos = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_lt_optimized else .cmp_lt, diff, try sema.addConstant(try mod.floatValue(operand_ty, 1.0)));
         const ok_neg = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_gt_optimized else .cmp_gt, diff, try sema.addConstant(try mod.floatValue(operand_ty, -1.0)));
         const ok = try block.addBinOp(.bool_and, ok_pos, ok_neg);
-        try sema.addSafetyCheck(block, ok, .integer_part_out_of_bounds);
+        try sema.addSafetyCheck(block, src, ok, .integer_part_out_of_bounds);
     }
     return result;
 }
@@ -20857,7 +20896,7 @@ fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
     if (block.wantSafety() and (try sema.typeHasRuntimeBits(elem_ty) or elem_ty.zigTypeTag(mod) == .Fn)) {
         if (!ptr_ty.isAllowzeroPtr(mod)) {
             const is_non_zero = try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize);
-            try sema.addSafetyCheck(block, is_non_zero, .cast_to_null);
+            try sema.addSafetyCheck(block, src, is_non_zero, .cast_to_null);
         }
 
         if (ptr_align > 1) {
@@ -20866,7 +20905,7 @@ fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
             );
             const remainder = try block.addBinOp(.bit_and, operand_coerced, align_minus_1);
             const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
-            try sema.addSafetyCheck(block, is_aligned, .incorrect_alignment);
+            try sema.addSafetyCheck(block, src, is_aligned, .incorrect_alignment);
         }
     }
     return block.addBitCast(ptr_ty, operand_coerced);
@@ -20954,7 +20993,7 @@ fn zirErrSetCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDat
     if (block.wantSafety() and !dest_ty.isAnyError(mod) and sema.mod.backendSupportsFeature(.error_set_has_value)) {
         const err_int_inst = try block.addBitCast(Type.err_int, operand);
         const ok = try block.addTyOp(.error_set_has_value, dest_ty, err_int_inst);
-        try sema.addSafetyCheck(block, ok, .invalid_error_code);
+        try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
     }
     return block.addBitCast(dest_ty, operand);
 }
@@ -21300,7 +21339,7 @@ fn ptrCastFull(
             const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize);
             break :ok try block.addBinOp(.bit_or, len_zero, is_non_zero);
         } else is_non_zero;
-        try sema.addSafetyCheck(block, ok, .cast_to_null);
+        try sema.addSafetyCheck(block, src, ok, .cast_to_null);
     }
 
     if (block.wantSafety() and dest_align > src_align and try sema.typeHasRuntimeBits(dest_info.child.toType())) {
@@ -21315,7 +21354,7 @@ fn ptrCastFull(
             const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize);
             break :ok try block.addBinOp(.bit_or, len_zero, is_aligned);
         } else is_aligned;
-        try sema.addSafetyCheck(block, ok, .incorrect_alignment);
+        try sema.addSafetyCheck(block, src, ok, .incorrect_alignment);
     }
 
     // If we're going from an array pointer to a slice, this will only be the pointer part!
@@ -22992,7 +23031,7 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     const callee_ty = sema.typeOf(func);
     const func_ty = try sema.checkCallArgumentCount(block, func, func_src, callee_ty, resolved_args.len, false);
     const ensure_result_used = extra.flags.ensure_result_used;
-    return sema.analyzeCall(block, func, func_ty, func_src, call_src, modifier, ensure_result_used, resolved_args, null, null);
+    return sema.analyzeCall(block, func, func_ty, func_src, call_src, modifier, ensure_result_used, resolved_args, null, null, .@"@call");
 }
 
 fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -23477,7 +23516,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
 
         if (block.wantSafety()) {
             const ok = try block.addBinOp(.cmp_eq, dest_len, src_len);
-            try sema.addSafetyCheck(block, ok, .memcpy_len_mismatch);
+            try sema.addSafetyCheck(block, src, ok, .memcpy_len_mismatch);
         }
     } else if (dest_len != .none) {
         if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| {
@@ -23619,7 +23658,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
         const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len);
         const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len);
         const ok = try block.addBinOp(.bit_or, ok1, ok2);
-        try sema.addSafetyCheck(block, ok, .memcpy_alias);
+        try sema.addSafetyCheck(block, src, ok, .memcpy_alias);
     }
 
     _ = try block.addInst(.{
@@ -24862,6 +24901,7 @@ fn preparePanicId(sema: *Sema, block: *Block, panic_id: Module.PanicId) !Module.
 fn addSafetyCheck(
     sema: *Sema,
     parent_block: *Block,
+    src: LazySrcLoc,
     ok: Air.Inst.Ref,
     panic_id: Module.PanicId,
 ) !void {
@@ -24881,7 +24921,7 @@ fn addSafetyCheck(
 
     defer fail_block.instructions.deinit(gpa);
 
-    try sema.safetyPanic(&fail_block, panic_id);
+    try sema.safetyPanic(&fail_block, src, panic_id);
     try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
 }
 
@@ -24940,7 +24980,7 @@ fn addSafetyCheckExtra(
     parent_block.instructions.appendAssumeCapacity(block_inst);
 }
 
-fn panicWithMsg(sema: *Sema, block: *Block, msg_inst: Air.Inst.Ref) !void {
+fn panicWithMsg(sema: *Sema, block: *Block, src: LazySrcLoc, msg_inst: Air.Inst.Ref, operation: CallOperation) !void {
     const mod = sema.mod;
 
     if (!mod.backendSupportsFeature(.panic_fn)) {
@@ -24959,12 +24999,13 @@ fn panicWithMsg(sema: *Sema, block: *Block, msg_inst: Air.Inst.Ref) !void {
         .ty = opt_usize_ty.toIntern(),
         .val = .none,
     } })).toValue());
-    try sema.callBuiltin(block, panic_fn, .auto, &.{ msg_inst, null_stack_trace, null_ret_addr });
+    try sema.callBuiltin(block, src, panic_fn, .auto, &.{ msg_inst, null_stack_trace, null_ret_addr }, operation);
 }
 
 fn panicUnwrapError(
     sema: *Sema,
     parent_block: *Block,
+    src: LazySrcLoc,
     operand: Air.Inst.Ref,
     unwrap_err_tag: Air.Inst.Tag,
     is_non_err_tag: Air.Inst.Tag,
@@ -24972,7 +25013,7 @@ fn panicUnwrapError(
     assert(!parent_block.is_comptime);
     const ok = try parent_block.addUnOp(is_non_err_tag, operand);
     if (!sema.mod.comp.formatted_panics) {
-        return sema.addSafetyCheck(parent_block, ok, .unwrap_error);
+        return sema.addSafetyCheck(parent_block, src, ok, .unwrap_error);
     }
     const gpa = sema.gpa;
 
@@ -24997,7 +25038,7 @@ fn panicUnwrapError(
             const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand);
             const err_return_trace = try sema.getErrorReturnTrace(&fail_block);
             const args: [2]Air.Inst.Ref = .{ err_return_trace, err };
-            try sema.callBuiltin(&fail_block, panic_fn, .auto, &args);
+            try sema.callBuiltin(&fail_block, src, panic_fn, .auto, &args, .@"safety check");
         }
     }
     try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
@@ -25006,6 +25047,7 @@ fn panicUnwrapError(
 fn panicIndexOutOfBounds(
     sema: *Sema,
     parent_block: *Block,
+    src: LazySrcLoc,
     index: Air.Inst.Ref,
     len: Air.Inst.Ref,
     cmp_op: Air.Inst.Tag,
@@ -25013,28 +25055,30 @@ fn panicIndexOutOfBounds(
     assert(!parent_block.is_comptime);
     const ok = try parent_block.addBinOp(cmp_op, index, len);
     if (!sema.mod.comp.formatted_panics) {
-        return sema.addSafetyCheck(parent_block, ok, .index_out_of_bounds);
+        return sema.addSafetyCheck(parent_block, src, ok, .index_out_of_bounds);
     }
-    try sema.safetyCheckFormatted(parent_block, ok, "panicOutOfBounds", &.{ index, len });
+    try sema.safetyCheckFormatted(parent_block, src, ok, "panicOutOfBounds", &.{ index, len });
 }
 
 fn panicInactiveUnionField(
     sema: *Sema,
     parent_block: *Block,
+    src: LazySrcLoc,
     active_tag: Air.Inst.Ref,
     wanted_tag: Air.Inst.Ref,
 ) !void {
     assert(!parent_block.is_comptime);
     const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag);
     if (!sema.mod.comp.formatted_panics) {
-        return sema.addSafetyCheck(parent_block, ok, .inactive_union_field);
+        return sema.addSafetyCheck(parent_block, src, ok, .inactive_union_field);
     }
-    try sema.safetyCheckFormatted(parent_block, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag });
+    try sema.safetyCheckFormatted(parent_block, src, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag });
 }
 
 fn panicSentinelMismatch(
     sema: *Sema,
     parent_block: *Block,
+    src: LazySrcLoc,
     maybe_sentinel: ?Value,
     sentinel_ty: Type,
     ptr: Air.Inst.Ref,
@@ -25069,19 +25113,20 @@ fn panicSentinelMismatch(
     else {
         const panic_fn = try sema.getBuiltin("checkNonScalarSentinel");
         const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel };
-        try sema.callBuiltin(parent_block, panic_fn, .auto, &args);
+        try sema.callBuiltin(parent_block, src, panic_fn, .auto, &args, .@"safety check");
         return;
     };
 
     if (!sema.mod.comp.formatted_panics) {
-        return sema.addSafetyCheck(parent_block, ok, .sentinel_mismatch);
+        return sema.addSafetyCheck(parent_block, src, ok, .sentinel_mismatch);
     }
-    try sema.safetyCheckFormatted(parent_block, ok, "panicSentinelMismatch", &.{ expected_sentinel, actual_sentinel });
+    try sema.safetyCheckFormatted(parent_block, src, ok, "panicSentinelMismatch", &.{ expected_sentinel, actual_sentinel });
 }
 
 fn safetyCheckFormatted(
     sema: *Sema,
     parent_block: *Block,
+    src: LazySrcLoc,
     ok: Air.Inst.Ref,
     func: []const u8,
     args: []const Air.Inst.Ref,
@@ -25106,15 +25151,15 @@ fn safetyCheckFormatted(
         _ = try fail_block.addNoOp(.trap);
     } else {
         const panic_fn = try sema.getBuiltin(func);
-        try sema.callBuiltin(&fail_block, panic_fn, .auto, args);
+        try sema.callBuiltin(&fail_block, src, panic_fn, .auto, args, .@"safety check");
     }
     try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
 }
 
-fn safetyPanic(sema: *Sema, block: *Block, panic_id: Module.PanicId) CompileError!void {
+fn safetyPanic(sema: *Sema, block: *Block, src: LazySrcLoc, panic_id: Module.PanicId) CompileError!void {
     const msg_decl_index = try sema.preparePanicId(block, panic_id);
-    const msg_inst = try sema.analyzeDeclVal(block, sema.src, msg_decl_index);
-    try sema.panicWithMsg(block, msg_inst);
+    const msg_inst = try sema.analyzeDeclVal(block, src, msg_decl_index);
+    try sema.panicWithMsg(block, src, msg_inst, .@"safety check");
 }
 
 fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
@@ -26199,7 +26244,7 @@ fn unionFieldPtr(
         // TODO would it be better if get_union_tag supported pointers to unions?
         const union_val = try block.addTyOp(.load, union_ty, union_ptr);
         const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_val);
-        try sema.panicInactiveUnionField(block, active_tag, wanted_tag);
+        try sema.panicInactiveUnionField(block, src, active_tag, wanted_tag);
     }
     if (field.ty.zigTypeTag(mod) == .NoReturn) {
         _ = try block.addNoOp(.unreach);
@@ -26271,7 +26316,7 @@ fn unionFieldVal(
         const wanted_tag_val = try mod.enumValueFieldIndex(union_obj.tag_ty, enum_field_index);
         const wanted_tag = try sema.addConstant(wanted_tag_val);
         const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_byval);
-        try sema.panicInactiveUnionField(block, active_tag, wanted_tag);
+        try sema.panicInactiveUnionField(block, src, active_tag, wanted_tag);
     }
     if (field.ty.zigTypeTag(mod) == .NoReturn) {
         _ = try block.addNoOp(.unreach);
@@ -26626,7 +26671,7 @@ fn elemValArray(
         if (maybe_index_val == null) {
             const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
             const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt;
-            try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
+            try sema.panicIndexOutOfBounds(block, src, elem_index, len_inst, cmp_op);
         }
     }
     return block.addBinOp(.array_elem_val, array, elem_index);
@@ -26688,7 +26733,7 @@ fn elemPtrArray(
     if (oob_safety and block.wantSafety() and offset == null) {
         const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
         const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
-        try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
+        try sema.panicIndexOutOfBounds(block, src, elem_index, len_inst, cmp_op);
     }
 
     return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
@@ -26746,7 +26791,7 @@ fn elemValSlice(
         else
             try block.addTyOp(.slice_len, Type.usize, slice);
         const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
-        try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
+        try sema.panicIndexOutOfBounds(block, src, elem_index, len_inst, cmp_op);
     }
     try sema.queueFullTypeResolution(sema.typeOf(slice));
     return block.addBinOp(.slice_elem_val, slice, elem_index);
@@ -26806,7 +26851,7 @@ fn elemPtrSlice(
             break :len try block.addTyOp(.slice_len, Type.usize, slice);
         };
         const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
-        try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
+        try sema.panicIndexOutOfBounds(block, src, elem_index, len_inst, cmp_op);
     }
     return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty);
 }
@@ -29705,7 +29750,7 @@ fn coerceCompatiblePtrs(
             const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize);
             break :ok try block.addBinOp(.bit_or, len_zero, is_non_zero);
         } else is_non_zero;
-        try sema.addSafetyCheck(block, ok, .cast_to_null);
+        try sema.addSafetyCheck(block, inst_src, ok, .cast_to_null);
     }
     return sema.bitCast(block, dest_ty, inst, inst_src, null);
 }
@@ -31134,9 +31179,9 @@ fn analyzeSlice(
         try sema.requireRuntimeBlock(block, src, runtime_src.?);
         const ok = try block.addBinOp(.cmp_lte, start, end);
         if (!sema.mod.comp.formatted_panics) {
-            try sema.addSafetyCheck(block, ok, .start_index_greater_than_end);
+            try sema.addSafetyCheck(block, src, ok, .start_index_greater_than_end);
         } else {
-            try sema.safetyCheckFormatted(block, ok, "panicStartGreaterThanEnd", &.{ start, end });
+            try sema.safetyCheckFormatted(block, src, ok, "panicStartGreaterThanEnd", &.{ start, end });
         }
     }
     const new_len = if (by_length)
@@ -31173,7 +31218,7 @@ fn analyzeSlice(
                 // requirement: slicing C ptr is non-null
                 if (ptr_ptr_child_ty.isCPtr(mod)) {
                     const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true);
-                    try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
+                    try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
                 }
 
                 if (slice_ty.isSlice(mod)) {
@@ -31188,11 +31233,11 @@ fn analyzeSlice(
                     else
                         end;
 
-                    try sema.panicIndexOutOfBounds(block, actual_end, actual_len, .cmp_lte);
+                    try sema.panicIndexOutOfBounds(block, src, actual_end, actual_len, .cmp_lte);
                 }
 
                 // requirement: result[new_len] == slice_sentinel
-                try sema.panicSentinelMismatch(block, slice_sentinel, elem_ty, result, new_len);
+                try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
             }
             return result;
         };
@@ -31230,7 +31275,7 @@ fn analyzeSlice(
         // requirement: slicing C ptr is non-null
         if (ptr_ptr_child_ty.isCPtr(mod)) {
             const is_non_null = try sema.analyzeIsNull(block, ptr_src, ptr, true);
-            try sema.addSafetyCheck(block, is_non_null, .unwrap_null);
+            try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
         }
 
         // requirement: end <= len
@@ -31254,11 +31299,11 @@ fn analyzeSlice(
                 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
             else
                 end;
-            try sema.panicIndexOutOfBounds(block, actual_end, len_inst, .cmp_lte);
+            try sema.panicIndexOutOfBounds(block, src, actual_end, len_inst, .cmp_lte);
         }
 
         // requirement: start <= end
-        try sema.panicIndexOutOfBounds(block, start, end, .cmp_lte);
+        try sema.panicIndexOutOfBounds(block, src, start, end, .cmp_lte);
     }
     const result = try block.addInst(.{
         .tag = .slice,
@@ -31272,7 +31317,7 @@ fn analyzeSlice(
     });
     if (block.wantSafety()) {
         // requirement: result[new_len] == slice_sentinel
-        try sema.panicSentinelMismatch(block, slice_sentinel, elem_ty, result, new_len);
+        try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
     }
     return result;
 }
test/cases/compile_errors/call_from_naked_func.zig
@@ -0,0 +1,24 @@
+export fn runtimeCall() callconv(.Naked) void {
+    f();
+}
+
+export fn runtimeBuiltinCall() callconv(.Naked) void {
+    @call(.auto, f, .{});
+}
+
+export fn comptimeCall() callconv(.Naked) void {
+    comptime f();
+}
+
+export fn comptimeBuiltinCall() callconv(.Naked) void {
+    @call(.compile_time, f, .{});
+}
+
+fn f() void {}
+
+// error
+// backend=llvm
+// target=native
+//
+// :2:6: error: runtime call not allowed in naked function
+// :6:5: error: runtime @call not allowed in naked function
test/cases/compile_errors/return_from_naked_function.zig
@@ -0,0 +1,14 @@
+fn foo() callconv(.Naked) void {
+    return;
+}
+
+comptime {
+    _ = &foo;
+}
+
+// error
+// backend=llvm
+// target=native
+//
+// :2:5: error: cannot return from naked function
+// :2:5: note: can only return using assembly
test/cases/compile_errors/unreachable_in_naked_func.zig
@@ -0,0 +1,30 @@
+fn runtimeSafetyDefault() callconv(.Naked) void {
+    unreachable;
+}
+
+fn runtimeSafetyOn() callconv(.Naked) void {
+    @setRuntimeSafety(true);
+    unreachable;
+}
+
+fn runtimeSafetyOff() callconv(.Naked) void {
+    @setRuntimeSafety(false);
+    unreachable;
+}
+
+comptime {
+    _ = &runtimeSafetyDefault;
+    _ = &runtimeSafetyOn;
+    _ = &runtimeSafetyOff;
+}
+
+// error
+// backend=llvm
+// target=native
+//
+// :2:5: error: runtime safety check not allowed in naked function
+// :2:5: note: use @setRuntimeSafety to disable runtime safety
+// :2:5: note: the end of a naked function is implicitly unreachable
+// :7:5: error: runtime safety check not allowed in naked function
+// :7:5: note: use @setRuntimeSafety to disable runtime safety
+// :7:5: note: the end of a naked function is implicitly unreachable