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(...@@ -38590,7 +38590,7 @@ pub fn resolveNavPtrModifiers(
38590 };38590 };
38591}38591}
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 {
38594 const pt = sema.pt;38594 const pt = sema.pt;
38595 const zcu = pt.zcu;38595 const zcu = pt.zcu;
38596 const ip = &zcu.intern_pool;38596 const ip = &zcu.intern_pool;
...@@ -38605,38 +38605,47 @@ pub fn analyzeMemoizedState(sema: *Sema, block: *Block, src: LazySrcLoc, builtin...@@ -38605,38 +38605,47 @@ pub fn analyzeMemoizedState(sema: *Sema, block: *Block, src: LazySrcLoc, builtin
38605 .nested => |nested| access: {38605 .nested => |nested| access: {
38606 const parent_ty: Type = .fromInterned(zcu.builtin_decl_values.get(nested[0]));38606 const parent_ty: Type = .fromInterned(zcu.builtin_decl_values.get(nested[0]));
38607 const parent_ns = parent_ty.getNamespace(zcu).unwrap() orelse {38607 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])});
38609 };38609 };
38610 break :access .{ parent_ns, "std.builtin." ++ @tagName(nested[0]), nested[1] };38610 break :access .{ parent_ns, "std.builtin." ++ @tagName(nested[0]), nested[1] };
38611 },38611 },
38612 };38612 };
3861338613
38614 const name_nts = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls);38614 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) orelse38615 const nav = try sema.namespaceLookup(block, simple_src, parent_ns, name_nts) orelse
38616 return sema.fail(block, src, "{s} missing {s}", .{ parent_name, name });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()) {38623 const result = try sema.analyzeNavVal(block, src, nav);
38621 .type => if (val.typeOf(zcu).zigTypeTag(zcu) != .type) {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) {
38622 return sema.fail(block, src, "{s}.{s} is not a type", .{ parent_name, name });38628 return sema.fail(block, src, "{s}.{s} is not a type", .{ parent_name, name });
38623 } else {38629 } else val: {
38624 try val.toType().resolveFully(pt);38630 try uncoerced_val.toType().resolveFully(pt);
38631 break :val uncoerced_val;
38625 },38632 },
38626 .func => if (val.typeOf(zcu).zigTypeTag(zcu) != .@"fn") {38633 .func => val: {
38627 return sema.fail(block, src, "{s}.{s} is not a function", .{ parent_name, name });38634 if (try sema.getExpectedBuiltinFnType(src, builtin_decl)) |func_ty| {
38628 },38635 const coerced = try sema.coerce(block, func_ty, Air.internedToRef(uncoerced_val.toIntern()), src);
38629 .string => {38636 break :val .fromInterned(coerced.toInterned().?);
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 });
38637 }38637 }
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;
38638 },38642 },
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
38641 const prev = zcu.builtin_decl_values.get(builtin_decl);38650 const prev = zcu.builtin_decl_values.get(builtin_decl);
38642 if (val.toIntern() != prev) {38651 if (val.toIntern() != prev) {
...@@ -38648,7 +38657,7 @@ pub fn analyzeMemoizedState(sema: *Sema, block: *Block, src: LazySrcLoc, builtin...@@ -38648,7 +38657,7 @@ pub fn analyzeMemoizedState(sema: *Sema, block: *Block, src: LazySrcLoc, builtin
3864838657
38649 if (stage == .panic) {38658 if (stage == .panic) {
38650 // We use `getBuiltinType` because this is from an earlier stage.38659 // 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);
38652 const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty);38661 const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty);
38653 const opt_ptr_stack_trace_ty = try pt.optionalType(ptr_stack_trace_ty.toIntern());38662 const opt_ptr_stack_trace_ty = try pt.optionalType(ptr_stack_trace_ty.toIntern());
38654 const null_stack_trace = try pt.intern(.{ .opt = .{38663 const null_stack_trace = try pt.intern(.{ .opt = .{
...@@ -38663,3 +38672,59 @@ pub fn analyzeMemoizedState(sema: *Sema, block: *Block, src: LazySrcLoc, builtin...@@ -38663,3 +38672,59 @@ pub fn analyzeMemoizedState(sema: *Sema, block: *Block, src: LazySrcLoc, builtin
3866338672
38664 return any_changed;38673 return any_changed;
38665}38674}
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 {...@@ -5754,10 +5754,12 @@ pub const FuncGen = struct {
5754 const o = fg.ng.object;5754 const o = fg.ng.object;
5755 const zcu = o.pt.zcu;5755 const zcu = o.pt.zcu;
5756 const ip = &zcu.intern_pool;5756 const ip = &zcu.intern_pool;
5757 const panic_msg_val = zcu.builtin_decl_values.get(panic_id.toBuiltin());5757 const msg_len: u64, const msg_ptr: Builder.Constant = msg: {
5758 assert(panic_msg_val != .none);5758 const str_val = zcu.builtin_decl_values.get(panic_id.toBuiltin());
5759 const msg_len = Value.fromInterned(panic_msg_val).typeOf(zcu).childType(zcu).arrayLen(zcu);5759 assert(str_val != .none);
5760 const msg_ptr = try o.lowerValue(panic_msg_val);5760 const slice = ip.indexToKey(str_val).slice;
5761 break :msg .{ Value.fromInterned(slice.len).toUnsignedInt(zcu), try o.lowerValue(slice.ptr) };
5762 };
5761 const null_opt_addr_global = try fg.resolveNullOptUsize();5763 const null_opt_addr_global = try fg.resolveNullOptUsize();
5762 const target = zcu.getTarget();5764 const target = zcu.getTarget();
5763 const llvm_usize = try o.lowerType(Type.usize);5765 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'