add lazy value for fn prototypes

this case now works:

```zig
const Node = struct {
    field: fn (*Node) *Node,
};
```
This commit is contained in:
Andrew Kelley 2019-08-23 13:28:26 -04:00
parent 3865b6ad8f
commit 20049caaba
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
4 changed files with 183 additions and 63 deletions

View File

@ -303,11 +303,12 @@ enum LazyValueId {
LazyValueIdAlignOf,
LazyValueIdPtrType,
LazyValueIdSliceType,
LazyValueIdFnType,
};
struct LazyValue {
LazyValueId id;
IrExecutable *exec;
LazyValueId id;
};
struct LazyValueAlignOf {
@ -317,23 +318,35 @@ struct LazyValueAlignOf {
struct LazyValueSliceType {
LazyValue base;
ZigType *elem_type;
ConstExprValue *align_val; // can be null
bool is_const;
bool is_volatile;
bool is_allowzero;
ZigType *elem_type;
ConstExprValue *align_val; // can be null
};
struct LazyValuePtrType {
LazyValue base;
bool is_const;
bool is_volatile;
bool is_allowzero;
ZigType *elem_type;
ConstExprValue *align_val; // can be null
PtrLen ptr_len;
uint32_t bit_offset_in_host;
uint32_t host_int_bytes;
bool is_const;
bool is_volatile;
bool is_allowzero;
};
struct LazyValueFnType {
LazyValue base;
bool is_generic;
AstNode *proto_node;
ConstExprValue **param_types;
ConstExprValue *align_val; // can be null
ConstExprValue *return_type;
};
struct ConstExprValue {

View File

@ -986,11 +986,16 @@ static Error type_val_resolve_zero_bits(CodeGen *g, ConstExprValue *type_val, Zi
case LazyValueIdSliceType:
*is_zero_bits = false;
return ErrorNone;
case LazyValueIdFnType: {
LazyValueFnType *lazy_fn_type = reinterpret_cast<LazyValueFnType *>(type_val->data.x_lazy);
*is_zero_bits = lazy_fn_type->is_generic;
return ErrorNone;
}
}
zig_unreachable();
}
static Error type_val_resolve_is_opaque_type(CodeGen *g, ConstExprValue *type_val, bool *is_opaque_type) {
Error type_val_resolve_is_opaque_type(CodeGen *g, ConstExprValue *type_val, bool *is_opaque_type) {
if (type_val->special != ConstValSpecialLazy) {
assert(type_val->special == ConstValSpecialStatic);
*is_opaque_type = (type_val->data.x_type->id == ZigTypeIdOpaque);
@ -1002,6 +1007,7 @@ static Error type_val_resolve_is_opaque_type(CodeGen *g, ConstExprValue *type_va
zig_unreachable();
case LazyValueIdSliceType:
case LazyValueIdPtrType:
case LazyValueIdFnType:
*is_opaque_type = false;
return ErrorNone;
}
@ -1028,6 +1034,34 @@ static ReqCompTime type_val_resolve_requires_comptime(CodeGen *g, ConstExprValue
return ReqCompTimeInvalid;
return type_requires_comptime(g, lazy_ptr_type->elem_type, parent_type);
}
case LazyValueIdFnType: {
LazyValueFnType *lazy_fn_type = reinterpret_cast<LazyValueFnType *>(type_val->data.x_lazy);
if (lazy_fn_type->is_generic)
return ReqCompTimeYes;
switch (type_val_resolve_requires_comptime(g, lazy_fn_type->return_type, parent_type)) {
case ReqCompTimeInvalid:
return ReqCompTimeInvalid;
case ReqCompTimeYes:
return ReqCompTimeYes;
case ReqCompTimeNo:
break;
}
size_t param_count = lazy_fn_type->proto_node->data.fn_proto.params.length;
for (size_t i = 0; i < param_count; i += 1) {
AstNode *param_node = lazy_fn_type->proto_node->data.fn_proto.params.at(i);
bool param_is_var_args = param_node->data.param_decl.is_var_args;
if (param_is_var_args) break;
switch (type_val_resolve_requires_comptime(g, lazy_fn_type->param_types[i], parent_type)) {
case ReqCompTimeInvalid:
return ReqCompTimeInvalid;
case ReqCompTimeYes:
return ReqCompTimeYes;
case ReqCompTimeNo:
break;
}
}
return ReqCompTimeNo;
}
}
zig_unreachable();
}
@ -1047,6 +1081,7 @@ static Error type_val_resolve_abi_align(CodeGen *g, ConstExprValue *type_val, si
zig_unreachable();
case LazyValueIdSliceType:
case LazyValueIdPtrType:
case LazyValueIdFnType:
*abi_align = g->builtin_types.entry_usize->abi_align;
return ErrorNone;
}
@ -1061,8 +1096,9 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, Cons
case LazyValueIdInvalid:
case LazyValueIdAlignOf:
zig_unreachable();
case LazyValueIdSliceType:
return OnePossibleValueNo; // it has the len field
case LazyValueIdSliceType: // it has the len field
case LazyValueIdFnType:
return OnePossibleValueNo;
case LazyValueIdPtrType: {
Error err;
bool zero_bits;
@ -2395,10 +2431,12 @@ static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) {
} else if (packed) {
field->align = 1;
} else {
if ((err = type_val_resolve_abi_align(g, field->type_val, &field->align))) {
size_t result_abi_align;
if ((err = type_val_resolve_abi_align(g, field->type_val, &result_abi_align))) {
struct_type->data.structure.resolve_status = ResolveStatusInvalid;
return err;
}
field->align = result_abi_align;
}
if (field->align > struct_type->abi_align) {

View File

@ -247,4 +247,6 @@ ConstExprValue *analyze_const_value_allow_lazy(CodeGen *g, Scope *scope, AstNode
void resolve_llvm_types_fn(CodeGen *g, ZigFn *fn);
bool fn_is_async(ZigFn *fn);
Error type_val_resolve_is_opaque_type(CodeGen *g, ConstExprValue *type_val, bool *is_opaque_type);
#endif

View File

@ -22869,84 +22869,65 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct
AstNode *proto_node = instruction->base.source_node;
assert(proto_node->type == NodeTypeFnProto);
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type);
result->value.special = ConstValSpecialLazy;
LazyValueFnType *lazy_fn_type = allocate<LazyValueFnType>(1);
result->value.data.x_lazy = &lazy_fn_type->base;
lazy_fn_type->base.id = LazyValueIdFnType;
lazy_fn_type->base.exec = ira->new_irb.exec;
if (proto_node->data.fn_proto.auto_err_set) {
ir_add_error(ira, &instruction->base,
buf_sprintf("inferring error set of return type valid only for function definitions"));
return ira->codegen->invalid_instruction;
}
FnTypeId fn_type_id = {0};
init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length);
size_t param_count = proto_node->data.fn_proto.params.length;
lazy_fn_type->proto_node = proto_node;
lazy_fn_type->param_types = allocate<ConstExprValue *>(param_count);
for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) {
AstNode *param_node = proto_node->data.fn_proto.params.at(fn_type_id.next_param_index);
for (size_t param_index = 0; param_index < param_count; param_index += 1) {
AstNode *param_node = proto_node->data.fn_proto.params.at(param_index);
assert(param_node->type == NodeTypeParamDecl);
bool param_is_var_args = param_node->data.param_decl.is_var_args;
if (param_is_var_args) {
if (fn_type_id.cc == CallingConventionC) {
fn_type_id.param_count = fn_type_id.next_param_index;
continue;
} else if (fn_type_id.cc == CallingConventionUnspecified) {
return ir_const_type(ira, &instruction->base, get_generic_fn_type(ira->codegen, &fn_type_id));
if (proto_node->data.fn_proto.cc == CallingConventionC) {
break;
} else if (proto_node->data.fn_proto.cc == CallingConventionUnspecified) {
lazy_fn_type->is_generic = true;
return result;
} else {
zig_unreachable();
}
}
FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];
param_info->is_noalias = param_node->data.param_decl.is_noalias;
if (instruction->param_types[fn_type_id.next_param_index] == nullptr) {
param_info->type = nullptr;
return ir_const_type(ira, &instruction->base, get_generic_fn_type(ira->codegen, &fn_type_id));
} else {
IrInstruction *param_type_value = instruction->param_types[fn_type_id.next_param_index]->child;
ZigType *param_type = ir_resolve_type(ira, param_type_value);
if (type_is_invalid(param_type))
return ira->codegen->invalid_instruction;
switch (type_requires_comptime(ira->codegen, param_type, nullptr)) {
case ReqCompTimeYes:
if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
ir_add_error(ira, param_type_value,
buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'",
buf_ptr(&param_type->name), calling_convention_name(fn_type_id.cc)));
return ira->codegen->invalid_instruction;
}
param_info->type = param_type;
fn_type_id.next_param_index += 1;
return ir_const_type(ira, &instruction->base, get_generic_fn_type(ira->codegen, &fn_type_id));
case ReqCompTimeInvalid:
return ira->codegen->invalid_instruction;
case ReqCompTimeNo:
break;
}
if (!type_has_bits(param_type) && !calling_convention_allows_zig_types(fn_type_id.cc)) {
ir_add_error(ira, param_type_value,
buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'",
buf_ptr(&param_type->name), calling_convention_name(fn_type_id.cc)));
return ira->codegen->invalid_instruction;
}
param_info->type = param_type;
if (instruction->param_types[param_index] == nullptr) {
lazy_fn_type->is_generic = true;
return result;
}
IrInstruction *param_type_value = instruction->param_types[param_index]->child;
if (type_is_invalid(param_type_value->value.type))
return ira->codegen->invalid_instruction;
ConstExprValue *param_type_val = ir_resolve_const(ira, param_type_value, LazyOk);
if (param_type_val == nullptr)
return ira->codegen->invalid_instruction;
lazy_fn_type->param_types[param_index] = param_type_val;
}
if (instruction->align_value != nullptr) {
if (!ir_resolve_align(ira, instruction->align_value->child, &fn_type_id.alignment))
lazy_fn_type->align_val = ir_resolve_const(ira, instruction->align_value->child, LazyOk);
if (lazy_fn_type->align_val == nullptr)
return ira->codegen->invalid_instruction;
}
IrInstruction *return_type_value = instruction->return_type->child;
fn_type_id.return_type = ir_resolve_type(ira, return_type_value);
if (type_is_invalid(fn_type_id.return_type))
return ira->codegen->invalid_instruction;
if (fn_type_id.return_type->id == ZigTypeIdOpaque) {
ir_add_error(ira, instruction->return_type,
buf_sprintf("return type cannot be opaque"));
return ira->codegen->invalid_instruction;
}
lazy_fn_type->return_type = ir_resolve_const(ira, instruction->return_type->child, LazyOk);
if (lazy_fn_type->return_type == nullptr)
return ira->codegen->invalid_instruction;
return ir_const_type(ira, &instruction->base, get_fn_type(ira->codegen, &fn_type_id));
return result;
}
static IrInstruction *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrInstructionTestComptime *instruction) {
@ -25492,6 +25473,82 @@ bool ir_has_side_effects(IrInstruction *instruction) {
zig_unreachable();
}
static ZigType *ir_resolve_lazy_fn_type(CodeGen *codegen, IrExecutable *exec, AstNode *source_node,
LazyValueFnType *lazy_fn_type)
{
AstNode *proto_node = lazy_fn_type->proto_node;
FnTypeId fn_type_id = {0};
init_fn_type_id(&fn_type_id, proto_node, proto_node->data.fn_proto.params.length);
for (; fn_type_id.next_param_index < fn_type_id.param_count; fn_type_id.next_param_index += 1) {
AstNode *param_node = proto_node->data.fn_proto.params.at(fn_type_id.next_param_index);
assert(param_node->type == NodeTypeParamDecl);
bool param_is_var_args = param_node->data.param_decl.is_var_args;
if (param_is_var_args) {
if (fn_type_id.cc == CallingConventionC) {
fn_type_id.param_count = fn_type_id.next_param_index;
continue;
} else if (fn_type_id.cc == CallingConventionUnspecified) {
return get_generic_fn_type(codegen, &fn_type_id);
} else {
zig_unreachable();
}
}
FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];
param_info->is_noalias = param_node->data.param_decl.is_noalias;
if (lazy_fn_type->param_types[fn_type_id.next_param_index] == nullptr) {
param_info->type = nullptr;
return get_generic_fn_type(codegen, &fn_type_id);
} else {
ZigType *param_type = ir_resolve_const_type(codegen, exec, source_node,
lazy_fn_type->param_types[fn_type_id.next_param_index]);
if (type_is_invalid(param_type))
return nullptr;
switch (type_requires_comptime(codegen, param_type, nullptr)) {
case ReqCompTimeYes:
if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
exec_add_error_node(codegen, exec, source_node,
buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'",
buf_ptr(&param_type->name), calling_convention_name(fn_type_id.cc)));
return nullptr;
}
param_info->type = param_type;
fn_type_id.next_param_index += 1;
return get_generic_fn_type(codegen, &fn_type_id);
case ReqCompTimeInvalid:
return nullptr;
case ReqCompTimeNo:
break;
}
if (!type_has_bits(param_type) && !calling_convention_allows_zig_types(fn_type_id.cc)) {
exec_add_error_node(codegen, exec, source_node,
buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'",
buf_ptr(&param_type->name), calling_convention_name(fn_type_id.cc)));
return nullptr;
}
param_info->type = param_type;
}
}
if (lazy_fn_type->align_val != nullptr) {
if (!ir_resolve_const_align(codegen, exec, source_node, lazy_fn_type->align_val, &fn_type_id.alignment))
return nullptr;
}
fn_type_id.return_type = ir_resolve_const_type(codegen, exec, source_node, lazy_fn_type->return_type);
if (type_is_invalid(fn_type_id.return_type))
return nullptr;
if (fn_type_id.return_type->id == ZigTypeIdOpaque) {
exec_add_error_node(codegen, exec, source_node, buf_create_from_str("return type cannot be opaque"));
return nullptr;
}
return get_fn_type(codegen, &fn_type_id);
}
static Error ir_resolve_lazy_raw(CodeGen *codegen, AstNode *source_node, ConstExprValue *val) {
Error err;
if (val->special != ConstValSpecialLazy)
@ -25551,6 +25608,16 @@ static Error ir_resolve_lazy_raw(CodeGen *codegen, AstNode *source_node, ConstEx
val->special = ConstValSpecialStatic;
return ErrorNone;
}
case LazyValueIdFnType: {
ZigType *fn_type = ir_resolve_lazy_fn_type(codegen, exec, source_node,
reinterpret_cast<LazyValueFnType *>(val->data.x_lazy));
if (fn_type == nullptr)
return ErrorSemanticAnalyzeFail;
val->special = ConstValSpecialStatic;
assert(val->type->id == ZigTypeIdMetaType);
val->data.x_type = fn_type;
return ErrorNone;
}
}
zig_unreachable();
}