Skip to content

Commit 2af6971

Browse files
committed
stage2: fix some generics issues
* std.meta: correct use of `default_value` in reification. stage1 accepted a wrong type for `null`. * Sema: after instantiating a generic function, if the return type ends up being a comptime-known type, then we return an error, undoing the generic function instantiation, and making a comptime function call instead. - We also needed to clean up the dependency graph in this case. * Sema: reified enums set tag_ty_inferred to false since an integer tag type is provided. This is a limitation of the `@Type` builtin which will be addressed with #10710. * Sema: fix resolveInferredErrorSet incorrectly calling ensureFuncBodyAnalyzed on generic functions.
1 parent aca42c6 commit 2af6971

File tree

4 files changed

+88
-21
lines changed

4 files changed

+88
-21
lines changed

lib/std/meta.zig

+6-6
Original file line numberDiff line numberDiff line change
@@ -569,10 +569,10 @@ test "std.meta.fieldNames" {
569569
}
570570

571571
pub fn FieldEnum(comptime T: type) type {
572-
const fieldInfos = fields(T);
573-
var enumFields: [fieldInfos.len]std.builtin.Type.EnumField = undefined;
572+
const field_infos = fields(T);
573+
var enumFields: [field_infos.len]std.builtin.Type.EnumField = undefined;
574574
var decls = [_]std.builtin.Type.Declaration{};
575-
inline for (fieldInfos) |field, i| {
575+
inline for (field_infos) |field, i| {
576576
enumFields[i] = .{
577577
.name = field.name,
578578
.value = i,
@@ -581,7 +581,7 @@ pub fn FieldEnum(comptime T: type) type {
581581
return @Type(.{
582582
.Enum = .{
583583
.layout = .Auto,
584-
.tag_type = std.math.IntFittingRange(0, fieldInfos.len - 1),
584+
.tag_type = std.math.IntFittingRange(0, field_infos.len - 1),
585585
.fields = &enumFields,
586586
.decls = &decls,
587587
.is_exhaustive = true,
@@ -966,7 +966,7 @@ pub fn ArgsTuple(comptime Function: type) type {
966966
argument_field_list[i] = .{
967967
.name = std.fmt.bufPrint(&num_buf, "{d}", .{i}) catch unreachable,
968968
.field_type = T,
969-
.default_value = @as(?T, null),
969+
.default_value = null,
970970
.is_comptime = false,
971971
.alignment = if (@sizeOf(T) > 0) @alignOf(T) else 0,
972972
};
@@ -997,7 +997,7 @@ pub fn Tuple(comptime types: []const type) type {
997997
tuple_fields[i] = .{
998998
.name = std.fmt.bufPrint(&num_buf, "{d}", .{i}) catch unreachable,
999999
.field_type = T,
1000-
.default_value = @as(?T, null),
1000+
.default_value = null,
10011001
.is_comptime = false,
10021002
.alignment = if (@sizeOf(T) > 0) @alignOf(T) else 0,
10031003
};

src/Module.zig

+2-2
Original file line numberDiff line numberDiff line change
@@ -781,11 +781,11 @@ pub const Decl = struct {
781781
return &decl_plus_emit_h.emit_h;
782782
}
783783

784-
fn removeDependant(decl: *Decl, other: *Decl) void {
784+
pub fn removeDependant(decl: *Decl, other: *Decl) void {
785785
assert(decl.dependants.swapRemove(other));
786786
}
787787

788-
fn removeDependency(decl: *Decl, other: *Decl) void {
788+
pub fn removeDependency(decl: *Decl, other: *Decl) void {
789789
assert(decl.dependencies.swapRemove(other));
790790
}
791791

src/Sema.zig

+44-13
Original file line numberDiff line numberDiff line change
@@ -4688,7 +4688,7 @@ fn analyzeCall(
46884688

46894689
const gpa = sema.gpa;
46904690

4691-
const is_comptime_call = block.is_comptime or modifier == .compile_time or
4691+
var is_comptime_call = block.is_comptime or modifier == .compile_time or
46924692
try sema.typeRequiresComptime(block, func_src, func_ty_info.return_type);
46934693
var is_inline_call = is_comptime_call or modifier == .always_inline or
46944694
func_ty_info.cc == .Inline;
@@ -4706,7 +4706,13 @@ fn analyzeCall(
47064706
)) |some| {
47074707
return some;
47084708
} else |err| switch (err) {
4709-
error.GenericPoison => is_inline_call = true,
4709+
error.GenericPoison => {
4710+
is_inline_call = true;
4711+
},
4712+
error.ComptimeReturn => {
4713+
is_inline_call = true;
4714+
is_comptime_call = true;
4715+
},
47104716
else => |e| return e,
47114717
}
47124718
}
@@ -5149,7 +5155,13 @@ fn instantiateGenericCall(
51495155
// of each of its instantiations.
51505156
assert(new_decl.dependencies.keys().len == 0);
51515157
try mod.declareDeclDependency(new_decl, module_fn.owner_decl);
5152-
errdefer assert(module_fn.owner_decl.dependants.orderedRemove(new_decl));
5158+
// Resolving the new function type below will possibly declare more decl dependencies
5159+
// and so we remove them all here in case of error.
5160+
errdefer {
5161+
for (new_decl.dependencies.keys()) |dep| {
5162+
dep.removeDependant(new_decl);
5163+
}
5164+
}
51535165

51545166
var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
51555167
errdefer new_decl_arena.deinit();
@@ -5285,8 +5297,17 @@ fn instantiateGenericCall(
52855297

52865298
// Populate the Decl ty/val with the function and its type.
52875299
new_decl.ty = try child_sema.typeOf(new_func_inst).copy(new_decl_arena_allocator);
5288-
// If the call evaluated to a generic type return errror and call inline.
5289-
if (new_decl.ty.fnInfo().is_generic) return error.GenericPoison;
5300+
// If the call evaluated to a return type that requires comptime, never mind
5301+
// our generic instantiation. Instead we need to perform a comptime call.
5302+
const new_fn_info = new_decl.ty.fnInfo();
5303+
if (try sema.typeRequiresComptime(block, call_src, new_fn_info.return_type)) {
5304+
return error.ComptimeReturn;
5305+
}
5306+
// Similarly, if the call evaluated to a generic type we need to instead
5307+
// call it inline.
5308+
if (new_fn_info.is_generic or new_fn_info.cc == .Inline) {
5309+
return error.GenericPoison;
5310+
}
52905311

52915312
new_decl.val = try Value.Tag.function.create(new_decl_arena_allocator, new_func);
52925313
new_decl.has_tv = true;
@@ -12978,10 +12999,14 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
1297812999
new_decl.owns_tv = true;
1297913000
errdefer mod.abortAnonDecl(new_decl);
1298013001

13002+
// Enum tag type
13003+
var buffer: Value.ToTypeBuffer = undefined;
13004+
const int_tag_ty = try tag_type_val.toType(&buffer).copy(new_decl_arena_allocator);
13005+
1298113006
enum_obj.* = .{
1298213007
.owner_decl = new_decl,
12983-
.tag_ty = Type.@"null",
12984-
.tag_ty_inferred = true,
13008+
.tag_ty = int_tag_ty,
13009+
.tag_ty_inferred = false,
1298513010
.fields = .{},
1298613011
.values = .{},
1298713012
.node_offset = src.node_offset,
@@ -12992,10 +13017,6 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
1299213017
},
1299313018
};
1299413019

12995-
// Enum tag type
12996-
var buffer: Value.ToTypeBuffer = undefined;
12997-
enum_obj.tag_ty = try tag_type_val.toType(&buffer).copy(new_decl_arena_allocator);
12998-
1299913020
// Fields
1300013021
const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(target));
1300113022
if (fields_len > 0) {
@@ -21111,8 +21132,18 @@ fn resolveInferredErrorSet(
2111121132
return sema.fail(block, src, "unable to resolve inferred error set", .{});
2111221133
}
2111321134

21114-
// To ensure that all dependencies are properly added to the set.
21115-
try sema.ensureFuncBodyAnalyzed(ies.func);
21135+
// In order to ensure that all dependencies are properly added to the set, we
21136+
// need to ensure the function body is analyzed of the inferred error set.
21137+
// However, in the case of comptime/inline function calls with inferred error sets,
21138+
// each call gets a new InferredErrorSet object, which points to the same
21139+
// `*Module.Fn`. Not only is the function not relevant to the inferred error set
21140+
// in this case, it may be a generic function which would cause an assertion failure
21141+
// if we called `ensureFuncBodyAnalyzed` on it here.
21142+
if (ies.func.owner_decl.ty.fnInfo().return_type.errorUnionSet().castTag(.error_set_inferred).?.data == ies) {
21143+
// In this case we are dealing with the actual InferredErrorSet object that
21144+
// corresponds to the function, not one created to track an inline/comptime call.
21145+
try sema.ensureFuncBodyAnalyzed(ies.func);
21146+
}
2111621147

2111721148
ies.is_resolved = true;
2111821149

test/behavior/generics.zig

+36
Original file line numberDiff line numberDiff line change
@@ -241,3 +241,39 @@ test "function parameter is generic" {
241241
var rng: u32 = 2;
242242
S.init(rng, S.fill);
243243
}
244+
245+
test "generic function instantiation turns into comptime call" {
246+
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
247+
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
248+
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
249+
250+
const S = struct {
251+
fn doTheTest() !void {
252+
const E1 = enum { A };
253+
const e1f = fieldInfo(E1, .A);
254+
try expect(std.mem.eql(u8, e1f.name, "A"));
255+
}
256+
257+
pub fn fieldInfo(comptime T: type, comptime field: FieldEnum(T)) switch (@typeInfo(T)) {
258+
.Enum => std.builtin.Type.EnumField,
259+
else => void,
260+
} {
261+
return @typeInfo(T).Enum.fields[@enumToInt(field)];
262+
}
263+
264+
pub fn FieldEnum(comptime T: type) type {
265+
_ = T;
266+
var enumFields: [1]std.builtin.Type.EnumField = .{.{ .name = "A", .value = 0 }};
267+
return @Type(.{
268+
.Enum = .{
269+
.layout = .Auto,
270+
.tag_type = u0,
271+
.fields = &enumFields,
272+
.decls = &.{},
273+
.is_exhaustive = true,
274+
},
275+
});
276+
}
277+
};
278+
try S.doTheTest();
279+
}

0 commit comments

Comments
 (0)