authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-29 20:11:48-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-29 20:11:48-07:00
log9821a0c6f0caf3df4cc6000c000d9cc38baf27d8
treebaa31bbf0e3fc3a5cbed557eb2a800ccbf2b55d6
parente39c86399da565d3855d54b242bf7b5940e3924d

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.

3 files changed, 52 insertions(+), 9 deletions(-)

src/Sema.zig+33-7
......@@ -4764,12 +4764,20 @@ fn analyzeCall(
47644764
47654765 const gpa = sema.gpa;
47664766
4767 var is_comptime_call = block.is_comptime or modifier == .compile_time or
4768 try sema.typeRequiresComptime(block, func_src, func_ty_info.return_type);
4767 var is_generic_call = func_ty_info.is_generic;
4768 var is_comptime_call = block.is_comptime or modifier == .compile_time;
4769 if (!is_comptime_call) {
4770 if (sema.typeRequiresComptime(block, func_src, func_ty_info.return_type)) |ct| {
4771 is_comptime_call = ct;
4772 } else |err| switch (err) {
4773 error.GenericPoison => is_generic_call = true,
4774 else => |e| return e,
4775 }
4776 }
47694777 var is_inline_call = is_comptime_call or modifier == .always_inline or
47704778 func_ty_info.cc == .Inline;
47714779
4772 if (!is_inline_call and func_ty_info.is_generic) {
4780 if (!is_inline_call and is_generic_call) {
47734781 if (sema.instantiateGenericCall(
47744782 block,
47754783 func,
......@@ -6410,10 +6418,20 @@ fn funcCommon(
64106418 }
64116419 }
64126420
6413 is_generic = is_generic or
6414 try sema.typeRequiresComptime(block, ret_ty_src, bare_return_type);
6421 const ret_poison = if (!is_generic) rp: {
6422 if (sema.typeRequiresComptime(block, ret_ty_src, bare_return_type)) |ret_comptime| {
6423 is_generic = ret_comptime;
6424 break :rp bare_return_type.tag() == .generic_poison;
6425 } else |err| switch (err) {
6426 error.GenericPoison => {
6427 is_generic = true;
6428 break :rp true;
6429 },
6430 else => |e| return e,
6431 }
6432 } else bare_return_type.tag() == .generic_poison;
64156433
6416 const return_type = if (!inferred_error_set or bare_return_type.tag() == .generic_poison)
6434 const return_type = if (!inferred_error_set or ret_poison)
64176435 bare_return_type
64186436 else blk: {
64196437 const node = try sema.gpa.create(Module.Fn.InferredErrorSetListNode);
......@@ -13570,7 +13588,7 @@ fn reifyStruct(
1357013588 .zir_index = inst,
1357113589 .layout = layout_val.toEnum(std.builtin.Type.ContainerLayout),
1357213590 .status = .have_field_types,
13573 .known_non_opv = undefined,
13591 .known_non_opv = false,
1357413592 .namespace = .{
1357513593 .parent = block.namespace,
1357613594 .ty = struct_ty,
......@@ -21666,6 +21684,10 @@ fn semaStructFields(
2166621684 // TODO emit compile errors for invalid field types
2166721685 // such as arrays and pointers inside packed structs.
2166821686
21687 if (field_ty.tag() == .generic_poison) {
21688 return error.GenericPoison;
21689 }
21690
2166921691 const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
2167021692 assert(!gop.found_existing);
2167121693 gop.value_ptr.* = .{
......@@ -21913,6 +21935,10 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
2191321935 // But only resolve the source location if we need to emit a compile error.
2191421936 try sema.resolveType(&block_scope, src, field_type_ref);
2191521937
21938 if (field_ty.tag() == .generic_poison) {
21939 return error.GenericPoison;
21940 }
21941
2191621942 const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);
2191721943 assert(!gop.found_existing);
2191821944 gop.value_ptr.* = .{
src/Zir.zig+3-2
......@@ -63,7 +63,7 @@ pub const ExtraIndex = enum(u32) {
6363/// Returns the requested data, as well as the new index which is at the start of the
6464/// trailers for the object.
6565pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, end: usize } {
66 const fields = std.meta.fields(T);
66 const fields = @typeInfo(T).Struct.fields;
6767 var i: usize = index;
6868 var result: T = undefined;
6969 inline for (fields) |field| {
......@@ -94,7 +94,8 @@ pub fn nullTerminatedString(code: Zir, index: usize) [:0]const u8 {
9494
9595pub fn refSlice(code: Zir, start: usize, len: usize) []Inst.Ref {
9696 const raw_slice = code.extra[start..][0..len];
97 return @bitCast([]Inst.Ref, raw_slice);
97 // TODO we should be able to directly `@ptrCast` the slice to the other slice type.
98 return @ptrCast([*]Inst.Ref, raw_slice.ptr)[0..len];
9899}
99100
100101pub fn hasCompileErrors(code: Zir) bool {
test/behavior/generics.zig+16
......@@ -290,3 +290,19 @@ test "generic function with void and comptime parameter" {
290290 var s: S = .{ .x = 1234 };
291291 try namespace.foo({}, &s, u8);
292292}
293
294test "anonymous struct return type referencing comptime parameter" {
295 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
296
297 const S = struct {
298 pub fn extraData(comptime T: type, index: usize) struct { data: T, end: usize } {
299 return .{
300 .data = 1234,
301 .end = index,
302 };
303 }
304 };
305 const s = S.extraData(i32, 5678);
306 try expect(s.data == 1234);
307 try expect(s.end == 5678);
308}