support self-referential struct through a slice of optional

by making optionals even more lazy

closes #1805
This commit is contained in:
Andrew Kelley 2019-08-27 16:55:58 -04:00
parent c1fd7ed6e2
commit f4519c520a
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
5 changed files with 211 additions and 99 deletions

View File

@ -329,7 +329,7 @@ struct LazyValueSliceType {
LazyValue base;
IrAnalyze *ira;
ZigType *elem_type;
IrInstruction *elem_type;
IrInstruction *align_inst; // can be null
bool is_const;
@ -1222,6 +1222,7 @@ struct ZigTypeStruct {
struct ZigTypeOptional {
ZigType *child_type;
ResolveStatus resolve_status;
};
struct ZigTypeErrorUnion {

View File

@ -566,6 +566,7 @@ ZigType *get_optional_type(CodeGen *g, ZigType *child_type) {
}
entry->data.maybe.child_type = child_type;
entry->data.maybe.resolve_status = ResolveStatusSizeKnown;
child_type->optional_parent = entry;
return entry;
@ -1055,9 +1056,7 @@ static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ConstExprValue
zig_unreachable();
case LazyValueIdSliceType: {
LazyValueSliceType *lazy_slice_type = reinterpret_cast<LazyValueSliceType *>(type_val->data.x_lazy);
if (type_is_invalid(lazy_slice_type->elem_type))
return ReqCompTimeInvalid;
return type_requires_comptime(g, lazy_slice_type->elem_type);
return type_val_resolve_requires_comptime(g, &lazy_slice_type->elem_type->value);
}
case LazyValueIdPtrType: {
LazyValuePtrType *lazy_ptr_type = reinterpret_cast<LazyValuePtrType *>(type_val->data.x_lazy);
@ -1099,6 +1098,42 @@ static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ConstExprValue
zig_unreachable();
}
static Error type_val_resolve_abi_size(CodeGen *g, AstNode *source_node, ConstExprValue *type_val,
size_t *abi_size, size_t *size_in_bits)
{
Error err;
if (type_val->data.x_lazy->id == LazyValueIdOptType) {
if ((err = ir_resolve_lazy(g, source_node, type_val)))
return err;
}
if (type_val->special != ConstValSpecialLazy) {
assert(type_val->special == ConstValSpecialStatic);
ZigType *ty = type_val->data.x_type;
if ((err = type_resolve(g, ty, ResolveStatusSizeKnown)))
return err;
*abi_size = ty->abi_size;
*size_in_bits = ty->size_in_bits;
return ErrorNone;
}
switch (type_val->data.x_lazy->id) {
case LazyValueIdInvalid:
case LazyValueIdAlignOf:
zig_unreachable();
case LazyValueIdSliceType:
*abi_size = g->builtin_types.entry_usize->abi_size * 2;
*size_in_bits = g->builtin_types.entry_usize->size_in_bits * 2;
return ErrorNone;
case LazyValueIdPtrType:
case LazyValueIdFnType:
*abi_size = g->builtin_types.entry_usize->abi_size;
*size_in_bits = g->builtin_types.entry_usize->size_in_bits;
return ErrorNone;
case LazyValueIdOptType:
zig_unreachable();
}
zig_unreachable();
}
Error type_val_resolve_abi_align(CodeGen *g, ConstExprValue *type_val, uint32_t *abi_align) {
Error err;
if (type_val->special != ConstValSpecialLazy) {
@ -1767,6 +1802,17 @@ static size_t get_abi_size_bytes(size_t size_in_bits, size_t pointer_size_bytes)
return align_forward(store_size_bytes, abi_align);
}
ZigType *resolve_struct_field_type(CodeGen *g, TypeStructField *struct_field) {
Error err;
if (struct_field->type_entry == nullptr) {
if ((err = ir_resolve_lazy(g, struct_field->decl_node, struct_field->type_val))) {
return nullptr;
}
struct_field->type_entry = struct_field->type_val->data.x_type;
}
return struct_field->type_entry;
}
static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
assert(struct_type->id == ZigTypeIdStruct);
@ -1801,40 +1847,6 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
uint32_t *host_int_bytes = packed ? allocate<uint32_t>(struct_type->data.structure.gen_field_count) : nullptr;
// Resolve types for fields and then resolve sizes of all the field types.
// This is done before the offset loop because the offset loop has to look ahead.
for (size_t i = 0; i < field_count; i += 1) {
AstNode *field_source_node = decl_node->data.container_decl.fields.at(i);
TypeStructField *field = &struct_type->data.structure.fields[i];
if ((err = ir_resolve_lazy(g, field_source_node, field->type_val))) {
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return err;
}
field->type_entry = field->type_val->data.x_type;
if ((err = type_resolve(g, field->type_entry, ResolveStatusSizeKnown))) {
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return err;
}
if (struct_type->data.structure.layout == ContainerLayoutExtern &&
!type_allowed_in_extern(g, field->type_entry))
{
add_node_error(g, field_source_node,
buf_sprintf("extern structs cannot contain fields of type '%s'",
buf_ptr(&field->type_entry->name)));
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
} else if (packed) {
if ((err = emit_error_unless_type_allowed_in_packed_struct(g, field->type_entry, field_source_node))) {
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return err;
}
}
}
size_t packed_bits_offset = 0;
size_t next_offset = 0;
size_t first_packed_bits_offset_misalign = SIZE_MAX;
@ -1847,13 +1859,25 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
TypeStructField *field = &struct_type->data.structure.fields[i];
if (field->gen_index == SIZE_MAX)
continue;
ZigType *field_type = field->type_entry;
assert(field_type != nullptr);
field->gen_index = gen_field_index;
field->offset = next_offset;
if (packed) {
ZigType *field_type = resolve_struct_field_type(g, field);
if (field_type == nullptr) {
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return err;
}
if ((err = type_resolve(g, field->type_entry, ResolveStatusSizeKnown))) {
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return err;
}
if ((err = emit_error_unless_type_allowed_in_packed_struct(g, field->type_entry, field->decl_node))) {
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return err;
}
size_t field_size_in_bits = type_size_bits(g, field_type);
size_t next_packed_bits_offset = packed_bits_offset + field_size_in_bits;
@ -1889,6 +1913,15 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
}
packed_bits_offset = next_packed_bits_offset;
} else {
size_t field_abi_size;
size_t field_size_in_bits;
if ((err = type_val_resolve_abi_size(g, field->decl_node, field->type_val,
&field_abi_size, &field_size_in_bits)))
{
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return err;
}
gen_field_index += 1;
size_t next_src_field_index = i + 1;
for (; next_src_field_index < field_count; next_src_field_index += 1) {
@ -1898,7 +1931,7 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
}
size_t next_align = (next_src_field_index == field_count) ?
abi_align : struct_type->data.structure.fields[next_src_field_index].align;
next_offset = next_field_offset(next_offset, abi_align, field_type->abi_size, next_align);
next_offset = next_field_offset(next_offset, abi_align, field_abi_size, next_align);
size_in_bits = next_offset * 8;
}
}
@ -1917,6 +1950,36 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
struct_type->data.structure.resolve_loop_flag_other = false;
struct_type->data.structure.host_int_bytes = host_int_bytes;
// Resolve types for fields
if (!packed) {
for (size_t i = 0; i < field_count; i += 1) {
TypeStructField *field = &struct_type->data.structure.fields[i];
ZigType *field_type = resolve_struct_field_type(g, field);
if (field_type == nullptr) {
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return err;
}
if ((err = type_resolve(g, field_type, ResolveStatusSizeKnown))) {
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return err;
}
if (struct_type->data.structure.layout == ContainerLayoutExtern &&
!type_allowed_in_extern(g, field_type))
{
add_node_error(g, field->decl_node,
buf_sprintf("extern structs cannot contain fields of type '%s'",
buf_ptr(&field_type->name)));
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return ErrorSemanticAnalyzeFail;
}
}
}
return ErrorNone;
}
@ -7669,8 +7732,11 @@ static void resolve_llvm_types_integer(CodeGen *g, ZigType *type) {
type->llvm_type = LLVMIntType(type->size_in_bits);
}
static void resolve_llvm_types_optional(CodeGen *g, ZigType *type) {
if (type->llvm_di_type != nullptr) return;
static void resolve_llvm_types_optional(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) {
assert(type->id == ZigTypeIdOptional);
assert(type->data.maybe.resolve_status != ResolveStatusInvalid);
assert(type->data.maybe.resolve_status >= ResolveStatusSizeKnown);
if (type->data.maybe.resolve_status >= wanted_resolve_status) return;
LLVMTypeRef bool_llvm_type = get_llvm_type(g, g->builtin_types.entry_bool);
ZigLLVMDIType *bool_llvm_di_type = get_llvm_di_type(g, g->builtin_types.entry_bool);
@ -7679,30 +7745,41 @@ static void resolve_llvm_types_optional(CodeGen *g, ZigType *type) {
if (!type_has_bits(child_type)) {
type->llvm_type = bool_llvm_type;
type->llvm_di_type = bool_llvm_di_type;
type->data.maybe.resolve_status = ResolveStatusLLVMFull;
return;
}
if (type_is_nonnull_ptr(child_type) || child_type->id == ZigTypeIdErrorSet) {
type->llvm_type = get_llvm_type(g, child_type);
type->llvm_di_type = get_llvm_di_type(g, child_type);
type->data.maybe.resolve_status = ResolveStatusLLVMFull;
return;
}
ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
ZigLLVMDIFile *di_file = nullptr;
unsigned line = 0;
if (type->data.maybe.resolve_status < ResolveStatusLLVMFwdDecl) {
type->llvm_type = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(&type->name));
unsigned dwarf_kind = ZigLLVMTag_DW_structure_type();
type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
dwarf_kind, buf_ptr(&type->name),
compile_unit_scope, di_file, line);
type->data.maybe.resolve_status = ResolveStatusLLVMFwdDecl;
if (ResolveStatusLLVMFwdDecl >= wanted_resolve_status) return;
}
LLVMTypeRef child_llvm_type = get_llvm_type(g, child_type);
ZigLLVMDIType *child_llvm_di_type = get_llvm_di_type(g, child_type);
if (type_is_nonnull_ptr(child_type) || child_type->id == ZigTypeIdErrorSet) {
type->llvm_type = child_llvm_type;
type->llvm_di_type = child_llvm_di_type;
return;
}
if (type->data.maybe.resolve_status >= wanted_resolve_status) return;
LLVMTypeRef elem_types[] = {
get_llvm_type(g, child_type),
LLVMInt1Type(),
};
type->llvm_type = LLVMStructType(elem_types, 2, false);
ZigLLVMDIScope *compile_unit_scope = ZigLLVMCompileUnitToScope(g->compile_unit);
ZigLLVMDIFile *di_file = nullptr;
unsigned line = 0;
type->llvm_di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
ZigLLVMTag_DW_structure_type(), buf_ptr(&type->name),
compile_unit_scope, di_file, line);
LLVMStructSetBody(type->llvm_type, elem_types, 2, false);
uint64_t val_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, child_llvm_type);
uint64_t val_debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, child_llvm_type);
@ -7737,6 +7814,7 @@ static void resolve_llvm_types_optional(CodeGen *g, ZigType *type) {
ZigLLVMReplaceTemporary(g->dbuilder, type->llvm_di_type, replacement_di_type);
type->llvm_di_type = replacement_di_type;
type->data.maybe.resolve_status = ResolveStatusLLVMFull;
}
static void resolve_llvm_types_error_union(CodeGen *g, ZigType *type) {
@ -8180,7 +8258,7 @@ static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_r
case ZigTypeIdInt:
return resolve_llvm_types_integer(g, type);
case ZigTypeIdOptional:
return resolve_llvm_types_optional(g, type);
return resolve_llvm_types_optional(g, type, wanted_resolve_status);
case ZigTypeIdErrorUnion:
return resolve_llvm_types_error_union(g, type);
case ZigTypeIdArray:

View File

@ -248,5 +248,6 @@ bool fn_is_async(ZigFn *fn);
Error type_val_resolve_abi_align(CodeGen *g, ConstExprValue *type_val, uint32_t *abi_align);
ZigType *resolve_union_field_type(CodeGen *g, TypeUnionField *union_field);
ZigType *resolve_struct_field_type(CodeGen *g, TypeStructField *struct_field);
#endif

View File

@ -17127,11 +17127,14 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction
TypeStructField *field, IrInstruction *struct_ptr, ZigType *struct_type, bool initializing)
{
Error err;
switch (type_has_one_possible_value(ira->codegen, field->type_entry)) {
ZigType *field_type = resolve_struct_field_type(ira->codegen, field);
if (field_type == nullptr)
return ira->codegen->invalid_instruction;
switch (type_has_one_possible_value(ira->codegen, field_type)) {
case OnePossibleValueInvalid:
return ira->codegen->invalid_instruction;
case OnePossibleValueYes: {
IrInstruction *elem = ir_const(ira, source_instr, field->type_entry);
IrInstruction *elem = ir_const(ira, source_instr, field_type);
return ir_get_ref(ira, source_instr, elem, false, false);
}
case OnePossibleValueNo:
@ -17146,7 +17149,7 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction
get_host_int_bytes(ira->codegen, struct_type, field) : ptr_host_int_bytes;
bool is_const = struct_ptr->value.type->data.pointer.is_const;
bool is_volatile = struct_ptr->value.type->data.pointer.is_volatile;
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry,
ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type,
is_const, is_volatile, PtrLenSingle, field->align,
(uint32_t)(ptr_bit_offset + field->bit_offset_in_host),
(uint32_t)host_int_bytes_for_result_type, false);
@ -17945,48 +17948,14 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira,
return ira->codegen->invalid_instruction;
}
lazy_slice_type->elem_type = ir_resolve_type(ira, slice_type_instruction->child_type->child);
if (type_is_invalid(lazy_slice_type->elem_type))
lazy_slice_type->elem_type = slice_type_instruction->child_type->child;
if (ir_resolve_type_lazy(ira, lazy_slice_type->elem_type) == nullptr)
return ira->codegen->invalid_instruction;
lazy_slice_type->is_const = slice_type_instruction->is_const;
lazy_slice_type->is_volatile = slice_type_instruction->is_volatile;
lazy_slice_type->is_allowzero = slice_type_instruction->is_allow_zero;
switch (lazy_slice_type->elem_type->id) {
case ZigTypeIdInvalid: // handled above
zig_unreachable();
case ZigTypeIdUnreachable:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
ir_add_error_node(ira, slice_type_instruction->base.source_node,
buf_sprintf("slice of type '%s' not allowed", buf_ptr(&lazy_slice_type->elem_type->name)));
return ira->codegen->invalid_instruction;
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdPointer:
case ZigTypeIdArray:
case ZigTypeIdStruct:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdBoundFn:
case ZigTypeIdVector:
case ZigTypeIdFnFrame:
case ZigTypeIdAnyFrame:
break;
}
return result;
}
@ -20430,6 +20399,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr
inner_fields[1].special = ConstValSpecialStatic;
inner_fields[1].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int);
ZigType *field_type = resolve_struct_field_type(ira->codegen, struct_field);
if (field_type == nullptr)
return ErrorSemanticAnalyzeFail;
if ((err = type_resolve(ira->codegen, field_type, ResolveStatusZeroBitsKnown)))
return err;
if (!type_has_bits(struct_field->type_entry)) {
inner_fields[1].data.x_optional = nullptr;
} else {
@ -25588,11 +25562,50 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) {
if (!ir_resolve_align(ira, lazy_slice_type->align_inst, &align_bytes))
return ErrorSemanticAnalyzeFail;
}
ZigType *elem_type = ir_resolve_type(ira, lazy_slice_type->elem_type);
if (type_is_invalid(elem_type))
return ErrorSemanticAnalyzeFail;
switch (elem_type->id) {
case ZigTypeIdInvalid: // handled above
zig_unreachable();
case ZigTypeIdUnreachable:
case ZigTypeIdUndefined:
case ZigTypeIdNull:
case ZigTypeIdArgTuple:
case ZigTypeIdOpaque:
ir_add_error(ira, lazy_slice_type->elem_type,
buf_sprintf("slice of type '%s' not allowed", buf_ptr(&elem_type->name)));
return ErrorSemanticAnalyzeFail;
case ZigTypeIdMetaType:
case ZigTypeIdVoid:
case ZigTypeIdBool:
case ZigTypeIdInt:
case ZigTypeIdFloat:
case ZigTypeIdPointer:
case ZigTypeIdArray:
case ZigTypeIdStruct:
case ZigTypeIdComptimeFloat:
case ZigTypeIdComptimeInt:
case ZigTypeIdEnumLiteral:
case ZigTypeIdOptional:
case ZigTypeIdErrorUnion:
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdUnion:
case ZigTypeIdFn:
case ZigTypeIdBoundFn:
case ZigTypeIdVector:
case ZigTypeIdFnFrame:
case ZigTypeIdAnyFrame:
break;
}
ResolveStatus needed_status = (align_bytes == 0) ?
ResolveStatusZeroBitsKnown : ResolveStatusAlignmentKnown;
if ((err = type_resolve(ira->codegen, lazy_slice_type->elem_type, needed_status)))
if ((err = type_resolve(ira->codegen, elem_type, needed_status)))
return err;
ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, lazy_slice_type->elem_type,
ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, elem_type,
lazy_slice_type->is_const, lazy_slice_type->is_volatile, PtrLenUnknown, align_bytes,
0, 0, lazy_slice_type->is_allowzero);
val->special = ConstValSpecialStatic;

View File

@ -100,3 +100,22 @@ test "nested orelse" {
S.entry();
comptime S.entry();
}
test "self-referential struct through a slice of optional" {
const S = struct {
const Node = struct {
children: []?Node,
data: ?u8,
fn new() Node {
return Node{
.children = undefined,
.data = null,
};
}
};
};
var n = S.Node.new();
expect(n.data == null);
}