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:
40
src/Sema.zig
40
src/Sema.zig
@@ -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.* = .{
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user