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(
65476547 inst,
65486548 0,
65496549 target_util.defaultAddressSpace(target, .function),
6550 null,
6550 FuncLinkSection.default,
65516551 cc,
65526552 ret_ty,
65536553 false,
......@@ -6660,15 +6660,26 @@ fn handleExternLibName(
66606660 return sema.gpa.dupeZ(u8, lib_name);
66616661}
66626662
6663const FuncLinkSection = union(enum) {
6664 generic,
6665 default,
6666 explicit: [*:0]const u8,
6667};
6668
66636669fn funcCommon(
66646670 sema: *Sema,
66656671 block: *Block,
66666672 src_node_offset: i32,
66676673 func_inst: Zir.Inst.Index,
6668 alignment: u32,
6669 address_space: std.builtin.AddressSpace,
6670 section: ?[*:0]const u8,
6671 cc: std.builtin.CallingConvention,
6674 /// null means generic poison
6675 alignment: ?u32,
6676 /// null means generic poison
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
66726683 bare_return_type: Type,
66736684 var_args: bool,
66746685 inferred_error_set: bool,
......@@ -6679,7 +6690,11 @@ fn funcCommon(
66796690) CompileError!Air.Inst.Ref {
66806691 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;
66836698 // Check for generic params.
66846699 for (block.params.items) |param| {
66856700 if (param.ty.tag() == .generic_poison) is_generic = true;
......@@ -6700,25 +6715,28 @@ fn funcCommon(
67006715 errdefer if (maybe_inferred_error_set_node) |node| sema.gpa.destroy(node);
67016716 // 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();
67036719 const fn_ty: Type = fn_ty: {
67046720 // Hot path for some common function types.
67056721 // 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 and
6707 alignment == 0 and !inferred_error_set)
6722 if (!is_generic and block.params.items.len == 0 and !var_args and !inferred_error_set and
6723 alignment.? == 0 and
6724 address_space.? == target_util.defaultAddressSpace(target, .function) and
6725 section == .default)
67086726 {
6709 if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) {
6727 if (bare_return_type.zigTypeTag() == .NoReturn and cc.? == .Unspecified) {
67106728 break :fn_ty Type.initTag(.fn_noreturn_no_args);
67116729 }
67126730
6713 if (bare_return_type.zigTypeTag() == .Void and cc == .Unspecified) {
6731 if (bare_return_type.zigTypeTag() == .Void and cc.? == .Unspecified) {
67146732 break :fn_ty Type.initTag(.fn_void_no_args);
67156733 }
67166734
6717 if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Naked) {
6735 if (bare_return_type.zigTypeTag() == .NoReturn and cc.? == .Naked) {
67186736 break :fn_ty Type.initTag(.fn_naked_noreturn_no_args);
67196737 }
67206738
6721 if (bare_return_type.zigTypeTag() == .Void and cc == .C) {
6739 if (bare_return_type.zigTypeTag() == .Void and cc.? == .C) {
67226740 break :fn_ty Type.initTag(.fn_ccc_void_no_args);
67236741 }
67246742 }
......@@ -6764,21 +6782,33 @@ fn funcCommon(
67646782 });
67656783 };
67666784
6785 // stage1 bug workaround
6786 const cc_workaround = cc orelse undefined;
6787 const align_workaround = alignment orelse @as(u32, undefined);
6788
67676789 break :fn_ty try Type.Tag.function.create(sema.arena, .{
67686790 .param_types = param_types,
67696791 .comptime_params = comptime_params.ptr,
67706792 .return_type = return_type,
6771 .cc = cc,
6772 .alignment = alignment,
6793 .cc = cc_workaround,
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,
67736799 .is_var_args = var_args,
67746800 .is_generic = is_generic,
67756801 });
67766802 };
67776803
67786804 if (sema.owner_decl.owns_tv) {
6779 sema.owner_decl.@"linksection" = section;
6780 sema.owner_decl.@"align" = alignment;
6781 sema.owner_decl.@"addrspace" = address_space;
6805 switch (section) {
6806 .generic => sema.owner_decl.@"linksection" = undefined,
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;
67826812 }
67836813
67846814 if (is_extern) {
......@@ -16780,13 +16810,16 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1678016810 break :blk lib_name;
1678116811 } 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: {
1678416814 const body_len = sema.code.extra[extra_index];
1678516815 extra_index += 1;
1678616816 const body = sema.code.extra[extra_index..][0..body_len];
1678716817 extra_index += body.len;
1678816818
1678916819 const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u16);
16820 if (val.tag() == .generic_poison) {
16821 break :blk null;
16822 }
1679016823 const alignment = @intCast(u32, val.toUnsignedInt(target));
1679116824 if (alignment == target_util.defaultFunctionAlignment(target)) {
1679216825 break :blk 0;
......@@ -16796,7 +16829,12 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1679616829 } else if (extra.data.bits.has_align_ref) blk: {
1679716830 const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
1679816831 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 };
1680016838 const alignment = @intCast(u32, align_tv.val.toUnsignedInt(target));
1680116839 if (alignment == target_util.defaultFunctionAlignment(target)) {
1680216840 break :blk 0;
......@@ -16805,7 +16843,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1680516843 }
1680616844 } 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: {
1680916847 const body_len = sema.code.extra[extra_index];
1681016848 extra_index += 1;
1681116849 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
1681316851
1681416852 const addrspace_ty = try sema.getBuiltinType(block, addrspace_src, "AddressSpace");
1681516853 const val = try sema.resolveGenericBody(block, addrspace_src, body, inst, addrspace_ty);
16854 if (val.tag() == .generic_poison) {
16855 break :blk null;
16856 }
1681616857 break :blk val.toEnum(std.builtin.AddressSpace);
1681716858 } else if (extra.data.bits.has_addrspace_ref) blk: {
1681816859 const addrspace_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
1681916860 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 };
1682116867 break :blk addrspace_tv.val.toEnum(std.builtin.AddressSpace);
1682216868 } 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: {
1682516871 const body_len = sema.code.extra[extra_index];
1682616872 extra_index += 1;
1682716873 const body = sema.code.extra[extra_index..][0..body_len];
1682816874 extra_index += body.len;
1682916875
1683016876 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 }
1683116880 _ = val;
1683216881 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: {
1683416883 const section_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
1683516884 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 };
1683716891 _ = section_tv;
1683816892 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: {
1684216896 const body_len = sema.code.extra[extra_index];
1684316897 extra_index += 1;
1684416898 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
1684616900
1684716901 const cc_ty = try sema.getBuiltinType(block, addrspace_src, "CallingConvention");
1684816902 const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty);
16903 if (val.tag() == .generic_poison) {
16904 break :blk null;
16905 }
1684916906 break :blk val.toEnum(std.builtin.CallingConvention);
1685016907 } else if (extra.data.bits.has_cc_ref) blk: {
1685116908 const cc_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
1685216909 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 };
1685416916 break :blk cc_tv.val.toEnum(std.builtin.CallingConvention);
16855 } else .Unspecified;
16917 } else std.builtin.CallingConvention.Unspecified;
1685616918
1685716919 const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: {
1685816920 const body_len = sema.code.extra[extra_index];
src/type.zig+4
......@@ -6120,6 +6120,10 @@ pub const Type = extern union {
61206120 cc: std.builtin.CallingConvention,
61216121 is_var_args: bool,
61226122 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
61246128 pub fn paramIsComptime(self: @This(), i: usize) bool {
61256129 assert(i < self.param_types.len);
test/behavior/align.zig+31-12
......@@ -334,25 +334,44 @@ fn simple4() align(4) i32 {
334334 return 0x19;
335335}
336336
337test "generic function with align param" {
338 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
339 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
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;
337test "function align expression depends on generic parameter" {
338 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
339 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
343340
344341 // function alignment is a compile error on wasm32/wasm64
345342 if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
346343 if (native_arch == .thumb) return error.SkipZigTest;
347344
348 try expect(whyWouldYouEverDoThis(1) == 0x1);
349 try expect(whyWouldYouEverDoThis(4) == 0x1);
350 try expect(whyWouldYouEverDoThis(8) == 0x1);
345 const S = struct {
346 fn doTheTest() !void {
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();
351358}
352359
353fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 {
354 _ = align_bytes;
355 return 0x1;
360test "function callconv expression depends on generic parameter" {
361 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
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();
356375}
357376
358377test "runtime known array index has best alignment possible" {