Sema: fix generic instantiations of return types with nested captures

* In semaStructFields and semaUnionFields we return error.GenericPoison
   if one of the field types ends up being generic poison.
   - This requires handling function calls and function types taking
     this into account when calling `typeRequiresComptime` on the return
     type.
 * Unrelated: I noticed using Valgrind that struct reification did not
   populate the `known_opv` field. After fixing it, the behavior tests
   run Valgrind-clean.
 * ZIR: use `@ptrCast` to cast between slices instead of exploiting
   the fact that stage1 incorrectly allows `@bitCast` between slices.
   - A future enhancement will make Zig support `@ptrCast` to directly
     cast between slices.
This commit is contained in:
Andrew Kelley
2022-03-29 20:11:48 -07:00
parent e39c86399d
commit 9821a0c6f0
3 changed files with 52 additions and 9 deletions

View File

@@ -4764,12 +4764,20 @@ fn analyzeCall(
const gpa = sema.gpa;
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_generic_call = func_ty_info.is_generic;
var is_comptime_call = block.is_comptime or modifier == .compile_time;
if (!is_comptime_call) {
if (sema.typeRequiresComptime(block, func_src, func_ty_info.return_type)) |ct| {
is_comptime_call = ct;
} else |err| switch (err) {
error.GenericPoison => is_generic_call = true,
else => |e| return e,
}
}
var is_inline_call = is_comptime_call or modifier == .always_inline or
func_ty_info.cc == .Inline;
if (!is_inline_call and func_ty_info.is_generic) {
if (!is_inline_call and is_generic_call) {
if (sema.instantiateGenericCall(
block,
func,
@@ -6410,10 +6418,20 @@ fn funcCommon(
}
}
is_generic = is_generic or
try sema.typeRequiresComptime(block, ret_ty_src, bare_return_type);
const ret_poison = if (!is_generic) rp: {
if (sema.typeRequiresComptime(block, ret_ty_src, bare_return_type)) |ret_comptime| {
is_generic = ret_comptime;
break :rp bare_return_type.tag() == .generic_poison;
} else |err| switch (err) {
error.GenericPoison => {
is_generic = true;
break :rp true;
},
else => |e| return e,
}
} else bare_return_type.tag() == .generic_poison;
const return_type = if (!inferred_error_set or bare_return_type.tag() == .generic_poison)
const return_type = if (!inferred_error_set or ret_poison)
bare_return_type
else blk: {
const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode);
@@ -13570,7 +13588,7 @@ fn reifyStruct(
.zir_index = inst,
.layout = layout_val.toEnum(std.builtin.Type.ContainerLayout),
.status = .have_field_types,
.known_non_opv = undefined,
.known_non_opv = false,
.namespace = .{
.parent = block.namespace,
.ty = struct_ty,
@@ -21666,6 +21684,10 @@ fn semaStructFields(
// TODO emit compile errors for invalid field types
// such as arrays and pointers inside packed structs.
if (field_ty.tag() == .generic_poison) {
return error.GenericPoison;
}
const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
assert(!gop.found_existing);
gop.value_ptr.* = .{
@@ -21913,6 +21935,10 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
// But only resolve the source location if we need to emit a compile error.
try sema.resolveType(&block_scope, src, field_type_ref);
if (field_ty.tag() == .generic_poison) {
return error.GenericPoison;
}
const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);
assert(!gop.found_existing);
gop.value_ptr.* = .{

View File

@@ -63,7 +63,7 @@ pub const ExtraIndex = enum(u32) {
/// Returns the requested data, as well as the new index which is at the start of the
/// trailers for the object.
pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, end: usize } {
const fields = std.meta.fields(T);
const fields = @typeInfo(T).Struct.fields;
var i: usize = index;
var result: T = undefined;
inline for (fields) |field| {
@@ -94,7 +94,8 @@ pub fn nullTerminatedString(code: Zir, index: usize) [:0]const u8 {
pub fn refSlice(code: Zir, start: usize, len: usize) []Inst.Ref {
const raw_slice = code.extra[start..][0..len];
return @bitCast([]Inst.Ref, raw_slice);
// TODO we should be able to directly `@ptrCast` the slice to the other slice type.
return @ptrCast([*]Inst.Ref, raw_slice.ptr)[0..len];
}
pub fn hasCompileErrors(code: Zir) bool {

View File

@@ -290,3 +290,19 @@ test "generic function with void and comptime parameter" {
var s: S = .{ .x = 1234 };
try namespace.foo({}, &s, u8);
}
test "anonymous struct return type referencing comptime parameter" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
const S = struct {
pub fn extraData(comptime T: type, index: usize) struct { data: T, end: usize } {
return .{
.data = 1234,
.end = index,
};
}
};
const s = S.extraData(i32, 5678);
try expect(s.data == 1234);
try expect(s.end == 5678);
}