mirror of
https://github.com/ziglang/zig.git
synced 2026-02-14 05:20:34 +00:00
add lazy value for fn prototypes
this case now works:
```zig
const Node = struct {
field: fn (*Node) *Node,
};
```
This commit is contained in:
parent
3865b6ad8f
commit
20049caaba
@ -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 {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
173
src/ir.cpp
173
src/ir.cpp
@ -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(¶m_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(¶m_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(¶m_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(¶m_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();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user