authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-01-12 22:35:21+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-01-14 22:44:18+00:00
log4b910e525d97276c64e98832663f0acfe8ff5cfb
tree2dfbf6c657f1593aee37f9281e182224b8514cad
parent5322459a0bd346c78ba069262a5fd7073389a750

Sema: more validation for builtin decl types

Also improve the source locations when this validation fails. Resolves: #22465

3 files changed, 121 insertions(+), 26 deletions(-)

src/Sema.zig+87-22
......@@ -38590,7 +38590,7 @@ pub fn resolveNavPtrModifiers(
3859038590 };
3859138591}
3859238592
38593pub fn analyzeMemoizedState(sema: *Sema, block: *Block, src: LazySrcLoc, builtin_namespace: InternPool.NamespaceIndex, stage: InternPool.MemoizedStateStage) CompileError!bool {
38593pub fn analyzeMemoizedState(sema: *Sema, block: *Block, simple_src: LazySrcLoc, builtin_namespace: InternPool.NamespaceIndex, stage: InternPool.MemoizedStateStage) CompileError!bool {
3859438594 const pt = sema.pt;
3859538595 const zcu = pt.zcu;
3859638596 const ip = &zcu.intern_pool;
......@@ -38605,38 +38605,47 @@ pub fn analyzeMemoizedState(sema: *Sema, block: *Block, src: LazySrcLoc, builtin
3860538605 .nested => |nested| access: {
3860638606 const parent_ty: Type = .fromInterned(zcu.builtin_decl_values.get(nested[0]));
3860738607 const parent_ns = parent_ty.getNamespace(zcu).unwrap() orelse {
38608 return sema.fail(block, src, "std.builtin.{s} is not a container type", .{@tagName(nested[0])});
38608 return sema.fail(block, simple_src, "std.builtin.{s} is not a container type", .{@tagName(nested[0])});
3860938609 };
3861038610 break :access .{ parent_ns, "std.builtin." ++ @tagName(nested[0]), nested[1] };
3861138611 },
3861238612 };
3861338613
3861438614 const name_nts = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls);
38615 const result = try sema.namespaceLookupVal(block, src, parent_ns, name_nts) orelse
38616 return sema.fail(block, src, "{s} missing {s}", .{ parent_name, name });
38615 const nav = try sema.namespaceLookup(block, simple_src, parent_ns, name_nts) orelse
38616 return sema.fail(block, simple_src, "{s} missing {s}", .{ parent_name, name });
3861738617
38618 const val = try sema.resolveConstDefinedValue(block, src, result, null);
38618 const src: LazySrcLoc = .{
38619 .base_node_inst = ip.getNav(nav).srcInst(ip),
38620 .offset = .nodeOffset(0),
38621 };
3861938622
38620 switch (builtin_decl.kind()) {
38621 .type => if (val.typeOf(zcu).zigTypeTag(zcu) != .type) {
38623 const result = try sema.analyzeNavVal(block, src, nav);
38624
38625 const uncoerced_val = try sema.resolveConstDefinedValue(block, src, result, null);
38626 const maybe_lazy_val: Value = switch (builtin_decl.kind()) {
38627 .type => if (uncoerced_val.typeOf(zcu).zigTypeTag(zcu) != .type) {
3862238628 return sema.fail(block, src, "{s}.{s} is not a type", .{ parent_name, name });
38623 } else {
38624 try val.toType().resolveFully(pt);
38629 } else val: {
38630 try uncoerced_val.toType().resolveFully(pt);
38631 break :val uncoerced_val;
3862538632 },
38626 .func => if (val.typeOf(zcu).zigTypeTag(zcu) != .@"fn") {
38627 return sema.fail(block, src, "{s}.{s} is not a function", .{ parent_name, name });
38628 },
38629 .string => {
38630 const ty = val.typeOf(zcu);
38631 if (!ty.isSinglePointer(zcu) or
38632 !ty.isConstPtr(zcu) or
38633 ty.childType(zcu).zigTypeTag(zcu) != .array or
38634 ty.childType(zcu).childType(zcu).toIntern() != .u8_type)
38635 {
38636 return sema.fail(block, src, "{s}.{s} is not a valid string", .{ parent_name, name });
38633 .func => val: {
38634 if (try sema.getExpectedBuiltinFnType(src, builtin_decl)) |func_ty| {
38635 const coerced = try sema.coerce(block, func_ty, Air.internedToRef(uncoerced_val.toIntern()), src);
38636 break :val .fromInterned(coerced.toInterned().?);
3863738637 }
38638 if (uncoerced_val.typeOf(zcu).zigTypeTag(zcu) != .@"fn") {
38639 return sema.fail(block, src, "{s}.{s} is not a function", .{ parent_name, name });
38640 }
38641 break :val uncoerced_val;
3863838642 },
38639 }
38643 .string => val: {
38644 const coerced = try sema.coerce(block, .slice_const_u8, Air.internedToRef(uncoerced_val.toIntern()), src);
38645 break :val .fromInterned(coerced.toInterned().?);
38646 },
38647 };
38648 const val = try sema.resolveLazyValue(maybe_lazy_val);
3864038649
3864138650 const prev = zcu.builtin_decl_values.get(builtin_decl);
3864238651 if (val.toIntern() != prev) {
......@@ -38648,7 +38657,7 @@ pub fn analyzeMemoizedState(sema: *Sema, block: *Block, src: LazySrcLoc, builtin
3864838657
3864938658 if (stage == .panic) {
3865038659 // We use `getBuiltinType` because this is from an earlier stage.
38651 const stack_trace_ty = try sema.getBuiltinType(src, .StackTrace);
38660 const stack_trace_ty = try sema.getBuiltinType(simple_src, .StackTrace);
3865238661 const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty);
3865338662 const opt_ptr_stack_trace_ty = try pt.optionalType(ptr_stack_trace_ty.toIntern());
3865438663 const null_stack_trace = try pt.intern(.{ .opt = .{
......@@ -38663,3 +38672,59 @@ pub fn analyzeMemoizedState(sema: *Sema, block: *Block, src: LazySrcLoc, builtin
3866338672
3866438673 return any_changed;
3866538674}
38675
38676/// Given that `decl.kind() == .func`, get the type expected of the function if necessary.
38677/// If this will be type checked by `Sema` anyway, this function may return `null`. In
38678/// particular, generic functions should return `null`, as `Sema` will necessarily check
38679/// them at instantiation time. Returning non-null is necessary only when backends can emit
38680/// calls to the function, as is the case with the panic handler.
38681fn getExpectedBuiltinFnType(sema: *Sema, src: LazySrcLoc, decl: Zcu.BuiltinDecl) CompileError!?Type {
38682 const pt = sema.pt;
38683 return switch (decl) {
38684 // `fn ([]const u8, ?*StackTrace, ?usize) noreturn`
38685 .@"Panic.call" => try pt.funcType(.{
38686 .param_types = &.{
38687 .slice_const_u8_type,
38688 (try pt.optionalType(
38689 (try pt.singleMutPtrType(
38690 try sema.getBuiltinType(src, .StackTrace),
38691 )).toIntern(),
38692 )).toIntern(),
38693 (try pt.optionalType(.usize_type)).toIntern(),
38694 },
38695 .return_type = .noreturn_type,
38696 }),
38697
38698 // `fn (?*StackTrace, anyerror) noreturn`
38699 .@"Panic.unwrapError" => try pt.funcType(.{
38700 .param_types = &.{
38701 (try pt.optionalType(
38702 (try pt.singleMutPtrType(
38703 try sema.getBuiltinType(src, .StackTrace),
38704 )).toIntern(),
38705 )).toIntern(),
38706 .anyerror_type,
38707 },
38708 .return_type = .noreturn_type,
38709 }),
38710
38711 // `fn (usize, usize) noreturn`
38712 .@"Panic.outOfBounds",
38713 .@"Panic.startGreaterThanEnd",
38714 => try pt.funcType(.{
38715 .param_types = &.{ .usize_type, .usize_type },
38716 .return_type = .noreturn_type,
38717 }),
38718
38719 // Generic functions, so calls are necessarily validated by Sema
38720 .@"Panic.sentinelMismatch",
38721 .@"Panic.inactiveUnionField",
38722 => null,
38723
38724 // Other functions called exclusively by Sema
38725 .returnError,
38726 => null,
38727
38728 else => unreachable,
38729 };
38730}
src/codegen/llvm.zig+6-4
......@@ -5754,10 +5754,12 @@ pub const FuncGen = struct {
57545754 const o = fg.ng.object;
57555755 const zcu = o.pt.zcu;
57565756 const ip = &zcu.intern_pool;
5757 const panic_msg_val = zcu.builtin_decl_values.get(panic_id.toBuiltin());
5758 assert(panic_msg_val != .none);
5759 const msg_len = Value.fromInterned(panic_msg_val).typeOf(zcu).childType(zcu).arrayLen(zcu);
5760 const msg_ptr = try o.lowerValue(panic_msg_val);
5757 const msg_len: u64, const msg_ptr: Builder.Constant = msg: {
5758 const str_val = zcu.builtin_decl_values.get(panic_id.toBuiltin());
5759 assert(str_val != .none);
5760 const slice = ip.indexToKey(str_val).slice;
5761 break :msg .{ Value.fromInterned(slice.len).toUnsignedInt(zcu), try o.lowerValue(slice.ptr) };
5762 };
57615763 const null_opt_addr_global = try fg.resolveNullOptUsize();
57625764 const target = zcu.getTarget();
57635765 const llvm_usize = try o.lowerType(Type.usize);
test/cases/compile_errors/bad_panic_signature.zig created+28
......@@ -0,0 +1,28 @@
1pub const Panic = struct {
2 pub const call = badPanicSignature;
3 pub const sentinelMismatch = std.debug.FormattedPanic.sentinelMismatch;
4 pub const unwrapError = std.debug.FormattedPanic.unwrapError;
5 pub const outOfBounds = std.debug.FormattedPanic.outOfBounds;
6 pub const startGreaterThanEnd = std.debug.FormattedPanic.startGreaterThanEnd;
7 pub const inactiveUnionField = std.debug.FormattedPanic.inactiveUnionField;
8 pub const messages = std.debug.FormattedPanic.messages;
9};
10
11fn badPanicSignature(msg: []const u8, bad1: usize, bad2: void) noreturn {
12 _ = msg;
13 _ = bad1;
14 _ = bad2;
15 @trap();
16}
17
18export fn foo(a: u8) void {
19 @setRuntimeSafety(true);
20 _ = a + 1; // safety check to reference the panic handler
21}
22
23const std = @import("std");
24
25// error
26//
27// :2:9: error: expected type 'fn ([]const u8, ?*builtin.StackTrace, ?usize) noreturn', found 'fn ([]const u8, usize, void) noreturn'
28// :2:9: note: parameter 1 'usize' cannot cast into '?*builtin.StackTrace'