Skip to content

Commit bd89a73

Browse files
committed
Sema: implement functions generic across callconv() or align()
1 parent 7e98b04 commit bd89a73

File tree

3 files changed

+125
-40
lines changed

3 files changed

+125
-40
lines changed

src/Sema.zig

Lines changed: 90 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -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

6663+
const FuncLinkSection = union(enum) {
6664+
generic,
6665+
default,
6666+
explicit: [*:0]const u8,
6667+
};
6668+
66636669
fn 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,54 +16843,78 @@ 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];
1681216850
extra_index += body.len;
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];
1684516899
extra_index += body.len;
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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -334,25 +334,44 @@ fn simple4() align(4) i32 {
334334
return 0x19;
335335
}
336336

337-
test "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;
337+
test "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

353-
fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) u8 {
354-
_ = align_bytes;
355-
return 0x1;
360+
test "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

358377
test "runtime known array index has best alignment possible" {

0 commit comments

Comments
 (0)