authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-23 23:28:05-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-23 23:28:05-07:00
log2af69710a7b1513f3540ae0e1178cb5f3948204d
tree9f8aa5fd7d80ec976a5355348e7b3a6347caf1b5
parentaca42c62598523b92de7a51d3d84f2f2e5146536

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.

4 files changed, 88 insertions(+), 21 deletions(-)

lib/std/meta.zig+6-6
...@@ -569,10 +569,10 @@ test "std.meta.fieldNames" {...@@ -569,10 +569,10 @@ test "std.meta.fieldNames" {
569}569}
570570
571pub fn FieldEnum(comptime T: type) type {571pub fn FieldEnum(comptime T: type) type {
572 const fieldInfos = fields(T);572 const field_infos = fields(T);
573 var enumFields: [fieldInfos.len]std.builtin.Type.EnumField = undefined;573 var enumFields: [field_infos.len]std.builtin.Type.EnumField = undefined;
574 var decls = [_]std.builtin.Type.Declaration{};574 var decls = [_]std.builtin.Type.Declaration{};
575 inline for (fieldInfos) |field, i| {575 inline for (field_infos) |field, i| {
576 enumFields[i] = .{576 enumFields[i] = .{
577 .name = field.name,577 .name = field.name,
578 .value = i,578 .value = i,
...@@ -581,7 +581,7 @@ pub fn FieldEnum(comptime T: type) type {...@@ -581,7 +581,7 @@ pub fn FieldEnum(comptime T: type) type {
581 return @Type(.{581 return @Type(.{
582 .Enum = .{582 .Enum = .{
583 .layout = .Auto,583 .layout = .Auto,
584 .tag_type = std.math.IntFittingRange(0, fieldInfos.len - 1),584 .tag_type = std.math.IntFittingRange(0, field_infos.len - 1),
585 .fields = &enumFields,585 .fields = &enumFields,
586 .decls = &decls,586 .decls = &decls,
587 .is_exhaustive = true,587 .is_exhaustive = true,
...@@ -966,7 +966,7 @@ pub fn ArgsTuple(comptime Function: type) type {...@@ -966,7 +966,7 @@ pub fn ArgsTuple(comptime Function: type) type {
966 argument_field_list[i] = .{966 argument_field_list[i] = .{
967 .name = std.fmt.bufPrint(&num_buf, "{d}", .{i}) catch unreachable,967 .name = std.fmt.bufPrint(&num_buf, "{d}", .{i}) catch unreachable,
968 .field_type = T,968 .field_type = T,
969 .default_value = @as(?T, null),969 .default_value = null,
970 .is_comptime = false,970 .is_comptime = false,
971 .alignment = if (@sizeOf(T) > 0) @alignOf(T) else 0,971 .alignment = if (@sizeOf(T) > 0) @alignOf(T) else 0,
972 };972 };
...@@ -997,7 +997,7 @@ pub fn Tuple(comptime types: []const type) type {...@@ -997,7 +997,7 @@ pub fn Tuple(comptime types: []const type) type {
997 tuple_fields[i] = .{997 tuple_fields[i] = .{
998 .name = std.fmt.bufPrint(&num_buf, "{d}", .{i}) catch unreachable,998 .name = std.fmt.bufPrint(&num_buf, "{d}", .{i}) catch unreachable,
999 .field_type = T,999 .field_type = T,
1000 .default_value = @as(?T, null),1000 .default_value = null,
1001 .is_comptime = false,1001 .is_comptime = false,
1002 .alignment = if (@sizeOf(T) > 0) @alignOf(T) else 0,1002 .alignment = if (@sizeOf(T) > 0) @alignOf(T) else 0,
1003 };1003 };
src/Module.zig+2-2
...@@ -781,11 +781,11 @@ pub const Decl = struct {...@@ -781,11 +781,11 @@ pub const Decl = struct {
781 return &decl_plus_emit_h.emit_h;781 return &decl_plus_emit_h.emit_h;
782 }782 }
783783
784 fn removeDependant(decl: *Decl, other: *Decl) void {784 pub fn removeDependant(decl: *Decl, other: *Decl) void {
785 assert(decl.dependants.swapRemove(other));785 assert(decl.dependants.swapRemove(other));
786 }786 }
787787
788 fn removeDependency(decl: *Decl, other: *Decl) void {788 pub fn removeDependency(decl: *Decl, other: *Decl) void {
789 assert(decl.dependencies.swapRemove(other));789 assert(decl.dependencies.swapRemove(other));
790 }790 }
791791
src/Sema.zig+44-13
...@@ -4688,7 +4688,7 @@ fn analyzeCall(...@@ -4688,7 +4688,7 @@ fn analyzeCall(
46884688
4689 const gpa = sema.gpa;4689 const gpa = sema.gpa;
46904690
4691 const is_comptime_call = block.is_comptime or modifier == .compile_time or4691 var is_comptime_call = block.is_comptime or modifier == .compile_time or
4692 try sema.typeRequiresComptime(block, func_src, func_ty_info.return_type);4692 try sema.typeRequiresComptime(block, func_src, func_ty_info.return_type);
4693 var is_inline_call = is_comptime_call or modifier == .always_inline or4693 var is_inline_call = is_comptime_call or modifier == .always_inline or
4694 func_ty_info.cc == .Inline;4694 func_ty_info.cc == .Inline;
...@@ -4706,7 +4706,13 @@ fn analyzeCall(...@@ -4706,7 +4706,13 @@ fn analyzeCall(
4706 )) |some| {4706 )) |some| {
4707 return some;4707 return some;
4708 } else |err| switch (err) {4708 } 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 },
4710 else => |e| return e,4716 else => |e| return e,
4711 }4717 }
4712 }4718 }
...@@ -5149,7 +5155,13 @@ fn instantiateGenericCall(...@@ -5149,7 +5155,13 @@ fn instantiateGenericCall(
5149 // of each of its instantiations.5155 // of each of its instantiations.
5150 assert(new_decl.dependencies.keys().len == 0);5156 assert(new_decl.dependencies.keys().len == 0);
5151 try mod.declareDeclDependency(new_decl, module_fn.owner_decl);5157 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
5154 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);5166 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
5155 errdefer new_decl_arena.deinit();5167 errdefer new_decl_arena.deinit();
...@@ -5285,8 +5297,17 @@ fn instantiateGenericCall(...@@ -5285,8 +5297,17 @@ fn instantiateGenericCall(
52855297
5286 // Populate the Decl ty/val with the function and its type.5298 // Populate the Decl ty/val with the function and its type.
5287 new_decl.ty = try child_sema.typeOf(new_func_inst).copy(new_decl_arena_allocator);5299 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.5300 // If the call evaluated to a return type that requires comptime, never mind
5289 if (new_decl.ty.fnInfo().is_generic) return error.GenericPoison;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
5291 new_decl.val = try Value.Tag.function.create(new_decl_arena_allocator, new_func);5312 new_decl.val = try Value.Tag.function.create(new_decl_arena_allocator, new_func);
5292 new_decl.has_tv = true;5313 new_decl.has_tv = true;
...@@ -12978,10 +12999,14 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -12978,10 +12999,14 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
12978 new_decl.owns_tv = true;12999 new_decl.owns_tv = true;
12979 errdefer mod.abortAnonDecl(new_decl);13000 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
12981 enum_obj.* = .{13006 enum_obj.* = .{
12982 .owner_decl = new_decl,13007 .owner_decl = new_decl,
12983 .tag_ty = Type.@"null",13008 .tag_ty = int_tag_ty,
12984 .tag_ty_inferred = true,13009 .tag_ty_inferred = false,
12985 .fields = .{},13010 .fields = .{},
12986 .values = .{},13011 .values = .{},
12987 .node_offset = src.node_offset,13012 .node_offset = src.node_offset,
...@@ -12992,10 +13017,6 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I...@@ -12992,10 +13017,6 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
12992 },13017 },
12993 };13018 };
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
12999 // Fields13020 // Fields
13000 const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(target));13021 const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(target));
13001 if (fields_len > 0) {13022 if (fields_len > 0) {
...@@ -21111,8 +21132,18 @@ fn resolveInferredErrorSet(...@@ -21111,8 +21132,18 @@ fn resolveInferredErrorSet(
21111 return sema.fail(block, src, "unable to resolve inferred error set", .{});21132 return sema.fail(block, src, "unable to resolve inferred error set", .{});
21112 }21133 }
2111321134
21114 // To ensure that all dependencies are properly added to the set.21135 // In order to ensure that all dependencies are properly added to the set, we
21115 try sema.ensureFuncBodyAnalyzed(ies.func);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
21117 ies.is_resolved = true;21148 ies.is_resolved = true;
2111821149
test/behavior/generics.zig+36
...@@ -241,3 +241,39 @@ test "function parameter is generic" {...@@ -241,3 +241,39 @@ test "function parameter is generic" {
241 var rng: u32 = 2;241 var rng: u32 = 2;
242 S.init(rng, S.fill);242 S.init(rng, S.fill);
243}243}
244
245test "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}