diff --git a/src/analyze.cpp b/src/analyze.cpp index 3bfa59906..2d7c5e433 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -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,69 +742,74 @@ 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) { - FnTableEntry *fn_table_entry = allocate(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->label_table.init(8); - fn_table_entry->member_of_struct = struct_type; + if (skip) { + return; + } - if (struct_type) { - buf_resize(&fn_table_entry->symbol_name, 0); - buf_appendf(&fn_table_entry->symbol_name, "%s_%s", - buf_ptr(&struct_type->name), - buf_ptr(proto_name)); - } else { - buf_init_from_buf(&fn_table_entry->symbol_name, proto_name); - } + FnTableEntry *fn_table_entry = allocate(1); + fn_table_entry->import_entry = import; + fn_table_entry->proto_node = proto_node; + 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; - g->fn_protos.append(fn_table_entry); + if (struct_type) { + buf_resize(&fn_table_entry->symbol_name, 0); + buf_appendf(&fn_table_entry->symbol_name, "%s_%s", + buf_ptr(&struct_type->name), + buf_ptr(proto_name)); + } else { + buf_init_from_buf(&fn_table_entry->symbol_name, proto_name); + } + + 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); + fn_table->put(proto_name, fn_table_entry); - if (!struct_type && - g->bootstrap_import && - import == g->root_import && buf_eql_str(proto_name, "main")) - { - g->bootstrap_import->fn_table.put(proto_name, fn_table_entry); - } + if (!struct_type && + g->bootstrap_import && + import == g->root_import && buf_eql_str(proto_name, "main")) + { + g->bootstrap_import->fn_table.put(proto_name, fn_table_entry); + } - resolve_function_proto(g, proto_node, fn_table_entry, import); + 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; + 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(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); - break; - } + // 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) { - 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); +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 preview_types_root(CodeGen *g, ImportTableEntry *import, AstNode *node) { +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_types(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; + + recursive_resolve_decl(g, decl_node->codegen_node->decl_node.import, decl_node); + + // 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(1); + node->codegen_node->decl_node.deps.init(1); } diff --git a/src/analyze.hpp b/src/analyze.hpp index 8e86ef00a..4f20bf966 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -178,6 +178,10 @@ struct CodeGen { HashMap link_table; HashMap import_table; HashMap builtin_fn_table; + HashMap primitive_type_table; + HashMap 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 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); diff --git a/src/codegen.cpp b/src/codegen.cpp index 66ab62bfc..87a3b3510 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -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); diff --git a/src/parser.cpp b/src/parser.cpp index a27f2d66d..31f9aadcd 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -457,6 +457,7 @@ struct ParseContext { ZigList *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(1); node->type = type; node->owner = pc->owner; + node->create_index = pc->next_create_index; + pc->next_create_index += 1; return node; } diff --git a/src/parser.hpp b/src/parser.hpp index 43d828165..e5e4dec36 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -80,6 +80,16 @@ struct AstNodeFnProto { ZigList 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 { diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 168fc3566..6711346a6 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -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, }