authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-30 14:16:28-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-30 14:16:28-07:00
logbd89a73d5289536948b052eb7f052d6de193441b
tree39930f14ff7576884e67ed54a3d3f06ec8ab2785
parent7e98b047ddc51062a32cbf2fb852d38e5023886c

Sema: implement functions generic across callconv() or align()


3 files changed, 125 insertions(+), 40 deletions(-)

src/Sema.zig+90-28
...@@ -6547,7 +6547,7 @@ fn zirFunc(...@@ -6547,7 +6547,7 @@ fn zirFunc(
6547 inst,6547 inst,
6548 0,6548 0,
6549 target_util.defaultAddressSpace(target, .function),6549 target_util.defaultAddressSpace(target, .function),
6550 null,6550 FuncLinkSection.default,
6551 cc,6551 cc,
6552 ret_ty,6552 ret_ty,
6553 false,6553 false,
...@@ -6660,15 +6660,26 @@ fn handleExternLibName(...@@ -6660,15 +6660,26 @@ fn handleExternLibName(
6660 return sema.gpa.dupeZ(u8, lib_name);6660 return sema.gpa.dupeZ(u8, lib_name);
6661}6661}
66626662
6663const FuncLinkSection = union(enum) {
6664 generic,
6665 default,
6666 explicit: [*:0]const u8,
6667};
6668
6663fn funcCommon(6669fn funcCommon(
6664 sema: *Sema,6670 sema: *Sema,
6665 block: *Block,6671 block: *Block,
6666 src_node_offset: i32,6672 src_node_offset: i32,
6667 func_inst: Zir.Inst.Index,6673 func_inst: Zir.Inst.Index,
6668 alignment: u32,6674 /// null means generic poison
6669 address_space: std.builtin.AddressSpace,6675 alignment: ?u32,
6670 section: ?[*:0]const u8,6676 /// null means generic poison
6671 cc: std.builtin.CallingConvention,6677 address_space: ?std.builtin.AddressSpace,
6678 /// outer null means generic poison; inner null means default link section
6679 section: FuncLinkSection,
6680 /// null means generic poison
6681 cc: ?std.builtin.CallingConvention,
6682 /// this might be Type.generic_poison
6672 bare_return_type: Type,6683 bare_return_type: Type,
6673 var_args: bool,6684 var_args: bool,
6674 inferred_error_set: bool,6685 inferred_error_set: bool,
...@@ -6679,7 +6690,11 @@ fn funcCommon(...@@ -6679,7 +6690,11 @@ fn funcCommon(
6679) CompileError!Air.Inst.Ref {6690) CompileError!Air.Inst.Ref {
6680 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };6691 const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };
66816692
6682 var is_generic = bare_return_type.tag() == .generic_poison;6693 var is_generic = bare_return_type.tag() == .generic_poison or
6694 alignment == null or
6695 address_space == null or
6696 section == .generic or
6697 cc == null;
6683 // Check for generic params.6698 // Check for generic params.
6684 for (block.params.items) |param| {6699 for (block.params.items) |param| {
6685 if (param.ty.tag() == .generic_poison) is_generic = true;6700 if (param.ty.tag() == .generic_poison) is_generic = true;
...@@ -6700,25 +6715,28 @@ fn funcCommon(...@@ -6700,25 +6715,28 @@ fn funcCommon(
6700 errdefer if (maybe_inferred_error_set_node) |node| sema.gpa.destroy(node);6715 errdefer if (maybe_inferred_error_set_node) |node| sema.gpa.destroy(node);
6701 // Note: no need to errdefer since this will still be in its default state at the end of the function.6716 // Note: no need to errdefer since this will still be in its default state at the end of the function.
67026717
6718 const target = sema.mod.getTarget();
6703 const fn_ty: Type = fn_ty: {6719 const fn_ty: Type = fn_ty: {
6704 // Hot path for some common function types.6720 // Hot path for some common function types.
6705 // TODO can we eliminate some of these Type tag values? seems unnecessarily complicated.6721 // TODO can we eliminate some of these Type tag values? seems unnecessarily complicated.
6706 if (!is_generic and block.params.items.len == 0 and !var_args and6722 if (!is_generic and block.params.items.len == 0 and !var_args and !inferred_error_set and
6707 alignment == 0 and !inferred_error_set)6723 alignment.? == 0 and
6724 address_space.? == target_util.defaultAddressSpace(target, .function) and
6725 section == .default)
6708 {6726 {
6709 if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) {6727 if (bare_return_type.zigTypeTag() == .NoReturn and cc.? == .Unspecified) {
6710 break :fn_ty Type.initTag(.fn_noreturn_no_args);6728 break :fn_ty Type.initTag(.fn_noreturn_no_args);
6711 }6729 }
67126730
6713 if (bare_return_type.zigTypeTag() == .Void and cc == .Unspecified) {6731 if (bare_return_type.zigTypeTag() == .Void and cc.? == .Unspecified) {
6714 break :fn_ty Type.initTag(.fn_void_no_args);6732 break :fn_ty Type.initTag(.fn_void_no_args);
6715 }6733 }
67166734
6717 if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Naked) {6735 if (bare_return_type.zigTypeTag() == .NoReturn and cc.? == .Naked) {
6718 break :fn_ty Type.initTag(.fn_naked_noreturn_no_args);6736 break :fn_ty Type.initTag(.fn_naked_noreturn_no_args);
6719 }6737 }
67206738
6721 if (bare_return_type.zigTypeTag() == .Void and cc == .C) {6739 if (bare_return_type.zigTypeTag() == .Void and cc.? == .C) {
6722 break :fn_ty Type.initTag(.fn_ccc_void_no_args);6740 break :fn_ty Type.initTag(.fn_ccc_void_no_args);
6723 }6741 }
6724 }6742 }
...@@ -6764,21 +6782,33 @@ fn funcCommon(...@@ -6764,21 +6782,33 @@ fn funcCommon(
6764 });6782 });
6765 };6783 };
67666784
6785 // stage1 bug workaround
6786 const cc_workaround = cc orelse undefined;
6787 const align_workaround = alignment orelse @as(u32, undefined);
6788
6767 break :fn_ty try Type.Tag.function.create(sema.arena, .{6789 break :fn_ty try Type.Tag.function.create(sema.arena, .{
6768 .param_types = param_types,6790 .param_types = param_types,
6769 .comptime_params = comptime_params.ptr,6791 .comptime_params = comptime_params.ptr,
6770 .return_type = return_type,6792 .return_type = return_type,
6771 .cc = cc,6793 .cc = cc_workaround,
6772 .alignment = alignment,6794 .cc_is_generic = cc == null,
6795 .alignment = align_workaround,
6796 .align_is_generic = alignment == null,
6797 .section_is_generic = section == .generic,
6798 .addrspace_is_generic = address_space == null,
6773 .is_var_args = var_args,6799 .is_var_args = var_args,
6774 .is_generic = is_generic,6800 .is_generic = is_generic,
6775 });6801 });
6776 };6802 };
67776803
6778 if (sema.owner_decl.owns_tv) {6804 if (sema.owner_decl.owns_tv) {
6779 sema.owner_decl.@"linksection" = section;6805 switch (section) {
6780 sema.owner_decl.@"align" = alignment;6806 .generic => sema.owner_decl.@"linksection" = undefined,
6781 sema.owner_decl.@"addrspace" = address_space;6807 .default => sema.owner_decl.@"linksection" = null,
6808 .explicit => |s| sema.owner_decl.@"linksection" = s,
6809 }
6810 if (alignment) |a| sema.owner_decl.@"align" = a;
6811 if (address_space) |a| sema.owner_decl.@"addrspace" = a;
6782 }6812 }
67836813
6784 if (is_extern) {6814 if (is_extern) {
...@@ -16780,13 +16810,16 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A...@@ -16780,13 +16810,16 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
16780 break :blk lib_name;16810 break :blk lib_name;
16781 } else null;16811 } else null;
1678216812
16783 const @"align": u32 = if (extra.data.bits.has_align_body) blk: {16813 const @"align": ?u32 = if (extra.data.bits.has_align_body) blk: {
16784 const body_len = sema.code.extra[extra_index];16814 const body_len = sema.code.extra[extra_index];
16785 extra_index += 1;16815 extra_index += 1;
16786 const body = sema.code.extra[extra_index..][0..body_len];16816 const body = sema.code.extra[extra_index..][0..body_len];
16787 extra_index += body.len;16817 extra_index += body.len;
1678816818
16789 const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u16);16819 const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u16);
16820 if (val.tag() == .generic_poison) {
16821 break :blk null;
16822 }
16790 const alignment = @intCast(u32, val.toUnsignedInt(target));16823 const alignment = @intCast(u32, val.toUnsignedInt(target));
16791 if (alignment == target_util.defaultFunctionAlignment(target)) {16824 if (alignment == target_util.defaultFunctionAlignment(target)) {
16792 break :blk 0;16825 break :blk 0;
...@@ -16796,7 +16829,12 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A...@@ -16796,7 +16829,12 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
16796 } else if (extra.data.bits.has_align_ref) blk: {16829 } else if (extra.data.bits.has_align_ref) blk: {
16797 const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);16830 const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
16798 extra_index += 1;16831 extra_index += 1;
16799 const align_tv = try sema.resolveInstConst(block, align_src, align_ref);16832 const align_tv = sema.resolveInstConst(block, align_src, align_ref) catch |err| switch (err) {
16833 error.GenericPoison => {
16834 break :blk null;
16835 },
16836 else => |e| return e,
16837 };
16800 const alignment = @intCast(u32, align_tv.val.toUnsignedInt(target));16838 const alignment = @intCast(u32, align_tv.val.toUnsignedInt(target));
16801 if (alignment == target_util.defaultFunctionAlignment(target)) {16839 if (alignment == target_util.defaultFunctionAlignment(target)) {
16802 break :blk 0;16840 break :blk 0;
...@@ -16805,7 +16843,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A...@@ -16805,7 +16843,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
16805 }16843 }
16806 } else 0;16844 } else 0;
1680716845
16808 const @"addrspace": std.builtin.AddressSpace = if (extra.data.bits.has_addrspace_body) blk: {16846 const @"addrspace": ?std.builtin.AddressSpace = if (extra.data.bits.has_addrspace_body) blk: {
16809 const body_len = sema.code.extra[extra_index];16847 const body_len = sema.code.extra[extra_index];
16810 extra_index += 1;16848 extra_index += 1;
16811 const body = sema.code.extra[extra_index..][0..body_len];16849 const body = sema.code.extra[extra_index..][0..body_len];
...@@ -16813,32 +16851,48 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A...@@ -16813,32 +16851,48 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1681316851
16814 const addrspace_ty = try sema.getBuiltinType(block, addrspace_src, "AddressSpace");16852 const addrspace_ty = try sema.getBuiltinType(block, addrspace_src, "AddressSpace");
16815 const val = try sema.resolveGenericBody(block, addrspace_src, body, inst, addrspace_ty);16853 const val = try sema.resolveGenericBody(block, addrspace_src, body, inst, addrspace_ty);
16854 if (val.tag() == .generic_poison) {
16855 break :blk null;
16856 }
16816 break :blk val.toEnum(std.builtin.AddressSpace);16857 break :blk val.toEnum(std.builtin.AddressSpace);
16817 } else if (extra.data.bits.has_addrspace_ref) blk: {16858 } else if (extra.data.bits.has_addrspace_ref) blk: {
16818 const addrspace_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);16859 const addrspace_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
16819 extra_index += 1;16860 extra_index += 1;
16820 const addrspace_tv = try sema.resolveInstConst(block, addrspace_src, addrspace_ref);16861 const addrspace_tv = sema.resolveInstConst(block, addrspace_src, addrspace_ref) catch |err| switch (err) {
16862 error.GenericPoison => {
16863 break :blk null;
16864 },
16865 else => |e| return e,
16866 };
16821 break :blk addrspace_tv.val.toEnum(std.builtin.AddressSpace);16867 break :blk addrspace_tv.val.toEnum(std.builtin.AddressSpace);
16822 } else target_util.defaultAddressSpace(target, .function);16868 } else target_util.defaultAddressSpace(target, .function);
1682316869
16824 const @"linksection": ?[*:0]const u8 = if (extra.data.bits.has_section_body) {16870 const @"linksection": FuncLinkSection = if (extra.data.bits.has_section_body) blk: {
16825 const body_len = sema.code.extra[extra_index];16871 const body_len = sema.code.extra[extra_index];
16826 extra_index += 1;16872 extra_index += 1;
16827 const body = sema.code.extra[extra_index..][0..body_len];16873 const body = sema.code.extra[extra_index..][0..body_len];
16828 extra_index += body.len;16874 extra_index += body.len;
1682916875
16830 const val = try sema.resolveGenericBody(block, section_src, body, inst, Type.initTag(.const_slice_u8));16876 const val = try sema.resolveGenericBody(block, section_src, body, inst, Type.initTag(.const_slice_u8));
16877 if (val.tag() == .generic_poison) {
16878 break :blk FuncLinkSection{ .generic = {} };
16879 }
16831 _ = val;16880 _ = val;
16832 return sema.fail(block, section_src, "TODO implement linksection on functions", .{});16881 return sema.fail(block, section_src, "TODO implement linksection on functions", .{});
16833 } else if (extra.data.bits.has_section_ref) {16882 } else if (extra.data.bits.has_section_ref) blk: {
16834 const section_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);16883 const section_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
16835 extra_index += 1;16884 extra_index += 1;
16836 const section_tv = try sema.resolveInstConst(block, section_src, section_ref);16885 const section_tv = sema.resolveInstConst(block, section_src, section_ref) catch |err| switch (err) {
16886 error.GenericPoison => {
16887 break :blk FuncLinkSection{ .generic = {} };
16888 },
16889 else => |e| return e,
16890 };
16837 _ = section_tv;16891 _ = section_tv;
16838 return sema.fail(block, section_src, "TODO implement linksection on functions", .{});16892 return sema.fail(block, section_src, "TODO implement linksection on functions", .{});
16839 } else null;16893 } else FuncLinkSection{ .default = {} };
1684016894
16841 const cc: std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: {16895 const cc: ?std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: {
16842 const body_len = sema.code.extra[extra_index];16896 const body_len = sema.code.extra[extra_index];
16843 extra_index += 1;16897 extra_index += 1;
16844 const body = sema.code.extra[extra_index..][0..body_len];16898 const body = sema.code.extra[extra_index..][0..body_len];
...@@ -16846,13 +16900,21 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A...@@ -16846,13 +16900,21 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1684616900
16847 const cc_ty = try sema.getBuiltinType(block, addrspace_src, "CallingConvention");16901 const cc_ty = try sema.getBuiltinType(block, addrspace_src, "CallingConvention");
16848 const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty);16902 const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty);
16903 if (val.tag() == .generic_poison) {
16904 break :blk null;
16905 }
16849 break :blk val.toEnum(std.builtin.CallingConvention);16906 break :blk val.toEnum(std.builtin.CallingConvention);
16850 } else if (extra.data.bits.has_cc_ref) blk: {16907 } else if (extra.data.bits.has_cc_ref) blk: {
16851 const cc_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);16908 const cc_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
16852 extra_index += 1;16909 extra_index += 1;
16853 const cc_tv = try sema.resolveInstConst(block, cc_src, cc_ref);16910 const cc_tv = sema.resolveInstConst(block, cc_src, cc_ref) catch |err| switch (err) {
16911 error.GenericPoison => {
16912 break :blk null;
16913 },
16914 else => |e| return e,
16915 };
16854 break :blk cc_tv.val.toEnum(std.builtin.CallingConvention);16916 break :blk cc_tv.val.toEnum(std.builtin.CallingConvention);
16855 } else .Unspecified;16917 } else std.builtin.CallingConvention.Unspecified;
1685616918
16857 const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: {16919 const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: {
16858 const body_len = sema.code.extra[extra_index];16920 const body_len = sema.code.extra[extra_index];
src/type.zig+4
...@@ -6120,6 +6120,10 @@ pub const Type = extern union {...@@ -6120,6 +6120,10 @@ pub const Type = extern union {
6120 cc: std.builtin.CallingConvention,6120 cc: std.builtin.CallingConvention,
6121 is_var_args: bool,6121 is_var_args: bool,
6122 is_generic: bool,6122 is_generic: bool,
6123 align_is_generic: bool = false,
6124 cc_is_generic: bool = false,
6125 section_is_generic: bool = false,
6126 addrspace_is_generic: bool = false,
61236127
6124 pub fn paramIsComptime(self: @This(), i: usize) bool {6128 pub fn paramIsComptime(self: @This(), i: usize) bool {
6125 assert(i < self.param_types.len);6129 assert(i < self.param_types.len);
test/behavior/align.zig+31-12
...@@ -334,25 +334,44 @@ fn simple4() align(4) i32 {...@@ -334,25 +334,44 @@ fn simple4() align(4) i32 {
334 return 0x19;334 return 0x19;
335}335}
336336
337test "generic function with align param" {337test "function align expression depends on generic parameter" {
338 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;338 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
339 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;339 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
340 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
341 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
342 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
343340
344 // function alignment is a compile error on wasm32/wasm64341 // function alignment is a compile error on wasm32/wasm64
345 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;342 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
346 if (native_arch == .thumb) return error.SkipZigTest;343 if (native_arch == .thumb) return error.SkipZigTest;
347344
348 try expect(whyWouldYouEverDoThis(1) == 0x1);345 const S = struct {
349 try expect(whyWouldYouEverDoThis(4) == 0x1);346 fn doTheTest() !void {
350 try expect(whyWouldYouEverDoThis(8) == 0x1);347 try expect(foobar(1) == 2);
348 try expect(foobar(4) == 5);
349 try expect(foobar(8) == 9);
350 }
351
352 fn foobar(comptime align_bytes: u8) align(align_bytes) u8 {
353 return align_bytes + 1;
354 }
355 };
356 try S.doTheTest();
357 comptime try S.doTheTest();
351}358}
352359
353fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 {360test "function callconv expression depends on generic parameter" {
354 _ = align_bytes;361 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
355 return 0x1;362
363 const S = struct {
364 fn doTheTest() !void {
365 try expect(foobar(.C, 1) == 2);
366 try expect(foobar(.Unspecified, 2) == 3);
367 }
368
369 fn foobar(comptime cc: std.builtin.CallingConvention, arg: u8) callconv(cc) u8 {
370 return arg + 1;
371 }
372 };
373 try S.doTheTest();
374 comptime try S.doTheTest();
356}375}
357376
358test "runtime known array index has best alignment possible" {377test "runtime known array index has best alignment possible" {