diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 7be06e0fd1a6..4e03eb602bc0 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -569,10 +569,10 @@ test "std.meta.fieldNames" { } pub fn FieldEnum(comptime T: type) type { - const fieldInfos = fields(T); - var enumFields: [fieldInfos.len]std.builtin.Type.EnumField = undefined; + const field_infos = fields(T); + var enumFields: [field_infos.len]std.builtin.Type.EnumField = undefined; var decls = [_]std.builtin.Type.Declaration{}; - inline for (fieldInfos) |field, i| { + inline for (field_infos) |field, i| { enumFields[i] = .{ .name = field.name, .value = i, @@ -581,7 +581,7 @@ pub fn FieldEnum(comptime T: type) type { return @Type(.{ .Enum = .{ .layout = .Auto, - .tag_type = std.math.IntFittingRange(0, fieldInfos.len - 1), + .tag_type = std.math.IntFittingRange(0, field_infos.len - 1), .fields = &enumFields, .decls = &decls, .is_exhaustive = true, @@ -966,7 +966,7 @@ pub fn ArgsTuple(comptime Function: type) type { argument_field_list[i] = .{ .name = std.fmt.bufPrint(&num_buf, "{d}", .{i}) catch unreachable, .field_type = T, - .default_value = @as(?T, null), + .default_value = null, .is_comptime = false, .alignment = if (@sizeOf(T) > 0) @alignOf(T) else 0, }; @@ -997,7 +997,7 @@ pub fn Tuple(comptime types: []const type) type { tuple_fields[i] = .{ .name = std.fmt.bufPrint(&num_buf, "{d}", .{i}) catch unreachable, .field_type = T, - .default_value = @as(?T, null), + .default_value = null, .is_comptime = false, .alignment = if (@sizeOf(T) > 0) @alignOf(T) else 0, }; diff --git a/src/Module.zig b/src/Module.zig index 79d6343949b5..0666936f1fed 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -781,11 +781,11 @@ pub const Decl = struct { return &decl_plus_emit_h.emit_h; } - fn removeDependant(decl: *Decl, other: *Decl) void { + pub fn removeDependant(decl: *Decl, other: *Decl) void { assert(decl.dependants.swapRemove(other)); } - fn removeDependency(decl: *Decl, other: *Decl) void { + pub fn removeDependency(decl: *Decl, other: *Decl) void { assert(decl.dependencies.swapRemove(other)); } diff --git a/src/Sema.zig b/src/Sema.zig index d8cc908ae887..277e4a6ba6d3 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4688,7 +4688,7 @@ fn analyzeCall( const gpa = sema.gpa; - const is_comptime_call = block.is_comptime or modifier == .compile_time or + var is_comptime_call = block.is_comptime or modifier == .compile_time or try sema.typeRequiresComptime(block, func_src, func_ty_info.return_type); var is_inline_call = is_comptime_call or modifier == .always_inline or func_ty_info.cc == .Inline; @@ -4706,7 +4706,13 @@ fn analyzeCall( )) |some| { return some; } else |err| switch (err) { - error.GenericPoison => is_inline_call = true, + error.GenericPoison => { + is_inline_call = true; + }, + error.ComptimeReturn => { + is_inline_call = true; + is_comptime_call = true; + }, else => |e| return e, } } @@ -5149,7 +5155,13 @@ fn instantiateGenericCall( // of each of its instantiations. assert(new_decl.dependencies.keys().len == 0); try mod.declareDeclDependency(new_decl, module_fn.owner_decl); - errdefer assert(module_fn.owner_decl.dependants.orderedRemove(new_decl)); + // Resolving the new function type below will possibly declare more decl dependencies + // and so we remove them all here in case of error. + errdefer { + for (new_decl.dependencies.keys()) |dep| { + dep.removeDependant(new_decl); + } + } var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); errdefer new_decl_arena.deinit(); @@ -5285,8 +5297,17 @@ fn instantiateGenericCall( // Populate the Decl ty/val with the function and its type. new_decl.ty = try child_sema.typeOf(new_func_inst).copy(new_decl_arena_allocator); - // If the call evaluated to a generic type return errror and call inline. - if (new_decl.ty.fnInfo().is_generic) return error.GenericPoison; + // If the call evaluated to a return type that requires comptime, never mind + // our generic instantiation. Instead we need to perform a comptime call. + const new_fn_info = new_decl.ty.fnInfo(); + if (try sema.typeRequiresComptime(block, call_src, new_fn_info.return_type)) { + return error.ComptimeReturn; + } + // Similarly, if the call evaluated to a generic type we need to instead + // call it inline. + if (new_fn_info.is_generic or new_fn_info.cc == .Inline) { + return error.GenericPoison; + } new_decl.val = try Value.Tag.function.create(new_decl_arena_allocator, new_func); new_decl.has_tv = true; @@ -12978,10 +12999,14 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I new_decl.owns_tv = true; errdefer mod.abortAnonDecl(new_decl); + // Enum tag type + var buffer: Value.ToTypeBuffer = undefined; + const int_tag_ty = try tag_type_val.toType(&buffer).copy(new_decl_arena_allocator); + enum_obj.* = .{ .owner_decl = new_decl, - .tag_ty = Type.@"null", - .tag_ty_inferred = true, + .tag_ty = int_tag_ty, + .tag_ty_inferred = false, .fields = .{}, .values = .{}, .node_offset = src.node_offset, @@ -12992,10 +13017,6 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I }, }; - // Enum tag type - var buffer: Value.ToTypeBuffer = undefined; - enum_obj.tag_ty = try tag_type_val.toType(&buffer).copy(new_decl_arena_allocator); - // Fields const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(target)); if (fields_len > 0) { @@ -21111,8 +21132,18 @@ fn resolveInferredErrorSet( return sema.fail(block, src, "unable to resolve inferred error set", .{}); } - // To ensure that all dependencies are properly added to the set. - try sema.ensureFuncBodyAnalyzed(ies.func); + // In order to ensure that all dependencies are properly added to the set, we + // need to ensure the function body is analyzed of the inferred error set. + // However, in the case of comptime/inline function calls with inferred error sets, + // each call gets a new InferredErrorSet object, which points to the same + // `*Module.Fn`. Not only is the function not relevant to the inferred error set + // in this case, it may be a generic function which would cause an assertion failure + // if we called `ensureFuncBodyAnalyzed` on it here. + if (ies.func.owner_decl.ty.fnInfo().return_type.errorUnionSet().castTag(.error_set_inferred).?.data == ies) { + // In this case we are dealing with the actual InferredErrorSet object that + // corresponds to the function, not one created to track an inline/comptime call. + try sema.ensureFuncBodyAnalyzed(ies.func); + } ies.is_resolved = true; diff --git a/test/behavior/generics.zig b/test/behavior/generics.zig index e9a46cbf5c1a..767c2f3ee3dd 100644 --- a/test/behavior/generics.zig +++ b/test/behavior/generics.zig @@ -241,3 +241,39 @@ test "function parameter is generic" { var rng: u32 = 2; S.init(rng, S.fill); } + +test "generic function instantiation turns into comptime call" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + + const S = struct { + fn doTheTest() !void { + const E1 = enum { A }; + const e1f = fieldInfo(E1, .A); + try expect(std.mem.eql(u8, e1f.name, "A")); + } + + pub fn fieldInfo(comptime T: type, comptime field: FieldEnum(T)) switch (@typeInfo(T)) { + .Enum => std.builtin.Type.EnumField, + else => void, + } { + return @typeInfo(T).Enum.fields[@enumToInt(field)]; + } + + pub fn FieldEnum(comptime T: type) type { + _ = T; + var enumFields: [1]std.builtin.Type.EnumField = .{.{ .name = "A", .value = 0 }}; + return @Type(.{ + .Enum = .{ + .layout = .Auto, + .tag_type = u0, + .fields = &enumFields, + .decls = &.{}, + .is_exhaustive = true, + }, + }); + } + }; + try S.doTheTest(); +}