Add two more resolution status' to Struct and Union

resolveTypeForCodegen is called when we needed to resolve a type fully,
even through pointer. This commit fully implements this, even through
pointer fields on structs and unions.

The function has now also been renamed to resolveTypeFully
This commit is contained in:
Jimmi Holst Christensen 2022-01-07 18:58:40 +01:00
parent d5093b6c13
commit 9d6bef49a5
3 changed files with 116 additions and 19 deletions

View File

@ -831,6 +831,10 @@ pub const Struct = struct {
have_field_types,
layout_wip,
have_layout,
fully_resolved_wip,
// The types and all its fields have had their layout resolved. Even through pointer,
// which `have_layout` does not ensure.
fully_resolved,
},
/// If true, definitely nonzero size at runtime. If false, resolving the fields
/// is necessary to determine whether it has bits at runtime.
@ -889,6 +893,22 @@ pub const Struct = struct {
.have_field_types,
.layout_wip,
.have_layout,
.fully_resolved_wip,
.fully_resolved,
=> true,
};
}
pub fn haveLayout(s: Struct) bool {
return switch (s.status) {
.none,
.field_types_wip,
.have_field_types,
.layout_wip,
=> false,
.have_layout,
.fully_resolved_wip,
.fully_resolved,
=> true,
};
}
@ -1003,6 +1023,10 @@ pub const Union = struct {
have_field_types,
layout_wip,
have_layout,
fully_resolved_wip,
// The types and all its fields have had their layout resolved. Even through pointer,
// which `have_layout` does not ensure.
fully_resolved,
},
pub const Field = struct {
@ -1033,6 +1057,8 @@ pub const Union = struct {
.have_field_types,
.layout_wip,
.have_layout,
.fully_resolved_wip,
.fully_resolved,
=> true,
};
}
@ -1102,8 +1128,22 @@ pub const Union = struct {
tag_size: u64,
};
pub fn haveLayout(u: Union) bool {
return switch (u.status) {
.none,
.field_types_wip,
.have_field_types,
.layout_wip,
=> false,
.have_layout,
.fully_resolved_wip,
.fully_resolved,
=> true,
};
}
pub fn getLayout(u: Union, target: Target, have_tag: bool) Layout {
assert(u.status == .have_layout);
assert(u.haveLayout());
var most_aligned_field: u32 = undefined;
var most_aligned_field_size: u64 = undefined;
var biggest_field: u32 = undefined;

View File

@ -4503,14 +4503,14 @@ fn analyzeCall(
const arg_src = call_src; // TODO: better source location
if (i < fn_params_len) {
const param_ty = func_ty.fnParamType(i);
try sema.resolveTypeForCodegen(block, arg_src, param_ty);
try sema.resolveTypeFully(block, arg_src, param_ty);
args[i] = try sema.coerce(block, param_ty, uncasted_arg, arg_src);
} else {
args[i] = uncasted_arg;
}
}
try sema.resolveTypeForCodegen(block, call_src, func_ty_info.return_type);
try sema.resolveTypeFully(block, call_src, func_ty_info.return_type);
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +
args.len);
@ -4580,7 +4580,7 @@ fn finishGenericCall(
const param_ty = new_fn_ty.fnParamType(runtime_i);
const arg_src = call_src; // TODO: better source location
const uncasted_arg = uncasted_args[total_i];
try sema.resolveTypeForCodegen(block, arg_src, param_ty);
try sema.resolveTypeFully(block, arg_src, param_ty);
const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src);
runtime_args[runtime_i] = casted_arg;
runtime_i += 1;
@ -4588,7 +4588,7 @@ fn finishGenericCall(
total_i += 1;
}
try sema.resolveTypeForCodegen(block, call_src, new_fn_ty.fnReturnType());
try sema.resolveTypeFully(block, call_src, new_fn_ty.fnReturnType());
}
try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len +
runtime_args_len);
@ -15228,7 +15228,7 @@ fn resolveStructLayout(
.field_types_wip, .layout_wip => {
return sema.fail(block, src, "struct {} depends on itself", .{ty});
},
.have_layout => return,
.have_layout, .fully_resolved_wip, .fully_resolved => return,
}
struct_obj.status = .layout_wip;
for (struct_obj.fields.values()) |field| {
@ -15250,7 +15250,7 @@ fn resolveUnionLayout(
.field_types_wip, .layout_wip => {
return sema.fail(block, src, "union {} depends on itself", .{ty});
},
.have_layout => return,
.have_layout, .fully_resolved_wip, .fully_resolved => return,
}
union_obj.status = .layout_wip;
for (union_obj.fields.values()) |field| {
@ -15259,7 +15259,7 @@ fn resolveUnionLayout(
union_obj.status = .have_layout;
}
fn resolveTypeForCodegen(
fn resolveTypeFully(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
@ -15268,20 +15268,67 @@ fn resolveTypeForCodegen(
switch (ty.zigTypeTag()) {
.Pointer => {
const child_ty = try sema.resolveTypeFields(block, src, ty.childType());
return resolveTypeForCodegen(sema, block, src, child_ty);
return resolveTypeFully(sema, block, src, child_ty);
},
.Struct => return resolveStructLayout(sema, block, src, ty),
.Union => return resolveUnionLayout(sema, block, src, ty),
.Array => return resolveTypeForCodegen(sema, block, src, ty.childType()),
.Struct => return resolveStructFully(sema, block, src, ty),
.Union => return resolveUnionFully(sema, block, src, ty),
.Array => return resolveTypeFully(sema, block, src, ty.childType()),
.Optional => {
var buf: Type.Payload.ElemType = undefined;
return resolveTypeForCodegen(sema, block, src, ty.optionalChild(&buf));
return resolveTypeFully(sema, block, src, ty.optionalChild(&buf));
},
.ErrorUnion => return resolveTypeForCodegen(sema, block, src, ty.errorUnionPayload()),
.ErrorUnion => return resolveTypeFully(sema, block, src, ty.errorUnionPayload()),
else => {},
}
}
fn resolveStructFully(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
ty: Type,
) CompileError!void {
try resolveStructLayout(sema, block, src, ty);
const resolved_ty = try sema.resolveTypeFields(block, src, ty);
const struct_obj = resolved_ty.castTag(.@"struct").?.data;
switch (struct_obj.status) {
.none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {},
.fully_resolved_wip, .fully_resolved => return,
}
// After we have resolve struct layout we have to go over the fields again to
// make sure pointer fields get their child types resolved as well
struct_obj.status = .fully_resolved_wip;
for (struct_obj.fields.values()) |field| {
try sema.resolveTypeFully(block, src, field.ty);
}
struct_obj.status = .fully_resolved;
}
fn resolveUnionFully(
sema: *Sema,
block: *Block,
src: LazySrcLoc,
ty: Type,
) CompileError!void {
try resolveUnionLayout(sema, block, src, ty);
const resolved_ty = try sema.resolveTypeFields(block, src, ty);
const union_obj = resolved_ty.cast(Type.Payload.Union).?.data;
switch (union_obj.status) {
.none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {},
.fully_resolved_wip, .fully_resolved => return,
}
// Same goes for unions (see comment about structs)
union_obj.status = .fully_resolved_wip;
for (union_obj.fields.values()) |field| {
try sema.resolveTypeFully(block, src, field.ty);
}
union_obj.status = .fully_resolved;
}
fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!Type {
switch (ty.tag()) {
.@"struct" => {
@ -15291,7 +15338,12 @@ fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) Comp
.field_types_wip => {
return sema.fail(block, src, "struct {} depends on itself", .{ty});
},
.have_field_types, .have_layout, .layout_wip => return ty,
.have_field_types,
.have_layout,
.layout_wip,
.fully_resolved_wip,
.fully_resolved,
=> return ty,
}
struct_obj.status = .field_types_wip;
@ -15324,7 +15376,12 @@ fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) Comp
.field_types_wip => {
return sema.fail(block, src, "union {} depends on itself", .{ty});
},
.have_field_types, .have_layout, .layout_wip => return ty,
.have_field_types,
.have_layout,
.layout_wip,
.fully_resolved_wip,
.fully_resolved,
=> return ty,
}
union_obj.status = .field_types_wip;

View File

@ -1916,7 +1916,7 @@ pub const Type = extern union {
const fields = self.structFields();
const is_packed = if (self.castTag(.@"struct")) |payload| p: {
const struct_obj = payload.data;
assert(struct_obj.status == .have_layout);
assert(struct_obj.haveLayout());
break :p struct_obj.layout == .Packed;
} else false;
@ -2220,7 +2220,7 @@ pub const Type = extern union {
if (field_count == 0) return 0;
const struct_obj = ty.castTag(.@"struct").?.data;
assert(struct_obj.status == .have_layout);
assert(struct_obj.haveLayout());
var total: u64 = 0;
for (struct_obj.fields.values()) |field| {
@ -3771,7 +3771,7 @@ pub const Type = extern union {
switch (ty.tag()) {
.@"struct" => {
const struct_obj = ty.castTag(.@"struct").?.data;
assert(struct_obj.status == .have_layout);
assert(struct_obj.haveLayout());
const is_packed = struct_obj.layout == .Packed;
if (!is_packed) {
var offset: u64 = 0;