mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
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.
This commit is contained in:
parent
aca42c6259
commit
2af69710a7
4 changed files with 88 additions and 21 deletions
|
|
@ -569,10 +569,10 @@ test "std.meta.fieldNames" {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn FieldEnum(comptime T: type) type {
|
pub fn FieldEnum(comptime T: type) type {
|
||||||
const fieldInfos = fields(T);
|
const field_infos = fields(T);
|
||||||
var enumFields: [fieldInfos.len]std.builtin.Type.EnumField = undefined;
|
var enumFields: [field_infos.len]std.builtin.Type.EnumField = undefined;
|
||||||
var decls = [_]std.builtin.Type.Declaration{};
|
var decls = [_]std.builtin.Type.Declaration{};
|
||||||
inline for (fieldInfos) |field, i| {
|
inline for (field_infos) |field, i| {
|
||||||
enumFields[i] = .{
|
enumFields[i] = .{
|
||||||
.name = field.name,
|
.name = field.name,
|
||||||
.value = i,
|
.value = i,
|
||||||
|
|
@ -581,7 +581,7 @@ pub fn FieldEnum(comptime T: type) type {
|
||||||
return @Type(.{
|
return @Type(.{
|
||||||
.Enum = .{
|
.Enum = .{
|
||||||
.layout = .Auto,
|
.layout = .Auto,
|
||||||
.tag_type = std.math.IntFittingRange(0, fieldInfos.len - 1),
|
.tag_type = std.math.IntFittingRange(0, field_infos.len - 1),
|
||||||
.fields = &enumFields,
|
.fields = &enumFields,
|
||||||
.decls = &decls,
|
.decls = &decls,
|
||||||
.is_exhaustive = true,
|
.is_exhaustive = true,
|
||||||
|
|
@ -966,7 +966,7 @@ pub fn ArgsTuple(comptime Function: type) type {
|
||||||
argument_field_list[i] = .{
|
argument_field_list[i] = .{
|
||||||
.name = std.fmt.bufPrint(&num_buf, "{d}", .{i}) catch unreachable,
|
.name = std.fmt.bufPrint(&num_buf, "{d}", .{i}) catch unreachable,
|
||||||
.field_type = T,
|
.field_type = T,
|
||||||
.default_value = @as(?T, null),
|
.default_value = null,
|
||||||
.is_comptime = false,
|
.is_comptime = false,
|
||||||
.alignment = if (@sizeOf(T) > 0) @alignOf(T) else 0,
|
.alignment = if (@sizeOf(T) > 0) @alignOf(T) else 0,
|
||||||
};
|
};
|
||||||
|
|
@ -997,7 +997,7 @@ pub fn Tuple(comptime types: []const type) type {
|
||||||
tuple_fields[i] = .{
|
tuple_fields[i] = .{
|
||||||
.name = std.fmt.bufPrint(&num_buf, "{d}", .{i}) catch unreachable,
|
.name = std.fmt.bufPrint(&num_buf, "{d}", .{i}) catch unreachable,
|
||||||
.field_type = T,
|
.field_type = T,
|
||||||
.default_value = @as(?T, null),
|
.default_value = null,
|
||||||
.is_comptime = false,
|
.is_comptime = false,
|
||||||
.alignment = if (@sizeOf(T) > 0) @alignOf(T) else 0,
|
.alignment = if (@sizeOf(T) > 0) @alignOf(T) else 0,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -781,11 +781,11 @@ pub const Decl = struct {
|
||||||
return &decl_plus_emit_h.emit_h;
|
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));
|
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));
|
assert(decl.dependencies.swapRemove(other));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
57
src/Sema.zig
57
src/Sema.zig
|
|
@ -4688,7 +4688,7 @@ fn analyzeCall(
|
||||||
|
|
||||||
const gpa = sema.gpa;
|
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);
|
try sema.typeRequiresComptime(block, func_src, func_ty_info.return_type);
|
||||||
var is_inline_call = is_comptime_call or modifier == .always_inline or
|
var is_inline_call = is_comptime_call or modifier == .always_inline or
|
||||||
func_ty_info.cc == .Inline;
|
func_ty_info.cc == .Inline;
|
||||||
|
|
@ -4706,7 +4706,13 @@ fn analyzeCall(
|
||||||
)) |some| {
|
)) |some| {
|
||||||
return some;
|
return some;
|
||||||
} else |err| switch (err) {
|
} 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,
|
else => |e| return e,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -5149,7 +5155,13 @@ fn instantiateGenericCall(
|
||||||
// of each of its instantiations.
|
// of each of its instantiations.
|
||||||
assert(new_decl.dependencies.keys().len == 0);
|
assert(new_decl.dependencies.keys().len == 0);
|
||||||
try mod.declareDeclDependency(new_decl, module_fn.owner_decl);
|
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);
|
var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
|
||||||
errdefer new_decl_arena.deinit();
|
errdefer new_decl_arena.deinit();
|
||||||
|
|
@ -5285,8 +5297,17 @@ fn instantiateGenericCall(
|
||||||
|
|
||||||
// Populate the Decl ty/val with the function and its type.
|
// 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);
|
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 the call evaluated to a return type that requires comptime, never mind
|
||||||
if (new_decl.ty.fnInfo().is_generic) return error.GenericPoison;
|
// 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.val = try Value.Tag.function.create(new_decl_arena_allocator, new_func);
|
||||||
new_decl.has_tv = true;
|
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;
|
new_decl.owns_tv = true;
|
||||||
errdefer mod.abortAnonDecl(new_decl);
|
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.* = .{
|
enum_obj.* = .{
|
||||||
.owner_decl = new_decl,
|
.owner_decl = new_decl,
|
||||||
.tag_ty = Type.@"null",
|
.tag_ty = int_tag_ty,
|
||||||
.tag_ty_inferred = true,
|
.tag_ty_inferred = false,
|
||||||
.fields = .{},
|
.fields = .{},
|
||||||
.values = .{},
|
.values = .{},
|
||||||
.node_offset = src.node_offset,
|
.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
|
// Fields
|
||||||
const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(target));
|
const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(target));
|
||||||
if (fields_len > 0) {
|
if (fields_len > 0) {
|
||||||
|
|
@ -21111,8 +21132,18 @@ fn resolveInferredErrorSet(
|
||||||
return sema.fail(block, src, "unable to resolve inferred error set", .{});
|
return sema.fail(block, src, "unable to resolve inferred error set", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
// To ensure that all dependencies are properly added to the set.
|
// In order to ensure that all dependencies are properly added to the set, we
|
||||||
try sema.ensureFuncBodyAnalyzed(ies.func);
|
// 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;
|
ies.is_resolved = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -241,3 +241,39 @@ test "function parameter is generic" {
|
||||||
var rng: u32 = 2;
|
var rng: u32 = 2;
|
||||||
S.init(rng, S.fill);
|
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();
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue