Skip to content

Commit 9dd3552

Browse files
committed
Sema: implement functions generic across callconv() or align()
1 parent c95eeb1 commit 9dd3552

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
@@ -6556,7 +6556,7 @@ fn zirFunc(
65566556
inst,
65576557
0,
65586558
target_util.defaultAddressSpace(target, .function),
6559-
null,
6559+
FuncLinkSection.default,
65606560
cc,
65616561
ret_ty,
65626562
false,
@@ -6669,15 +6669,26 @@ fn handleExternLibName(
66696669
return sema.gpa.dupeZ(u8, lib_name);
66706670
}
66716671

6672+
const FuncLinkSection = union(enum) {
6673+
generic,
6674+
default,
6675+
explicit: [*:0]const u8,
6676+
};
6677+
66726678
fn funcCommon(
66736679
sema: *Sema,
66746680
block: *Block,
66756681
src_node_offset: i32,
66766682
func_inst: Zir.Inst.Index,
6677-
alignment: u32,
6678-
address_space: std.builtin.AddressSpace,
6679-
section: ?[*:0]const u8,
6680-
cc: std.builtin.CallingConvention,
6683+
/// null means generic poison
6684+
alignment: ?u32,
6685+
/// null means generic poison
6686+
address_space: ?std.builtin.AddressSpace,
6687+
/// outer null means generic poison; inner null means default link section
6688+
section: FuncLinkSection,
6689+
/// null means generic poison
6690+
cc: ?std.builtin.CallingConvention,
6691+
/// this might be Type.generic_poison
66816692
bare_return_type: Type,
66826693
var_args: bool,
66836694
inferred_error_set: bool,
@@ -6688,7 +6699,11 @@ fn funcCommon(
66886699
) CompileError!Air.Inst.Ref {
66896700
const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };
66906701

6691-
var is_generic = bare_return_type.tag() == .generic_poison;
6702+
var is_generic = bare_return_type.tag() == .generic_poison or
6703+
alignment == null or
6704+
address_space == null or
6705+
section == .generic or
6706+
cc == null;
66926707
// Check for generic params.
66936708
for (block.params.items) |param| {
66946709
if (param.ty.tag() == .generic_poison) is_generic = true;
@@ -6709,25 +6724,28 @@ fn funcCommon(
67096724
errdefer if (maybe_inferred_error_set_node) |node| sema.gpa.destroy(node);
67106725
// Note: no need to errdefer since this will still be in its default state at the end of the function.
67116726

6727+
const target = sema.mod.getTarget();
67126728
const fn_ty: Type = fn_ty: {
67136729
// Hot path for some common function types.
67146730
// TODO can we eliminate some of these Type tag values? seems unnecessarily complicated.
6715-
if (!is_generic and block.params.items.len == 0 and !var_args and
6716-
alignment == 0 and !inferred_error_set)
6731+
if (!is_generic and block.params.items.len == 0 and !var_args and !inferred_error_set and
6732+
alignment.? == 0 and
6733+
address_space.? == target_util.defaultAddressSpace(target, .function) and
6734+
section == .default)
67176735
{
6718-
if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) {
6736+
if (bare_return_type.zigTypeTag() == .NoReturn and cc.? == .Unspecified) {
67196737
break :fn_ty Type.initTag(.fn_noreturn_no_args);
67206738
}
67216739

6722-
if (bare_return_type.zigTypeTag() == .Void and cc == .Unspecified) {
6740+
if (bare_return_type.zigTypeTag() == .Void and cc.? == .Unspecified) {
67236741
break :fn_ty Type.initTag(.fn_void_no_args);
67246742
}
67256743

6726-
if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Naked) {
6744+
if (bare_return_type.zigTypeTag() == .NoReturn and cc.? == .Naked) {
67276745
break :fn_ty Type.initTag(.fn_naked_noreturn_no_args);
67286746
}
67296747

6730-
if (bare_return_type.zigTypeTag() == .Void and cc == .C) {
6748+
if (bare_return_type.zigTypeTag() == .Void and cc.? == .C) {
67316749
break :fn_ty Type.initTag(.fn_ccc_void_no_args);
67326750
}
67336751
}
@@ -6773,21 +6791,33 @@ fn funcCommon(
67736791
});
67746792
};
67756793

6794+
// stage1 bug workaround
6795+
const cc_workaround = cc orelse undefined;
6796+
const align_workaround = alignment orelse @as(u32, undefined);
6797+
67766798
break :fn_ty try Type.Tag.function.create(sema.arena, .{
67776799
.param_types = param_types,
67786800
.comptime_params = comptime_params.ptr,
67796801
.return_type = return_type,
6780-
.cc = cc,
6781-
.alignment = alignment,
6802+
.cc = cc_workaround,
6803+
.cc_is_generic = cc == null,
6804+
.alignment = align_workaround,
6805+
.align_is_generic = alignment == null,
6806+
.section_is_generic = section == .generic,
6807+
.addrspace_is_generic = address_space == null,
67826808
.is_var_args = var_args,
67836809
.is_generic = is_generic,
67846810
});
67856811
};
67866812

67876813
if (sema.owner_decl.owns_tv) {
6788-
sema.owner_decl.@"linksection" = section;
6789-
sema.owner_decl.@"align" = alignment;
6790-
sema.owner_decl.@"addrspace" = address_space;
6814+
switch (section) {
6815+
.generic => sema.owner_decl.@"linksection" = undefined,
6816+
.default => sema.owner_decl.@"linksection" = null,
6817+
.explicit => |s| sema.owner_decl.@"linksection" = s,
6818+
}
6819+
if (alignment) |a| sema.owner_decl.@"align" = a;
6820+
if (address_space) |a| sema.owner_decl.@"addrspace" = a;
67916821
}
67926822

67936823
if (is_extern) {
@@ -16807,13 +16837,16 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1680716837
break :blk lib_name;
1680816838
} else null;
1680916839

16810-
const @"align": u32 = if (extra.data.bits.has_align_body) blk: {
16840+
const @"align": ?u32 = if (extra.data.bits.has_align_body) blk: {
1681116841
const body_len = sema.code.extra[extra_index];
1681216842
extra_index += 1;
1681316843
const body = sema.code.extra[extra_index..][0..body_len];
1681416844
extra_index += body.len;
1681516845

1681616846
const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u16);
16847+
if (val.tag() == .generic_poison) {
16848+
break :blk null;
16849+
}
1681716850
const alignment = @intCast(u32, val.toUnsignedInt(target));
1681816851
if (alignment == target_util.defaultFunctionAlignment(target)) {
1681916852
break :blk 0;
@@ -16823,7 +16856,12 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1682316856
} else if (extra.data.bits.has_align_ref) blk: {
1682416857
const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
1682516858
extra_index += 1;
16826-
const align_tv = try sema.resolveInstConst(block, align_src, align_ref);
16859+
const align_tv = sema.resolveInstConst(block, align_src, align_ref) catch |err| switch (err) {
16860+
error.GenericPoison => {
16861+
break :blk null;
16862+
},
16863+
else => |e| return e,
16864+
};
1682716865
const alignment = @intCast(u32, align_tv.val.toUnsignedInt(target));
1682816866
if (alignment == target_util.defaultFunctionAlignment(target)) {
1682916867
break :blk 0;
@@ -16832,54 +16870,78 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
1683216870
}
1683316871
} else 0;
1683416872

16835-
const @"addrspace": std.builtin.AddressSpace = if (extra.data.bits.has_addrspace_body) blk: {
16873+
const @"addrspace": ?std.builtin.AddressSpace = if (extra.data.bits.has_addrspace_body) blk: {
1683616874
const body_len = sema.code.extra[extra_index];
1683716875
extra_index += 1;
1683816876
const body = sema.code.extra[extra_index..][0..body_len];
1683916877
extra_index += body.len;
1684016878

1684116879
const addrspace_ty = try sema.getBuiltinType(block, addrspace_src, "AddressSpace");
1684216880
const val = try sema.resolveGenericBody(block, addrspace_src, body, inst, addrspace_ty);
16881+
if (val.tag() == .generic_poison) {
16882+
break :blk null;
16883+
}
1684316884
break :blk val.toEnum(std.builtin.AddressSpace);
1684416885
} else if (extra.data.bits.has_addrspace_ref) blk: {
1684516886
const addrspace_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
1684616887
extra_index += 1;
16847-
const addrspace_tv = try sema.resolveInstConst(block, addrspace_src, addrspace_ref);
16888+
const addrspace_tv = sema.resolveInstConst(block, addrspace_src, addrspace_ref) catch |err| switch (err) {
16889+
error.GenericPoison => {
16890+
break :blk null;
16891+
},
16892+
else => |e| return e,
16893+
};
1684816894
break :blk addrspace_tv.val.toEnum(std.builtin.AddressSpace);
1684916895
} else target_util.defaultAddressSpace(target, .function);
1685016896

16851-
const @"linksection": ?[*:0]const u8 = if (extra.data.bits.has_section_body) {
16897+
const @"linksection": FuncLinkSection = if (extra.data.bits.has_section_body) blk: {
1685216898
const body_len = sema.code.extra[extra_index];
1685316899
extra_index += 1;
1685416900
const body = sema.code.extra[extra_index..][0..body_len];
1685516901
extra_index += body.len;
1685616902

1685716903
const val = try sema.resolveGenericBody(block, section_src, body, inst, Type.initTag(.const_slice_u8));
16904+
if (val.tag() == .generic_poison) {
16905+
break :blk FuncLinkSection{ .generic = {} };
16906+
}
1685816907
_ = val;
1685916908
return sema.fail(block, section_src, "TODO implement linksection on functions", .{});
16860-
} else if (extra.data.bits.has_section_ref) {
16909+
} else if (extra.data.bits.has_section_ref) blk: {
1686116910
const section_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
1686216911
extra_index += 1;
16863-
const section_tv = try sema.resolveInstConst(block, section_src, section_ref);
16912+
const section_tv = sema.resolveInstConst(block, section_src, section_ref) catch |err| switch (err) {
16913+
error.GenericPoison => {
16914+
break :blk FuncLinkSection{ .generic = {} };
16915+
},
16916+
else => |e| return e,
16917+
};
1686416918
_ = section_tv;
1686516919
return sema.fail(block, section_src, "TODO implement linksection on functions", .{});
16866-
} else null;
16920+
} else FuncLinkSection{ .default = {} };
1686716921

16868-
const cc: std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: {
16922+
const cc: ?std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: {
1686916923
const body_len = sema.code.extra[extra_index];
1687016924
extra_index += 1;
1687116925
const body = sema.code.extra[extra_index..][0..body_len];
1687216926
extra_index += body.len;
1687316927

1687416928
const cc_ty = try sema.getBuiltinType(block, addrspace_src, "CallingConvention");
1687516929
const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty);
16930+
if (val.tag() == .generic_poison) {
16931+
break :blk null;
16932+
}
1687616933
break :blk val.toEnum(std.builtin.CallingConvention);
1687716934
} else if (extra.data.bits.has_cc_ref) blk: {
1687816935
const cc_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
1687916936
extra_index += 1;
16880-
const cc_tv = try sema.resolveInstConst(block, cc_src, cc_ref);
16937+
const cc_tv = sema.resolveInstConst(block, cc_src, cc_ref) catch |err| switch (err) {
16938+
error.GenericPoison => {
16939+
break :blk null;
16940+
},
16941+
else => |e| return e,
16942+
};
1688116943
break :blk cc_tv.val.toEnum(std.builtin.CallingConvention);
16882-
} else .Unspecified;
16944+
} else std.builtin.CallingConvention.Unspecified;
1688316945

1688416946
const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: {
1688516947
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)