order-independent declarations

code constructs and traverses a dependency graph in a deterministic
order.
This commit is contained in:
Andrew Kelley 2016-01-09 23:49:22 -07:00
parent 6d9119fcd9
commit 1fe1235e14
6 changed files with 504 additions and 202 deletions

View File

@ -14,6 +14,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
TypeTableEntry *expected_type, AstNode *node);
static TypeTableEntry *eval_const_expr(CodeGen *g, BlockContext *context,
AstNode *node, AstNodeNumberLiteral *out_number_literal);
static void collect_type_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *type_node, DeclNode *decl_node);
static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import,
BlockContext *context, TypeTableEntry *expected_type, AstNode *node);
static AstNode *first_executing_node(AstNode *node) {
switch (node->type) {
@ -438,6 +441,9 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node, ImportTableEntry
{
Buf *name = &node->data.type.primitive_name;
auto table_entry = import->type_table.maybe_get(name);
if (!table_entry) {
table_entry = g->primitive_type_table.maybe_get(name);
}
if (table_entry) {
type_node->entry = table_entry->value;
} else {
@ -716,10 +722,19 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE
}
}
static void preview_fn_def(CodeGen *g, ImportTableEntry *import, AstNode *node, TypeTableEntry *struct_type) {
assert(node->type == NodeTypeFnDef);
AstNode *proto_node = node->data.fn_def.fn_proto;
assert(proto_node->type == NodeTypeFnProto);
static void preview_fn_proto(CodeGen *g, ImportTableEntry *import,
AstNode *proto_node)
{
AstNode *fn_def_node = proto_node->data.fn_proto.fn_def_node;
AstNode *extern_node = proto_node->data.fn_proto.extern_node;
AstNode *struct_node = proto_node->data.fn_proto.struct_node;
TypeTableEntry *struct_type;
if (struct_node) {
struct_type = struct_node->codegen_node->data.struct_decl_node.type_entry;
} else {
struct_type = nullptr;
}
Buf *proto_name = &proto_node->data.fn_proto.name;
auto fn_table = struct_type ? &struct_type->data.structure.fn_table : &import->fn_table;
@ -727,34 +742,36 @@ static void preview_fn_def(CodeGen *g, ImportTableEntry *import, AstNode *node,
auto entry = fn_table->maybe_get(proto_name);
bool skip = false;
bool is_internal = (proto_node->data.fn_proto.visib_mod != VisibModExport);
bool is_c_compat = !is_internal || extern_node;
bool is_pub = (proto_node->data.fn_proto.visib_mod != VisibModPrivate);
if (entry) {
add_node_error(g, node,
add_node_error(g, proto_node,
buf_sprintf("redefinition of '%s'", buf_ptr(proto_name)));
alloc_codegen_node(node);
node->codegen_node->data.fn_def_node.skip = true;
proto_node->codegen_node->data.fn_proto_node.skip = true;
skip = true;
} else if (is_pub) {
auto entry = fn_table->maybe_get(proto_name);
if (entry) {
add_node_error(g, node,
buf_sprintf("redefinition of '%s'", buf_ptr(proto_name)));
alloc_codegen_node(node);
node->codegen_node->data.fn_def_node.skip = true;
add_node_error(g, proto_node, buf_sprintf("redefinition of '%s'", buf_ptr(proto_name)));
proto_node->codegen_node->data.fn_proto_node.skip = true;
skip = true;
}
}
if (proto_node->data.fn_proto.is_var_args) {
add_node_error(g, node,
if (!extern_node && proto_node->data.fn_proto.is_var_args) {
add_node_error(g, proto_node,
buf_sprintf("variadic arguments only allowed in extern functions"));
}
if (!skip) {
if (skip) {
return;
}
FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1);
fn_table_entry->import_entry = import;
fn_table_entry->proto_node = proto_node;
fn_table_entry->fn_def_node = node;
fn_table_entry->internal_linkage = is_internal;
fn_table_entry->calling_convention = is_internal ? LLVMFastCallConv : LLVMCCallConv;
fn_table_entry->fn_def_node = fn_def_node;
fn_table_entry->internal_linkage = !is_c_compat;
fn_table_entry->is_extern = extern_node;
fn_table_entry->calling_convention = is_c_compat ? LLVMCCallConv : LLVMFastCallConv;
fn_table_entry->label_table.init(8);
fn_table_entry->member_of_struct = struct_type;
@ -768,7 +785,10 @@ static void preview_fn_def(CodeGen *g, ImportTableEntry *import, AstNode *node,
}
g->fn_protos.append(fn_table_entry);
if (!extern_node) {
g->fn_defs.append(fn_table_entry);
}
fn_table->put(proto_name, fn_table_entry);
@ -782,14 +802,14 @@ static void preview_fn_def(CodeGen *g, ImportTableEntry *import, AstNode *node,
resolve_function_proto(g, proto_node, fn_table_entry, import);
alloc_codegen_node(proto_node);
proto_node->codegen_node->data.fn_proto_node.fn_table_entry = fn_table_entry;
preview_function_labels(g, node->data.fn_def.body, fn_table_entry);
if (fn_def_node) {
preview_function_labels(g, fn_def_node->data.fn_def.body, fn_table_entry);
}
}
static void preview_function_declarations(CodeGen *g, ImportTableEntry *import, AstNode *node) {
static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) {
switch (node->type) {
case NodeTypeExternBlock:
for (int i = 0; i < node->data.extern_block.directives->length; i += 1) {
@ -803,33 +823,9 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
buf_sprintf("invalid directive: '%s'", buf_ptr(name)));
}
}
for (int fn_decl_i = 0; fn_decl_i < node->data.extern_block.fn_decls.length; fn_decl_i += 1) {
AstNode *fn_decl = node->data.extern_block.fn_decls.at(fn_decl_i);
assert(fn_decl->type == NodeTypeFnDecl);
AstNode *fn_proto = fn_decl->data.fn_decl.fn_proto;
FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1);
fn_table_entry->proto_node = fn_proto;
fn_table_entry->is_extern = true;
fn_table_entry->calling_convention = LLVMCCallConv;
fn_table_entry->import_entry = import;
fn_table_entry->label_table.init(8);
buf_init_from_buf(&fn_table_entry->symbol_name, &fn_proto->data.fn_proto.name);
resolve_function_proto(g, fn_proto, fn_table_entry, import);
Buf *name = &fn_proto->data.fn_proto.name;
g->fn_protos.append(fn_table_entry);
import->fn_table.put(name, fn_table_entry);
alloc_codegen_node(fn_proto);
fn_proto->codegen_node->data.fn_proto_node.fn_table_entry = fn_table_entry;
}
break;
case NodeTypeFnDef:
preview_fn_def(g, import, node, nullptr);
case NodeTypeFnProto:
preview_fn_proto(g, import, node);
break;
case NodeTypeRootExportDecl:
if (import == g->root_import) {
@ -881,95 +877,22 @@ static void preview_function_declarations(CodeGen *g, ImportTableEntry *import,
resolve_struct_type(g, import, type_entry);
for (int i = 0; i < node->data.struct_decl.fns.length; i += 1) {
AstNode *fn_def_node = node->data.struct_decl.fns.at(i);
preview_fn_def(g, import, fn_def_node, type_entry);
// struct member fns will get resolved independently
break;
}
case NodeTypeVariableDeclaration:
{
VariableTableEntry *var = analyze_variable_declaration(g, import, import->block_context,
nullptr, node);
g->global_vars.append(var);
break;
}
case NodeTypeUse:
case NodeTypeVariableDeclaration:
// nothing to do here
break;
case NodeTypeDirective:
case NodeTypeParamDecl:
case NodeTypeFnProto:
case NodeTypeType:
case NodeTypeFnDecl:
case NodeTypeReturnExpr:
case NodeTypeRoot:
case NodeTypeBlock:
case NodeTypeBinOpExpr:
case NodeTypeFnCallExpr:
case NodeTypeArrayAccessExpr:
case NodeTypeSliceExpr:
case NodeTypeNumberLiteral:
case NodeTypeStringLiteral:
case NodeTypeCharLiteral:
case NodeTypeUnreachable:
case NodeTypeVoid:
case NodeTypeBoolLiteral:
case NodeTypeNullLiteral:
case NodeTypeSymbol:
case NodeTypeCastExpr:
case NodeTypePrefixOpExpr:
case NodeTypeIfBoolExpr:
case NodeTypeIfVarExpr:
case NodeTypeWhileExpr:
case NodeTypeLabel:
case NodeTypeGoto:
case NodeTypeBreak:
case NodeTypeContinue:
case NodeTypeAsmExpr:
case NodeTypeFieldAccessExpr:
case NodeTypeStructField:
case NodeTypeStructValueExpr:
case NodeTypeStructValueField:
case NodeTypeCompilerFnExpr:
case NodeTypeCompilerFnType:
zig_unreachable();
}
}
static void preview_types(CodeGen *g, ImportTableEntry *import, AstNode *node) {
switch (node->type) {
case NodeTypeStructDecl:
{
alloc_codegen_node(node);
StructDeclNode *struct_codegen = &node->codegen_node->data.struct_decl_node;
Buf *name = &node->data.struct_decl.name;
auto table_entry = import->type_table.maybe_get(name);
if (table_entry) {
struct_codegen->type_entry = table_entry->value;
add_node_error(g, node,
buf_sprintf("redefinition of '%s'", buf_ptr(name)));
} else {
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct);
entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(name));
entry->data.structure.decl_node = node;
entry->di_type = LLVMZigCreateReplaceableCompositeType(g->dbuilder,
LLVMZigTag_DW_structure_type(), buf_ptr(&node->data.struct_decl.name),
LLVMZigFileToScope(import->di_file), import->di_file, node->line + 1);
buf_init_from_buf(&entry->name, name);
// put off adding the debug type until we do the full struct body
// this type is incomplete until we do another pass
import->type_table.put(&entry->name, entry);
struct_codegen->type_entry = entry;
}
break;
}
case NodeTypeExternBlock:
case NodeTypeFnDef:
case NodeTypeRootExportDecl:
case NodeTypeUse:
case NodeTypeVariableDeclaration:
// nothing to do
break;
case NodeTypeDirective:
case NodeTypeParamDecl:
case NodeTypeFnProto:
case NodeTypeType:
case NodeTypeFnDecl:
case NodeTypeReturnExpr:
@ -2552,15 +2475,15 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNode *node) {
assert(node->type == NodeTypeFnDef);
if (node->codegen_node && node->codegen_node->data.fn_def_node.skip) {
AstNode *fn_proto_node = node->data.fn_def.fn_proto;
assert(fn_proto_node->type == NodeTypeFnProto);
if (fn_proto_node->codegen_node->data.fn_proto_node.skip) {
// we detected an error with this function definition which prevents us
// from further analyzing it.
return;
}
AstNode *fn_proto_node = node->data.fn_def.fn_proto;
assert(fn_proto_node->type == NodeTypeFnProto);
alloc_codegen_node(node);
BlockContext *context = new_block_context(node, import->block_context);
node->codegen_node->data.fn_def_node.block_context = context;
@ -2630,7 +2553,7 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo
}
}
static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import, AstNode *node) {
static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) {
switch (node->type) {
case NodeTypeFnDef:
analyze_top_level_fn_def(g, import, node);
@ -2733,12 +2656,8 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
break;
}
case NodeTypeVariableDeclaration:
{
VariableTableEntry *var = analyze_variable_declaration(g, import, import->block_context,
nullptr, node);
g->global_vars.append(var);
// handled in resolve phase
break;
}
case NodeTypeDirective:
case NodeTypeParamDecl:
case NodeTypeFnProto:
@ -2779,24 +2698,389 @@ static void analyze_top_level_declaration(CodeGen *g, ImportTableEntry *import,
}
}
static void find_function_declarations_root(CodeGen *g, ImportTableEntry *import, AstNode *node) {
static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *expr_node,
DeclNode *decl_node)
{
switch (expr_node->type) {
case NodeTypeNumberLiteral:
case NodeTypeStringLiteral:
case NodeTypeCharLiteral:
case NodeTypeVoid:
case NodeTypeBoolLiteral:
case NodeTypeNullLiteral:
case NodeTypeUnreachable:
case NodeTypeGoto:
case NodeTypeBreak:
case NodeTypeContinue:
// no dependencies on other top level declarations
break;
case NodeTypeSymbol:
decl_node->deps.put(&expr_node->data.symbol, expr_node);
break;
case NodeTypeBinOpExpr:
collect_expr_decl_deps(g, import, expr_node->data.bin_op_expr.op1, decl_node);
collect_expr_decl_deps(g, import, expr_node->data.bin_op_expr.op2, decl_node);
break;
case NodeTypeReturnExpr:
collect_expr_decl_deps(g, import, expr_node->data.return_expr.expr, decl_node);
break;
case NodeTypeCastExpr:
collect_expr_decl_deps(g, import, expr_node->data.cast_expr.expr, decl_node);
collect_type_decl_deps(g, import, expr_node->data.cast_expr.type, decl_node);
break;
case NodeTypePrefixOpExpr:
collect_expr_decl_deps(g, import, expr_node->data.prefix_op_expr.primary_expr, decl_node);
break;
case NodeTypeFnCallExpr:
collect_expr_decl_deps(g, import, expr_node->data.fn_call_expr.fn_ref_expr, decl_node);
for (int i = 0; i < expr_node->data.fn_call_expr.params.length; i += 1) {
AstNode *arg_node = expr_node->data.fn_call_expr.params.at(i);
collect_expr_decl_deps(g, import, arg_node, decl_node);
}
break;
case NodeTypeArrayAccessExpr:
collect_expr_decl_deps(g, import, expr_node->data.array_access_expr.array_ref_expr, decl_node);
collect_expr_decl_deps(g, import, expr_node->data.array_access_expr.subscript, decl_node);
break;
case NodeTypeSliceExpr:
collect_expr_decl_deps(g, import, expr_node->data.slice_expr.array_ref_expr, decl_node);
collect_expr_decl_deps(g, import, expr_node->data.slice_expr.start, decl_node);
if (expr_node->data.slice_expr.end) {
collect_expr_decl_deps(g, import, expr_node->data.slice_expr.end, decl_node);
}
break;
case NodeTypeFieldAccessExpr:
collect_expr_decl_deps(g, import, expr_node->data.field_access_expr.struct_expr, decl_node);
break;
case NodeTypeIfBoolExpr:
collect_expr_decl_deps(g, import, expr_node->data.if_bool_expr.condition, decl_node);
collect_expr_decl_deps(g, import, expr_node->data.if_bool_expr.then_block, decl_node);
if (expr_node->data.if_bool_expr.else_node) {
collect_expr_decl_deps(g, import, expr_node->data.if_bool_expr.else_node, decl_node);
}
break;
case NodeTypeIfVarExpr:
if (expr_node->data.if_var_expr.var_decl.type) {
collect_type_decl_deps(g, import, expr_node->data.if_var_expr.var_decl.type, decl_node);
}
if (expr_node->data.if_var_expr.var_decl.expr) {
collect_expr_decl_deps(g, import, expr_node->data.if_var_expr.var_decl.expr, decl_node);
}
collect_expr_decl_deps(g, import, expr_node->data.if_var_expr.then_block, decl_node);
if (expr_node->data.if_bool_expr.else_node) {
collect_expr_decl_deps(g, import, expr_node->data.if_var_expr.else_node, decl_node);
}
break;
case NodeTypeWhileExpr:
collect_expr_decl_deps(g, import, expr_node->data.while_expr.condition, decl_node);
collect_expr_decl_deps(g, import, expr_node->data.while_expr.body, decl_node);
break;
case NodeTypeBlock:
for (int i = 0; i < expr_node->data.block.statements.length; i += 1) {
AstNode *stmt = expr_node->data.block.statements.at(i);
collect_expr_decl_deps(g, import, stmt, decl_node);
}
break;
case NodeTypeAsmExpr:
for (int i = 0; i < expr_node->data.asm_expr.output_list.length; i += 1) {
AsmOutput *asm_output = expr_node->data.asm_expr.output_list.at(i);
if (asm_output->return_type) {
collect_type_decl_deps(g, import, asm_output->return_type, decl_node);
} else {
decl_node->deps.put(&asm_output->variable_name, expr_node);
}
}
for (int i = 0; i < expr_node->data.asm_expr.input_list.length; i += 1) {
AsmInput *asm_input = expr_node->data.asm_expr.input_list.at(i);
collect_expr_decl_deps(g, import, asm_input->expr, decl_node);
}
break;
case NodeTypeStructValueExpr:
collect_type_decl_deps(g, import, expr_node->data.struct_val_expr.type, decl_node);
for (int i = 0; i < expr_node->data.struct_val_expr.fields.length; i += 1) {
AstNode *field_node = expr_node->data.struct_val_expr.fields.at(i);
assert(field_node->type == NodeTypeStructValueField);
collect_expr_decl_deps(g, import, field_node->data.struct_val_field.expr, decl_node);
}
break;
case NodeTypeCompilerFnExpr:
collect_expr_decl_deps(g, import, expr_node->data.compiler_fn_expr.expr, decl_node);
break;
case NodeTypeCompilerFnType:
collect_type_decl_deps(g, import, expr_node->data.compiler_fn_type.type, decl_node);
break;
case NodeTypeRoot:
case NodeTypeRootExportDecl:
case NodeTypeFnProto:
case NodeTypeFnDef:
case NodeTypeFnDecl:
case NodeTypeParamDecl:
case NodeTypeType:
case NodeTypeExternBlock:
case NodeTypeDirective:
case NodeTypeVariableDeclaration:
case NodeTypeUse:
case NodeTypeLabel:
case NodeTypeStructDecl:
case NodeTypeStructField:
case NodeTypeStructValueField:
zig_unreachable();
}
}
static void collect_type_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *type_node, DeclNode *decl_node) {
assert(type_node->type == NodeTypeType);
switch (type_node->data.type.type) {
case AstNodeTypeTypePrimitive:
{
Buf *name = &type_node->data.type.primitive_name;
auto table_entry = g->primitive_type_table.maybe_get(name);
if (!table_entry) {
table_entry = import->type_table.maybe_get(name);
}
if (!table_entry) {
decl_node->deps.put(name, type_node);
}
break;
}
case AstNodeTypeTypePointer:
collect_type_decl_deps(g, import, type_node->data.type.child_type, decl_node);
break;
case AstNodeTypeTypeArray:
collect_type_decl_deps(g, import, type_node->data.type.child_type, decl_node);
if (type_node->data.type.array_size) {
collect_expr_decl_deps(g, import, type_node->data.type.array_size, decl_node);
}
break;
case AstNodeTypeTypeMaybe:
collect_type_decl_deps(g, import, type_node->data.type.child_type, decl_node);
break;
case AstNodeTypeTypeCompilerExpr:
collect_expr_decl_deps(g, import, type_node->data.type.compiler_expr, decl_node);
break;
}
}
static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode *node) {
switch (node->type) {
case NodeTypeStructDecl:
{
alloc_codegen_node(node);
StructDeclNode *struct_codegen = &node->codegen_node->data.struct_decl_node;
Buf *name = &node->data.struct_decl.name;
auto table_entry = import->type_table.maybe_get(name);
if (!table_entry) {
table_entry = g->primitive_type_table.maybe_get(name);
}
if (table_entry) {
struct_codegen->type_entry = table_entry->value;
add_node_error(g, node,
buf_sprintf("redefinition of '%s'", buf_ptr(name)));
} else {
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdStruct);
entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), buf_ptr(name));
entry->data.structure.decl_node = node;
entry->di_type = LLVMZigCreateReplaceableCompositeType(g->dbuilder,
LLVMZigTag_DW_structure_type(), buf_ptr(&node->data.struct_decl.name),
LLVMZigFileToScope(import->di_file), import->di_file, node->line + 1);
buf_init_from_buf(&entry->name, name);
// put off adding the debug type until we do the full struct body
// this type is incomplete until we do another pass
import->type_table.put(&entry->name, entry);
struct_codegen->type_entry = entry;
}
// determine which other top level declarations this struct depends on.
DeclNode *decl_node = &node->codegen_node->decl_node;
for (int i = 0; i < node->data.struct_decl.fields.length; i += 1) {
AstNode *field_node = node->data.struct_decl.fields.at(i);
AstNode *type_node = field_node->data.struct_field.type;
collect_type_decl_deps(g, import, type_node, decl_node);
}
node->codegen_node->decl_node.name = name;
node->codegen_node->decl_node.import = import;
if (decl_node->deps.size() > 0) {
g->unresolved_top_level_decls.put(name, node);
} else {
resolve_top_level_decl(g, import, node);
}
// handle the member function definitions independently
for (int i = 0; i < node->data.struct_decl.fns.length; i += 1) {
AstNode *fn_def_node = node->data.struct_decl.fns.at(i);
AstNode *fn_proto_node = fn_def_node->data.fn_def.fn_proto;
fn_proto_node->data.fn_proto.struct_node = node;
detect_top_level_decl_deps(g, import, fn_def_node);
}
break;
}
case NodeTypeExternBlock:
for (int fn_decl_i = 0; fn_decl_i < node->data.extern_block.fn_decls.length; fn_decl_i += 1) {
AstNode *fn_decl = node->data.extern_block.fn_decls.at(fn_decl_i);
assert(fn_decl->type == NodeTypeFnDecl);
AstNode *fn_proto = fn_decl->data.fn_decl.fn_proto;
fn_proto->data.fn_proto.extern_node = node;
detect_top_level_decl_deps(g, import, fn_proto);
}
resolve_top_level_decl(g, import, node);
break;
case NodeTypeFnDef:
node->data.fn_def.fn_proto->data.fn_proto.fn_def_node = node;
detect_top_level_decl_deps(g, import, node->data.fn_def.fn_proto);
break;
case NodeTypeVariableDeclaration:
{
// determine which other top level declarations this variable declaration depends on.
alloc_codegen_node(node);
DeclNode *decl_node = &node->codegen_node->decl_node;
if (node->data.variable_declaration.type) {
collect_type_decl_deps(g, import, node->data.variable_declaration.type, decl_node);
}
if (node->data.variable_declaration.expr) {
collect_expr_decl_deps(g, import, node->data.variable_declaration.expr, decl_node);
}
Buf *name = &node->data.variable_declaration.symbol;
node->codegen_node->decl_node.name = name;
node->codegen_node->decl_node.import = import;
if (decl_node->deps.size() > 0) {
g->unresolved_top_level_decls.put(name, node);
} else {
resolve_top_level_decl(g, import, node);
}
break;
}
case NodeTypeFnProto:
{
// determine which other top level declarations this function prototype depends on.
alloc_codegen_node(node);
DeclNode *decl_node = &node->codegen_node->decl_node;
for (int i = 0; i < node->data.fn_proto.params.length; i += 1) {
AstNode *param_node = node->data.fn_proto.params.at(i);
assert(param_node->type == NodeTypeParamDecl);
collect_type_decl_deps(g, import, param_node->data.param_decl.type, decl_node);
}
Buf *name = &node->data.fn_proto.name;
node->codegen_node->decl_node.name = name;
node->codegen_node->decl_node.import = import;
if (decl_node->deps.size() > 0) {
g->unresolved_top_level_decls.put(name, node);
} else {
resolve_top_level_decl(g, import, node);
}
break;
}
case NodeTypeRootExportDecl:
resolve_top_level_decl(g, import, node);
break;
case NodeTypeUse:
// nothing to do
break;
case NodeTypeDirective:
case NodeTypeParamDecl:
case NodeTypeType:
case NodeTypeFnDecl:
case NodeTypeReturnExpr:
case NodeTypeRoot:
case NodeTypeBlock:
case NodeTypeBinOpExpr:
case NodeTypeFnCallExpr:
case NodeTypeArrayAccessExpr:
case NodeTypeSliceExpr:
case NodeTypeNumberLiteral:
case NodeTypeStringLiteral:
case NodeTypeCharLiteral:
case NodeTypeUnreachable:
case NodeTypeVoid:
case NodeTypeBoolLiteral:
case NodeTypeNullLiteral:
case NodeTypeSymbol:
case NodeTypeCastExpr:
case NodeTypePrefixOpExpr:
case NodeTypeIfBoolExpr:
case NodeTypeIfVarExpr:
case NodeTypeWhileExpr:
case NodeTypeLabel:
case NodeTypeGoto:
case NodeTypeBreak:
case NodeTypeContinue:
case NodeTypeAsmExpr:
case NodeTypeFieldAccessExpr:
case NodeTypeStructField:
case NodeTypeStructValueExpr:
case NodeTypeStructValueField:
case NodeTypeCompilerFnExpr:
case NodeTypeCompilerFnType:
zig_unreachable();
}
}
static void recursive_resolve_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) {
auto it = node->codegen_node->decl_node.deps.entry_iterator();
for (;;) {
auto *entry = it.next();
if (!entry)
break;
auto unresolved_entry = g->unresolved_top_level_decls.maybe_get(entry->key);
if (!unresolved_entry) {
continue;
}
AstNode *child_node = unresolved_entry->value;
if (child_node->codegen_node->decl_node.in_current_deps) {
zig_panic("TODO infinite top level decl loop");
}
// set temporary flag
child_node->codegen_node->decl_node.in_current_deps = true;
recursive_resolve_decl(g, child_node->codegen_node->decl_node.import, child_node);
// unset temporary flag
child_node->codegen_node->decl_node.in_current_deps = false;
}
resolve_top_level_decl(g, import, node);
g->unresolved_top_level_decls.remove(node->codegen_node->decl_node.name);
}
static void resolve_top_level_declarations_root(CodeGen *g, ImportTableEntry *import, AstNode *node) {
assert(node->type == NodeTypeRoot);
for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) {
AstNode *child = node->data.root.top_level_decls.at(i);
preview_function_declarations(g, import, child);
detect_top_level_decl_deps(g, import, child);
}
while (g->unresolved_top_level_decls.size() > 0) {
// for the sake of determinism, find the element with the lowest
// insert index and resolve that one.
AstNode *decl_node = nullptr;
auto it = g->unresolved_top_level_decls.entry_iterator();
for (;;) {
auto *entry = it.next();
if (!entry)
break;
AstNode *this_node = entry->value;
if (!decl_node || this_node->create_index < decl_node->create_index) {
decl_node = this_node;
}
}
// set temporary flag
decl_node->codegen_node->decl_node.in_current_deps = true;
static void preview_types_root(CodeGen *g, ImportTableEntry *import, AstNode *node) {
assert(node->type == NodeTypeRoot);
recursive_resolve_decl(g, decl_node->codegen_node->decl_node.import, decl_node);
for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) {
AstNode *child = node->data.root.top_level_decls.at(i);
preview_types(g, import, child);
// unset temporary flag
decl_node->codegen_node->decl_node.in_current_deps = false;
}
}
static void analyze_top_level_decls_root(CodeGen *g, ImportTableEntry *import, AstNode *node) {
@ -2804,7 +3088,7 @@ static void analyze_top_level_decls_root(CodeGen *g, ImportTableEntry *import, A
for (int i = 0; i < node->data.root.top_level_decls.length; i += 1) {
AstNode *child = node->data.root.top_level_decls.at(i);
analyze_top_level_declaration(g, import, child);
analyze_top_level_decl(g, import, child);
}
}
@ -2817,21 +3101,9 @@ void semantic_analyze(CodeGen *g) {
break;
ImportTableEntry *import = entry->value;
preview_types_root(g, import, import->root);
resolve_top_level_declarations_root(g, import, import->root);
}
}
{
auto it = g->import_table.entry_iterator();
for (;;) {
auto *entry = it.next();
if (!entry)
break;
ImportTableEntry *import = entry->value;
find_function_declarations_root(g, import, import->root);
}
}
{
auto it = g->import_table.entry_iterator();
for (;;) {
@ -2844,7 +3116,6 @@ void semantic_analyze(CodeGen *g) {
}
}
if (!g->root_out_name) {
add_node_error(g, g->root_import->root,
buf_sprintf("missing export declaration and output name not provided"));
@ -2857,5 +3128,6 @@ void semantic_analyze(CodeGen *g) {
void alloc_codegen_node(AstNode *node) {
assert(!node->codegen_node);
node->codegen_node = allocate<CodeGenNode>(1);
node->codegen_node->decl_node.deps.init(1);
}

View File

@ -178,6 +178,10 @@ struct CodeGen {
HashMap<Buf *, bool, buf_hash, buf_eql_buf> link_table;
HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table;
HashMap<Buf *, BuiltinFnEntry *, buf_hash, buf_eql_buf> builtin_fn_table;
HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> primitive_type_table;
HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> unresolved_top_level_decls;
uint32_t next_unresolved_index;
struct {
TypeTableEntry *entry_bool;
@ -270,12 +274,12 @@ struct TypeNode {
struct FnProtoNode {
FnTableEntry *fn_table_entry;
bool skip;
};
struct FnDefNode {
TypeTableEntry *implicit_return_type;
BlockContext *block_context;
bool skip;
};
@ -364,6 +368,15 @@ struct FnCallNode {
BuiltinFnEntry *builtin_fn;
};
struct DeclNode {
HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> deps;
Buf *name;
ImportTableEntry *import;
// set this flag temporarily to detect infinite loops
bool in_current_deps;
};
// TODO get rid of this structure and put the data directly in the appropriate AST node
struct CodeGenNode {
union {
TypeNode type_node; // for NodeTypeType
@ -388,6 +401,7 @@ struct CodeGenNode {
FnCallNode fn_call_node; // for NodeTypeFnCallExpr
} data;
ExprNode expr_node; // for all the expression nodes
DeclNode decl_node; // for all top level decls
};
void semantic_analyze(CodeGen *g);

View File

@ -23,6 +23,8 @@ CodeGen *codegen_create(Buf *root_source_dir) {
g->link_table.init(32);
g->import_table.init(32);
g->builtin_fn_table.init(32);
g->primitive_type_table.init(32);
g->unresolved_top_level_decls.init(32);
g->build_type = CodeGenBuildTypeDebug;
g->root_source_dir = root_source_dir;
@ -2109,6 +2111,7 @@ static void define_builtin_types(CodeGen *g) {
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_unsigned());
g->builtin_types.entry_bool = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
@ -2120,6 +2123,7 @@ static void define_builtin_types(CodeGen *g) {
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_unsigned());
g->builtin_types.entry_u8 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
@ -2132,6 +2136,7 @@ static void define_builtin_types(CodeGen *g) {
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_unsigned());
g->builtin_types.entry_u16 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
@ -2144,6 +2149,7 @@ static void define_builtin_types(CodeGen *g) {
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_unsigned());
g->builtin_types.entry_u32 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
@ -2156,6 +2162,7 @@ static void define_builtin_types(CodeGen *g) {
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_unsigned());
g->builtin_types.entry_u64 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
g->builtin_types.entry_c_string_literal = get_pointer_to_type(g, g->builtin_types.entry_u8, true, false);
{
@ -2169,6 +2176,7 @@ static void define_builtin_types(CodeGen *g) {
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_signed());
g->builtin_types.entry_i8 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
@ -2181,6 +2189,7 @@ static void define_builtin_types(CodeGen *g) {
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_signed());
g->builtin_types.entry_i16 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
@ -2193,6 +2202,7 @@ static void define_builtin_types(CodeGen *g) {
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_signed());
g->builtin_types.entry_i32 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
@ -2205,6 +2215,7 @@ static void define_builtin_types(CodeGen *g) {
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_signed());
g->builtin_types.entry_i64 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
@ -2217,6 +2228,7 @@ static void define_builtin_types(CodeGen *g) {
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_signed());
g->builtin_types.entry_isize = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
@ -2229,6 +2241,7 @@ static void define_builtin_types(CodeGen *g) {
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_unsigned());
g->builtin_types.entry_usize = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
@ -2240,6 +2253,7 @@ static void define_builtin_types(CodeGen *g) {
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_float());
g->builtin_types.entry_f32 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
@ -2251,6 +2265,7 @@ static void define_builtin_types(CodeGen *g) {
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_float());
g->builtin_types.entry_f64 = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVoid);
@ -2260,6 +2275,7 @@ static void define_builtin_types(CodeGen *g) {
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_unsigned());
g->builtin_types.entry_void = entry;
g->primitive_type_table.put(&entry->name, entry);
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdUnreachable);
@ -2267,6 +2283,7 @@ static void define_builtin_types(CodeGen *g) {
buf_init_from_str(&entry->name, "unreachable");
entry->di_type = g->builtin_types.entry_void->di_type;
g->builtin_types.entry_unreachable = entry;
g->primitive_type_table.put(&entry->name, entry);
}
}
@ -2506,21 +2523,6 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path,
import_entry->path = full_path;
import_entry->fn_table.init(32);
import_entry->type_table.init(32);
import_entry->type_table.put(&g->builtin_types.entry_bool->name, g->builtin_types.entry_bool);
import_entry->type_table.put(&g->builtin_types.entry_u8->name, g->builtin_types.entry_u8);
import_entry->type_table.put(&g->builtin_types.entry_u16->name, g->builtin_types.entry_u16);
import_entry->type_table.put(&g->builtin_types.entry_u32->name, g->builtin_types.entry_u32);
import_entry->type_table.put(&g->builtin_types.entry_u64->name, g->builtin_types.entry_u64);
import_entry->type_table.put(&g->builtin_types.entry_i8->name, g->builtin_types.entry_i8);
import_entry->type_table.put(&g->builtin_types.entry_i16->name, g->builtin_types.entry_i16);
import_entry->type_table.put(&g->builtin_types.entry_i32->name, g->builtin_types.entry_i32);
import_entry->type_table.put(&g->builtin_types.entry_i64->name, g->builtin_types.entry_i64);
import_entry->type_table.put(&g->builtin_types.entry_isize->name, g->builtin_types.entry_isize);
import_entry->type_table.put(&g->builtin_types.entry_usize->name, g->builtin_types.entry_usize);
import_entry->type_table.put(&g->builtin_types.entry_f32->name, g->builtin_types.entry_f32);
import_entry->type_table.put(&g->builtin_types.entry_f64->name, g->builtin_types.entry_f64);
import_entry->type_table.put(&g->builtin_types.entry_void->name, g->builtin_types.entry_void);
import_entry->type_table.put(&g->builtin_types.entry_unreachable->name, g->builtin_types.entry_unreachable);
import_entry->root = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color);
assert(import_entry->root);

View File

@ -457,6 +457,7 @@ struct ParseContext {
ZigList<AstNode *> *directive_list;
ImportTableEntry *owner;
ErrColor err_color;
uint32_t next_create_index;
};
__attribute__ ((format (printf, 4, 5)))
@ -512,6 +513,8 @@ static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) {
AstNode *node = allocate<AstNode>(1);
node->type = type;
node->owner = pc->owner;
node->create_index = pc->next_create_index;
pc->next_create_index += 1;
return node;
}

View File

@ -80,6 +80,16 @@ struct AstNodeFnProto {
ZigList<AstNode *> params;
AstNode *return_type;
bool is_var_args;
// the extern block this fn proto is inside. can be null.
// populated by semantic analyzer.
AstNode *extern_node;
// the struct decl node this fn proto is inside. can be null.
// populated by semantic analyzer.
AstNode *struct_node;
// the function definition this fn proto is inside. can be null.
// populated by semantic analyzer.
AstNode *fn_def_node;
};
struct AstNodeFnDef {
@ -369,6 +379,7 @@ struct AstNode {
enum NodeType type;
int line;
int column;
uint32_t create_index; // for determinism purposes
CodeGenNode *codegen_node;
ImportTableEntry *owner;
union {

View File

@ -1203,7 +1203,7 @@ struct A { a : A, }
struct A { b : B, }
struct B { c : C, }
struct C { a : A, }
)SOURCE", 1, ".tmp_source.zig:2:1: error: struct has infinite size");
)SOURCE", 1, ".tmp_source.zig:4:1: error: struct has infinite size");
add_compile_fail_case("invalid struct field", R"SOURCE(
struct A { x : i32, }