authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-02-09 10:20:58+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-10 10:26:11+00:00
log5865abf7f5ea2d4500cb8387252d9efee26ecae1
tree026c2a4150b8f5881ef70d52dc0fc99dd8efea6b
parent12ddd5a698577e22a0bb5ed788ea4b9729a45b5d
signaturelock-open Commit is signed but in an unrecognized format.

Sema: defer extern function type validation to declaration or call

Because of packed structs, checking whether a type is extern-compatible requires that its layout be resolved. For functions to do this validation as soon as the function type is created would lead to dependency loops in cases like '*const fn (*@This()) void callconv(.c)`. Therefore, when creating a function *type*, we no longer perform this check immediately, instead waiting until the function is called.

3 files changed, 156 insertions(+), 109 deletions(-)

src/Sema.zig+152-106
...@@ -5747,6 +5747,7 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void...@@ -5747,6 +5747,7 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
5747 }5747 }
57485748
5749 const export_ty = ptr_ty.childType(zcu);5749 const export_ty = ptr_ty.childType(zcu);
5750 try sema.ensureLayoutResolved(export_ty, src, .@"export");
5750 if (!export_ty.validateExtern(.other, zcu)) {5751 if (!export_ty.validateExtern(.other, zcu)) {
5751 return sema.failWithOwnedErrorMsg(block, msg: {5752 return sema.failWithOwnedErrorMsg(block, msg: {
5752 const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)});5753 const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)});
...@@ -6749,6 +6750,37 @@ fn analyzeCall(...@@ -6749,6 +6750,37 @@ fn analyzeCall(
6749 } else func_src;6750 } else func_src;
67506751
6751 const func_ty_info = zcu.typeToFunc(func_ty).?;6752 const func_ty_info = zcu.typeToFunc(func_ty).?;
6753
6754 for (func_ty_info.param_types.get(ip), 0..) |param_ty_ip, param_index| {
6755 const arg_src = args_info.argSrc(block, param_index);
6756 try sema.ensureLayoutResolved(.fromInterned(param_ty_ip), arg_src, .init);
6757 }
6758 try sema.ensureLayoutResolved(.fromInterned(func_ty_info.return_type), func_ret_ty_src, .return_type);
6759 try sema.validateResolvedFuncType(
6760 block,
6761 func_ty_info.cc,
6762 func_ty_info.param_types.get(ip),
6763 .fromInterned(func_ty_info.return_type),
6764 func_src,
6765 maybe_func_inst,
6766 );
6767
6768 if (!callConvIsCallable(func_ty_info.cc)) {
6769 return sema.failWithOwnedErrorMsg(block, msg: {
6770 const msg = try sema.errMsg(
6771 func_src,
6772 "unable to call function with calling convention '{s}'",
6773 .{@tagName(func_ty_info.cc)},
6774 );
6775 errdefer msg.destroy(gpa);
6776 if (maybe_func_inst) |func_inst| try sema.errNote(.{
6777 .base_node_inst = func_inst,
6778 .offset = .nodeOffset(.zero),
6779 }, msg, "function declared here", .{});
6780 break :msg msg;
6781 });
6782 }
6783
6752 const any_comptime_params = func_ty_info.comptime_bits != 0 or ct: {6784 const any_comptime_params = func_ty_info.comptime_bits != 0 or ct: {
6753 for (func_ty_info.param_types.get(ip)) |param_ty| {6785 for (func_ty_info.param_types.get(ip)) |param_ty| {
6754 if (Type.fromInterned(param_ty).comptimeOnly(zcu)) break :ct true;6786 if (Type.fromInterned(param_ty).comptimeOnly(zcu)) break :ct true;
...@@ -6771,22 +6803,6 @@ fn analyzeCall(...@@ -6771,22 +6803,6 @@ fn analyzeCall(
6771 break :generic false;6803 break :generic false;
6772 };6804 };
67736805
6774 if (!callConvIsCallable(func_ty_info.cc)) {
6775 return sema.failWithOwnedErrorMsg(block, msg: {
6776 const msg = try sema.errMsg(
6777 func_src,
6778 "unable to call function with calling convention '{s}'",
6779 .{@tagName(func_ty_info.cc)},
6780 );
6781 errdefer msg.destroy(gpa);
6782 if (maybe_func_inst) |func_inst| try sema.errNote(.{
6783 .base_node_inst = func_inst,
6784 .offset = .nodeOffset(.zero),
6785 }, msg, "function declared here", .{});
6786 break :msg msg;
6787 });
6788 }
6789
6790 // We need this value in a few code paths.6806 // We need this value in a few code paths.
6791 const callee_val = try sema.resolveDefinedValue(block, call_src, callee);6807 const callee_val = try sema.resolveDefinedValue(block, call_src, callee);
6792 // If the callee is a comptime-known *non-extern* function, `func_val` is populated.6808 // If the callee is a comptime-known *non-extern* function, `func_val` is populated.
...@@ -8755,11 +8771,12 @@ fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc:...@@ -8755,11 +8771,12 @@ fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc:
8755 }8771 }
8756}8772}
87578773
8758fn checkParamTypeCommon(8774fn checkParamType(
8759 sema: *Sema,8775 sema: *Sema,
8760 block: *Block,8776 block: *Block,
8761 param_idx: u32,8777 param_idx: u32,
8762 param_ty: Type,8778 param_ty: Type,
8779 param_is_comptime: bool,
8763 param_is_noalias: bool,8780 param_is_noalias: bool,
8764 param_src: LazySrcLoc,8781 param_src: LazySrcLoc,
8765 cc: std.builtin.CallingConvention,8782 cc: std.builtin.CallingConvention,
...@@ -8774,29 +8791,22 @@ fn checkParamTypeCommon(...@@ -8774,29 +8791,22 @@ fn checkParamTypeCommon(
8774 opaque_str, param_ty.fmt(pt),8791 opaque_str, param_ty.fmt(pt),
8775 });8792 });
8776 }8793 }
8777 if (!param_ty.isGenericPoison() and8794 if (!target_util.fnCallConvAllowsZigTypes(cc)) {
8778 !target_util.fnCallConvAllowsZigTypes(cc) and8795 if (param_is_comptime) {
8779 !param_ty.validateExtern(.param_ty, zcu))8796 return sema.fail(block, param_src, "comptime parameters not allowed in function with calling convention '{t}'", .{cc});
8780 {8797 }
8781 return sema.failWithOwnedErrorMsg(block, msg: {8798 if (param_ty.isGenericPoison()) {
8782 const msg = try sema.errMsg(param_src, "parameter of type '{f}' not allowed in function with calling convention '{s}'", .{8799 return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{t}'", .{cc});
8783 param_ty.fmt(pt), @tagName(cc),8800 }
8784 });8801 // The `validateExtern` check happens later, in `validateResolvedFuncType`.
8785 errdefer msg.destroy(sema.gpa);
8786
8787 try sema.explainWhyTypeIsNotExtern(msg, param_src, param_ty, .param_ty);
8788
8789 try sema.addDeclaredHereNote(msg, param_ty);
8790 break :msg msg;
8791 });
8792 }8802 }
8793 switch (cc) {8803 switch (cc) {
8794 .x86_64_interrupt, .x86_interrupt => {8804 .x86_64_interrupt, .x86_interrupt => {
8795 const err_code_size = target.ptrBitWidth();8805 const err_code_size = target.ptrBitWidth();
8796 switch (param_idx) {8806 switch (param_idx) {
8797 0 => if (param_ty.zigTypeTag(zcu) != .pointer) return sema.fail(block, param_src, "first parameter of function with '{s}' calling convention must be a pointer type", .{@tagName(cc)}),8807 0 => if (param_ty.zigTypeTag(zcu) != .pointer) return sema.fail(block, param_src, "first parameter of function with '{t}' calling convention must be a pointer type", .{cc}),
8798 1 => if (param_ty.bitSize(zcu) != err_code_size) return sema.fail(block, param_src, "second parameter of function with '{s}' calling convention must be a {d}-bit integer", .{ @tagName(cc), err_code_size }),8808 1 => if (param_ty.bitSize(zcu) != err_code_size) return sema.fail(block, param_src, "second parameter of function with '{t}' calling convention must be a {d}-bit integer", .{ cc, err_code_size }),
8799 else => return sema.fail(block, param_src, "'{s}' calling convention supports up to 2 parameters, found {d}", .{ @tagName(cc), param_idx + 1 }),8809 else => return sema.fail(block, param_src, "'{t}' calling convention supports up to 2 parameters, found {d}", .{ cc, param_idx + 1 }),
8800 }8810 }
8801 },8811 },
8802 .arc_interrupt,8812 .arc_interrupt,
...@@ -8812,7 +8822,7 @@ fn checkParamTypeCommon(...@@ -8812,7 +8822,7 @@ fn checkParamTypeCommon(
8812 .m68k_interrupt,8822 .m68k_interrupt,
8813 .msp430_interrupt,8823 .msp430_interrupt,
8814 .avr_signal,8824 .avr_signal,
8815 => return sema.fail(block, param_src, "parameters are not allowed with '{s}' calling convention", .{@tagName(cc)}),8825 => return sema.fail(block, param_src, "parameters are not allowed with '{t}' calling convention", .{cc}),
8816 else => {},8826 else => {},
8817 }8827 }
8818 if (param_is_noalias and !param_ty.isGenericPoison() and !param_ty.isPtrAtRuntime(zcu) and !param_ty.isSliceAtRuntime(zcu)) {8828 if (param_is_noalias and !param_ty.isGenericPoison() and !param_ty.isPtrAtRuntime(zcu) and !param_ty.isSliceAtRuntime(zcu)) {
...@@ -8820,7 +8830,7 @@ fn checkParamTypeCommon(...@@ -8820,7 +8830,7 @@ fn checkParamTypeCommon(
8820 }8830 }
8821}8831}
88228832
8823fn checkReturnTypeAndCallConvCommon(8833fn checkReturnTypeAndCallConv(
8824 sema: *Sema,8834 sema: *Sema,
8825 block: *Block,8835 block: *Block,
8826 bare_ret_ty: Type,8836 bare_ret_ty: Type,
...@@ -8834,7 +8844,6 @@ fn checkReturnTypeAndCallConvCommon(...@@ -8834,7 +8844,6 @@ fn checkReturnTypeAndCallConvCommon(
8834) CompileError!void {8844) CompileError!void {
8835 const pt = sema.pt;8845 const pt = sema.pt;
8836 const zcu = pt.zcu;8846 const zcu = pt.zcu;
8837 const gpa = zcu.gpa;
8838 if (opt_varargs_src) |varargs_src| {8847 if (opt_varargs_src) |varargs_src| {
8839 try sema.checkCallConvSupportsVarArgs(block, varargs_src, @"callconv");8848 try sema.checkCallConvSupportsVarArgs(block, varargs_src, @"callconv");
8840 }8849 }
...@@ -8848,21 +8857,14 @@ fn checkReturnTypeAndCallConvCommon(...@@ -8848,21 +8857,14 @@ fn checkReturnTypeAndCallConvCommon(
8848 opaque_str, ies_ret_ty_prefix, bare_ret_ty.fmt(pt),8857 opaque_str, ies_ret_ty_prefix, bare_ret_ty.fmt(pt),
8849 });8858 });
8850 }8859 }
8851 if (!bare_ret_ty.isGenericPoison() and8860 if (!target_util.fnCallConvAllowsZigTypes(@"callconv")) {
8852 !target_util.fnCallConvAllowsZigTypes(@"callconv") and8861 if (inferred_error_set) {
8853 (inferred_error_set or !bare_ret_ty.validateExtern(.ret_ty, zcu)))8862 return sema.fail(block, ret_ty_src, "return type '!{f}' not allowed in function with calling convention '{t}'", .{ bare_ret_ty.fmt(pt), @"callconv" });
8854 {8863 }
8855 return sema.failWithOwnedErrorMsg(block, msg: {8864 if (bare_ret_ty.isGenericPoison()) {
8856 const msg = try sema.errMsg(ret_ty_src, "return type '{s}{f}' not allowed in function with calling convention '{s}'", .{8865 return sema.fail(block, ret_ty_src, "generic return type not allowed in function with calling convention '{t}'", .{@"callconv"});
8857 ies_ret_ty_prefix, bare_ret_ty.fmt(pt), @tagName(@"callconv"),8866 }
8858 });8867 // The `validateExtern` check happens later, in `validateResolvedFuncType`.
8859 errdefer msg.destroy(gpa);
8860 if (!inferred_error_set) {
8861 try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src, bare_ret_ty, .ret_ty);
8862 try sema.addDeclaredHereNote(msg, bare_ret_ty);
8863 }
8864 break :msg msg;
8865 });
8866 }8868 }
8867 validate_incoming_stack_align: {8869 validate_incoming_stack_align: {
8868 const a: u64 = switch (@"callconv") {8870 const a: u64 = switch (@"callconv") {
...@@ -8897,7 +8899,7 @@ fn checkReturnTypeAndCallConvCommon(...@@ -8897,7 +8899,7 @@ fn checkReturnTypeAndCallConvCommon(
8897 else => false,8899 else => false,
8898 };8900 };
8899 if (!ret_ok) {8901 if (!ret_ok) {
8900 return sema.fail(block, ret_ty_src, "function with calling convention '{s}' must return 'void' or 'noreturn'", .{@tagName(@"callconv")});8902 return sema.fail(block, ret_ty_src, "function with calling convention '{t}' must return 'void' or 'noreturn'", .{@"callconv"});
8901 }8903 }
8902 },8904 },
8903 .@"inline" => if (is_noinline) {8905 .@"inline" => if (is_noinline) {
...@@ -8918,18 +8920,76 @@ fn checkReturnTypeAndCallConvCommon(...@@ -8918,18 +8920,76 @@ fn checkReturnTypeAndCallConvCommon(
8918 }8920 }
8919 }8921 }
8920 };8922 };
8921 return sema.fail(block, callconv_src, "calling convention '{s}' only available on architectures {f}", .{8923 return sema.fail(block, callconv_src, "calling convention '{t}' only available on architectures {f}", .{
8922 @tagName(@"callconv"),8924 @"callconv", ArchListFormatter{ .archs = allowed_archs },
8923 ArchListFormatter{ .archs = allowed_archs },
8924 });8925 });
8925 },8926 },
8926 .bad_backend => |bad_backend| return sema.fail(block, callconv_src, "calling convention '{s}' not supported by compiler backend '{s}'", .{8927 .bad_backend => |bad_backend| return sema.fail(block, callconv_src, "calling convention '{t}' not supported by compiler backend '{t}'", .{
8927 @tagName(@"callconv"),8928 @"callconv", bad_backend,
8928 @tagName(bad_backend),
8929 }),8929 }),
8930 }8930 }
8931}8931}
89328932
8933/// To avoid forcing type layout resolution too quickly, some validation of function types cannot be
8934/// performed when the type is first constructed, and instead must happen when either (a) a function
8935/// with that type is declared, or (b) a function with that type is called. That validation is
8936/// handled here.
8937///
8938/// Asserts that all parameter types and return types have their layout fully resolved.
8939fn validateResolvedFuncType(
8940 sema: *Sema,
8941 block: *Block,
8942 @"callconv": std.builtin.CallingConvention,
8943 param_types: []const InternPool.Index,
8944 ret_ty: Type,
8945 src: LazySrcLoc,
8946 maybe_func_decl_inst: ?InternPool.TrackedInst.Index,
8947) SemaError!void {
8948 const pt = sema.pt;
8949 const zcu = pt.zcu;
8950 const gpa = zcu.comp.gpa;
8951 if (!target_util.fnCallConvAllowsZigTypes(@"callconv")) {
8952 // Check that all parameter types are extern-compatible.
8953 for (param_types, 0..) |param_ty_ip, param_index| {
8954 const param_ty: Type = .fromInterned(param_ty_ip);
8955 if (!param_ty.validateExtern(.param_ty, zcu)) {
8956 const param_src: LazySrcLoc = if (maybe_func_decl_inst) |inst| .{
8957 .base_node_inst = inst,
8958 .offset = .{ .fn_proto_param = .{
8959 .fn_proto_node_offset = .zero,
8960 .param_index = @intCast(param_index),
8961 } },
8962 } else src;
8963 return sema.failWithOwnedErrorMsg(block, msg: {
8964 const msg = try sema.errMsg(param_src, "parameter of type '{f}' not allowed in function with calling convention '{t}'", .{
8965 param_ty.fmt(pt), @"callconv",
8966 });
8967 errdefer msg.destroy(gpa);
8968 try sema.explainWhyTypeIsNotExtern(msg, param_src, param_ty, .param_ty);
8969 try sema.addDeclaredHereNote(msg, param_ty);
8970 break :msg msg;
8971 });
8972 }
8973 }
8974 // Check that the return type is extern-compatible.
8975 if (!ret_ty.validateExtern(.ret_ty, zcu)) {
8976 const ret_ty_src: LazySrcLoc = if (maybe_func_decl_inst) |inst| .{
8977 .base_node_inst = inst,
8978 .offset = .{ .node_offset_fn_type_ret_ty = .zero },
8979 } else src;
8980 return sema.failWithOwnedErrorMsg(block, msg: {
8981 const msg = try sema.errMsg(ret_ty_src, "return type '{f}' not allowed in function with calling convention '{t}'", .{
8982 ret_ty.fmt(pt), @"callconv",
8983 });
8984 errdefer msg.destroy(gpa);
8985 try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src, ret_ty, .ret_ty);
8986 try sema.addDeclaredHereNote(msg, ret_ty);
8987 break :msg msg;
8988 });
8989 }
8990 }
8991}
8992
8933fn callConvIsCallable(cc: std.builtin.CallingConvention.Tag) bool {8993fn callConvIsCallable(cc: std.builtin.CallingConvention.Tag) bool {
8934 return switch (cc) {8994 return switch (cc) {
8935 .naked,8995 .naked,
...@@ -9022,6 +9082,7 @@ fn funcCommon(...@@ -9022,6 +9082,7 @@ fn funcCommon(
9022 const io = comp.io;9082 const io = comp.io;
9023 const ip = &zcu.intern_pool;9083 const ip = &zcu.intern_pool;
90249084
9085 const src = block.nodeOffset(src_node_offset);
9025 const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = src_node_offset });9086 const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = src_node_offset });
9026 const cc_src = block.src(.{ .node_offset_fn_type_cc = src_node_offset });9087 const cc_src = block.src(.{ .node_offset_fn_type_cc = src_node_offset });
90279088
...@@ -9036,27 +9097,21 @@ fn funcCommon(...@@ -9036,27 +9097,21 @@ fn funcCommon(
9036 .fn_proto_node_offset = src_node_offset,9097 .fn_proto_node_offset = src_node_offset,
9037 .param_index = @intCast(i),9098 .param_index = @intCast(i),
9038 } });9099 } });
9039 const param_ty_generic = param_ty.isGenericPoison();
9040 if (param_is_comptime) {9100 if (param_is_comptime) {
9041 comptime_bits |= @as(u32, 1) << @intCast(i); // TODO: handle cast error9101 comptime_bits |= @as(u32, 1) << @intCast(i); // TODO: handle cast error
9042 }9102 }
9043 if (param_is_comptime and !target_util.fnCallConvAllowsZigTypes(cc)) {9103 try sema.checkParamType(
9044 return sema.fail(block, param_src, "comptime parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)});
9045 }
9046 if (param_ty_generic and !target_util.fnCallConvAllowsZigTypes(cc)) {
9047 return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)});
9048 }
9049 try sema.checkParamTypeCommon(
9050 block,9104 block,
9051 @intCast(i),9105 @intCast(i),
9052 param_ty,9106 param_ty,
9107 param_is_comptime,
9053 is_noalias,9108 is_noalias,
9054 param_src,9109 param_src,
9055 cc,9110 cc,
9056 );9111 );
9057 }9112 }
90589113
9059 try sema.checkReturnTypeAndCallConvCommon(9114 try sema.checkReturnTypeAndCallConv(
9060 block,9115 block,
9061 bare_return_type,9116 bare_return_type,
9062 ret_ty_src,9117 ret_ty_src,
...@@ -9072,9 +9127,29 @@ fn funcCommon(...@@ -9072,9 +9127,29 @@ fn funcCommon(
90729127
9073 const param_types = block.params.items(.ty);9128 const param_types = block.params.items(.ty);
90749129
9130 if (has_body) {
9131 for (param_types, 0..) |param_ty_ip, param_index| {
9132 const param_ty: Type = .fromInterned(param_ty_ip);
9133 const param_src = block.src(.{ .fn_proto_param = .{
9134 .fn_proto_node_offset = src_node_offset,
9135 .param_index = @intCast(param_index),
9136 } });
9137 try sema.ensureLayoutResolved(param_ty, param_src, .parameter);
9138 }
9139 try sema.ensureLayoutResolved(bare_return_type, ret_ty_src, .return_type);
9140 try sema.validateResolvedFuncType(
9141 block,
9142 cc,
9143 param_types,
9144 bare_return_type,
9145 src,
9146 ip.getNav(sema.owner.unwrap().nav_val).srcInst(ip),
9147 );
9148 }
9149
9075 if (inferred_error_set) {9150 if (inferred_error_set) {
9076 assert(has_body);9151 assert(has_body);
9077 const func_val: Value = .fromInterned(try ip.getFuncDeclIes(gpa, io, pt.tid, .{9152 return .fromIntern(try ip.getFuncDeclIes(gpa, io, pt.tid, .{
9078 .owner_nav = sema.owner.unwrap().nav_val,9153 .owner_nav = sema.owner.unwrap().nav_val,
90799154
9080 .param_types = param_types,9155 .param_types = param_types,
...@@ -9091,8 +9166,6 @@ fn funcCommon(...@@ -9091,8 +9166,6 @@ fn funcCommon(
9091 .lbrace_column = @as(u16, @truncate(src_locs.columns)),9166 .lbrace_column = @as(u16, @truncate(src_locs.columns)),
9092 .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)),9167 .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)),
9093 }));9168 }));
9094 try sema.ensureLayoutResolved(func_val.typeOf(zcu), ret_ty_src, .return_type);
9095 return .fromValue(func_val);
9096 }9169 }
90979170
9098 const func_ty = try ip.getFuncType(gpa, io, pt.tid, .{9171 const func_ty = try ip.getFuncType(gpa, io, pt.tid, .{
...@@ -9106,7 +9179,6 @@ fn funcCommon(...@@ -9106,7 +9179,6 @@ fn funcCommon(
9106 });9179 });
91079180
9108 if (has_body) {9181 if (has_body) {
9109 try sema.ensureLayoutResolved(.fromInterned(func_ty), ret_ty_src, .return_type);
9110 return .fromIntern(try ip.getFuncDecl(gpa, io, pt.tid, .{9182 return .fromIntern(try ip.getFuncDecl(gpa, io, pt.tid, .{
9111 .owner_nav = sema.owner.unwrap().nav_val,9183 .owner_nav = sema.owner.unwrap().nav_val,
9112 .ty = func_ty,9184 .ty = func_ty,
...@@ -18256,19 +18328,6 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -18256,19 +18328,6 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
18256 }18328 }
18257 } else if (inst_data.size != .one and elem_ty.zigTypeTag(zcu) == .@"opaque") {18329 } else if (inst_data.size != .one and elem_ty.zigTypeTag(zcu) == .@"opaque") {
18258 return sema.fail(block, elem_ty_src, "indexable pointer to opaque type '{f}' not allowed", .{elem_ty.fmt(pt)});18330 return sema.fail(block, elem_ty_src, "indexable pointer to opaque type '{f}' not allowed", .{elem_ty.fmt(pt)});
18259 } else if (inst_data.size == .c) {
18260 if (!elem_ty.validateExtern(.other, zcu)) {
18261 const msg = msg: {
18262 const msg = try sema.errMsg(elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{f}'", .{elem_ty.fmt(pt)});
18263 errdefer msg.destroy(sema.gpa);
18264
18265 try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src, elem_ty, .other);
18266
18267 try sema.addDeclaredHereNote(msg, elem_ty);
18268 break :msg msg;
18269 };
18270 return sema.failWithOwnedErrorMsg(block, msg);
18271 }
18272 }18331 }
1827318332
18274 if (host_size != 0) {18333 if (host_size != 0) {
...@@ -19800,16 +19859,6 @@ fn zirReifyPointer(...@@ -19800,16 +19859,6 @@ fn zirReifyPointer(
19800 else => {},19859 else => {},
19801 }19860 }
1980219861
19803 if (size == .c and !elem_ty.validateExtern(.other, zcu)) {
19804 return sema.failWithOwnedErrorMsg(block, msg: {
19805 const msg = try sema.errMsg(src, "C pointers cannot point to non-C-ABI-compatible type '{f}'", .{elem_ty.fmt(pt)});
19806 errdefer msg.destroy(gpa);
19807 try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src, elem_ty, .other);
19808 try sema.addDeclaredHereNote(msg, elem_ty);
19809 break :msg msg;
19810 });
19811 }
19812
19813 const sentinel_ty = try pt.optionalType(elem_ty.toIntern());19862 const sentinel_ty = try pt.optionalType(elem_ty.toIntern());
19814 const sentinel_uncoerced = sema.resolveInst(extra.sentinel);19863 const sentinel_uncoerced = sema.resolveInst(extra.sentinel);
19815 const sentinel_coerced = try sema.coerce(block, sentinel_ty, sentinel_uncoerced, sentinel_src);19864 const sentinel_coerced = try sema.coerce(block, sentinel_ty, sentinel_uncoerced, sentinel_src);
...@@ -19898,10 +19947,11 @@ fn zirReifyFn(...@@ -19898,10 +19947,11 @@ fn zirReifyFn(
19898 try param_attrs_arr.elemValue(pt, param_idx),19947 try param_attrs_arr.elemValue(pt, param_idx),
19899 std.builtin.Type.Fn.Param.Attributes,19948 std.builtin.Type.Fn.Param.Attributes,
19900 );19949 );
19901 try sema.checkParamTypeCommon(19950 try sema.checkParamType(
19902 block,19951 block,
19903 @intCast(param_idx),19952 @intCast(param_idx),
19904 param_ty,19953 param_ty,
19954 false,
19905 param_attrs.@"noalias",19955 param_attrs.@"noalias",
19906 param_types_src,19956 param_types_src,
19907 fn_attrs.@"callconv",19957 fn_attrs.@"callconv",
...@@ -19919,7 +19969,7 @@ fn zirReifyFn(...@@ -19919,7 +19969,7 @@ fn zirReifyFn(
19919 try sema.checkCallConvSupportsVarArgs(block, fn_attrs_src, fn_attrs.@"callconv");19969 try sema.checkCallConvSupportsVarArgs(block, fn_attrs_src, fn_attrs.@"callconv");
19920 }19970 }
1992119971
19922 try sema.checkReturnTypeAndCallConvCommon(19972 try sema.checkReturnTypeAndCallConv(
19923 block,19973 block,
19924 ret_ty,19974 ret_ty,
19925 ret_ty_src,19975 ret_ty_src,
...@@ -19929,9 +19979,6 @@ fn zirReifyFn(...@@ -19929,9 +19979,6 @@ fn zirReifyFn(
19929 false,19979 false,
19930 false,19980 false,
19931 );19981 );
19932 if (ret_ty.comptimeOnly(zcu)) {
19933 return sema.fail(block, param_attrs_src, "cannot reify function type with comptime-only return type '{f}'", .{ret_ty.fmt(pt)});
19934 }
1993519982
19936 return .fromIntern(try ip.getFuncType(gpa, io, pt.tid, .{19983 return .fromIntern(try ip.getFuncType(gpa, io, pt.tid, .{
19937 .param_types = param_types_ip,19984 .param_types = param_types_ip,
...@@ -20615,7 +20662,7 @@ fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) C...@@ -20615,7 +20662,7 @@ fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) C
2061520662
20616 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.lhs);20663 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.lhs);
20617 const arg_ty = try sema.resolveType(block, ty_src, extra.rhs);20664 const arg_ty = try sema.resolveType(block, ty_src, extra.rhs);
2061820665 try sema.ensureLayoutResolved(arg_ty, ty_src, .parameter);
20619 if (!arg_ty.validateExtern(.param_ty, sema.pt.zcu)) {20666 if (!arg_ty.validateExtern(.param_ty, sema.pt.zcu)) {
20620 const msg = msg: {20667 const msg = msg: {
20621 const msg = try sema.errMsg(ty_src, "cannot get '{f}' from variadic argument", .{arg_ty.fmt(sema.pt)});20668 const msg = try sema.errMsg(ty_src, "cannot get '{f}' from variadic argument", .{arg_ty.fmt(sema.pt)});
...@@ -24639,13 +24686,12 @@ fn zirBuiltinExtern(...@@ -24639,13 +24686,12 @@ fn zirBuiltinExtern(
24639 return sema.fail(block, ty_src, "expected (optional) pointer", .{});24686 return sema.fail(block, ty_src, "expected (optional) pointer", .{});
24640 }24687 }
24641 if (!ty.validateExtern(.other, zcu)) {24688 if (!ty.validateExtern(.other, zcu)) {
24642 const msg = msg: {24689 return sema.failWithOwnedErrorMsg(block, msg: {
24643 const msg = try sema.errMsg(ty_src, "extern symbol cannot have type '{f}'", .{ty.fmt(pt)});24690 const msg = try sema.errMsg(ty_src, "extern symbol cannot have type '{f}'", .{ty.fmt(pt)});
24644 errdefer msg.destroy(sema.gpa);24691 errdefer msg.destroy(sema.gpa);
24645 try sema.explainWhyTypeIsNotExtern(msg, ty_src, ty, .other);24692 try sema.explainWhyTypeIsNotExtern(msg, ty_src, ty, .other);
24646 break :msg msg;24693 break :msg msg;
24647 };24694 });
24648 return sema.failWithOwnedErrorMsg(block, msg);
24649 }24695 }
2465024696
24651 const options = try sema.resolveExternOptions(block, options_src, extra.rhs);24697 const options = try sema.resolveExternOptions(block, options_src, extra.rhs);
...@@ -25034,7 +25080,7 @@ pub fn explainWhyTypeIsNotExtern(...@@ -25034,7 +25080,7 @@ pub fn explainWhyTypeIsNotExtern(
25034 src_loc: LazySrcLoc,25080 src_loc: LazySrcLoc,
25035 ty: Type,25081 ty: Type,
25036 position: Type.ExternPosition,25082 position: Type.ExternPosition,
25037) CompileError!void {25083) SemaError!void {
25038 const pt = sema.pt;25084 const pt = sema.pt;
25039 const zcu = pt.zcu;25085 const zcu = pt.zcu;
25040 switch (ty.zigTypeTag(zcu)) {25086 switch (ty.zigTypeTag(zcu)) {
src/Sema/type_resolution.zig+2-2
...@@ -32,6 +32,7 @@ pub const LayoutResolveReason = enum {...@@ -32,6 +32,7 @@ pub const LayoutResolveReason = enum {
32 type_info,32 type_info,
33 align_check,33 align_check,
34 bit_ptr_child,34 bit_ptr_child,
35 @"export",
35 builtin_type,36 builtin_type,
3637
37 /// Written after string: "while resolving type 'T' "38 /// Written after string: "while resolving type 'T' "
...@@ -56,6 +57,7 @@ pub const LayoutResolveReason = enum {...@@ -56,6 +57,7 @@ pub const LayoutResolveReason = enum {
56 .type_info => "for type information query here",57 .type_info => "for type information query here",
57 .align_check => "for alignment check here",58 .align_check => "for alignment check here",
58 .bit_ptr_child => "for bit size check here",59 .bit_ptr_child => "for bit size check here",
60 .@"export" => "for export here",
59 .builtin_type => "from 'std.builtin'",61 .builtin_type => "from 'std.builtin'",
60 // zig fmt: on62 // zig fmt: on
61 };63 };
...@@ -276,7 +278,6 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {...@@ -276,7 +278,6 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {
276 assert(!field_ty.isGenericPoison());278 assert(!field_ty.isGenericPoison());
277 const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) });279 const field_ty_src = block.src(.{ .container_field_type = @intCast(field_index) });
278 try sema.ensureLayoutResolved(field_ty, field_ty_src, .field);280 try sema.ensureLayoutResolved(field_ty, field_ty_src, .field);
279
280 if (field_ty.zigTypeTag(zcu) == .@"opaque") {281 if (field_ty.zigTypeTag(zcu) == .@"opaque") {
281 return sema.failWithOwnedErrorMsg(&block, msg: {282 return sema.failWithOwnedErrorMsg(&block, msg: {
282 const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in struct", .{field_ty.fmt(pt)});283 const msg = try sema.errMsg(field_ty_src, "cannot directly embed opaque type '{f}' in struct", .{field_ty.fmt(pt)});
...@@ -286,7 +287,6 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {...@@ -286,7 +287,6 @@ pub fn resolveStructLayout(sema: *Sema, struct_ty: Type) CompileError!void {
286 break :msg msg;287 break :msg msg;
287 });288 });
288 }289 }
289
290 if (struct_obj.layout == .@"extern" and !field_ty.validateExtern(.struct_field, zcu)) {290 if (struct_obj.layout == .@"extern" and !field_ty.validateExtern(.struct_field, zcu)) {
291 return sema.failWithOwnedErrorMsg(&block, msg: {291 return sema.failWithOwnedErrorMsg(&block, msg: {
292 const msg = try sema.errMsg(field_ty_src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});292 const msg = try sema.errMsg(field_ty_src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
src/Type.zig+2-1
...@@ -3055,9 +3055,10 @@ pub const ExternPosition = enum {...@@ -3055,9 +3055,10 @@ pub const ExternPosition = enum {
3055};3055};
30563056
3057/// Returns true if `ty` is allowed in extern types.3057/// Returns true if `ty` is allowed in extern types.
3058/// Does not require `ty` to be resolved in any way.3058/// Asserts that `ty` is fully resolved.
3059/// Keep in sync with `Sema.explainWhyTypeIsNotExtern`.3059/// Keep in sync with `Sema.explainWhyTypeIsNotExtern`.
3060pub fn validateExtern(ty: Type, position: ExternPosition, zcu: *const Zcu) bool {3060pub fn validateExtern(ty: Type, position: ExternPosition, zcu: *const Zcu) bool {
3061 ty.assertHasLayout(zcu);
3061 return switch (ty.zigTypeTag(zcu)) {3062 return switch (ty.zigTypeTag(zcu)) {
3062 .type,3063 .type,
3063 .comptime_float,3064 .comptime_float,