diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 401722148..f43d23c1c 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -425,9 +425,9 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { TypeProc *pt = &proc_type->Proc; - bool is_foreign = (pl->tags & ProcTag_foreign) != 0; + bool is_foreign = e->Procedure.is_foreign; + bool is_export = (e->flags & EntityFlag_ForeignExport) != 0; bool is_link_name = (pl->tags & ProcTag_link_name) != 0; - bool is_export = (pl->tags & ProcTag_export) != 0; bool is_inline = (pl->tags & ProcTag_inline) != 0; bool is_no_inline = (pl->tags & ProcTag_no_inline) != 0; bool is_require_results = (pl->tags & ProcTag_require_results) != 0; @@ -489,7 +489,11 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pl->body, pl->tags); } } else if (!is_foreign) { - error(e->token, "Only a foreign procedure cannot have a body"); + if (e->flags & EntityFlag_ForeignExport) { + error(e->token, "Foreign export procedures must have a body"); + } else { + error(e->token, "Only a foreign procedure cannot have a body"); + } } if (pt->result_count == 0 && is_require_results) { diff --git a/src/checker.cpp b/src/checker.cpp index 1d5dee576..fbdd299a1 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -423,6 +423,7 @@ struct CheckerContext { DeclInfo * curr_proc_decl; AstNode * curr_foreign_library; + bool in_foreign_export; bool collect_delayed_decls; bool allow_polymorphic_types; bool no_polymorphic_errors; @@ -1481,13 +1482,13 @@ PtrSet generate_minimum_dependency_set(CheckerInfo *info, Entity *star add_dependency_to_map(&map, info, e); } } else if (e->kind == Entity_Procedure) { - if ((e->Procedure.tags & ProcTag_export) != 0) { - add_dependency_to_map(&map, info, e); - } if (e->Procedure.is_foreign) { add_dependency_to_map(&map, info, e->Procedure.foreign_library); } } + if (e->flags & EntityFlag_ForeignExport) { + add_dependency_to_map(&map, info, e); + } } add_dependency_to_map(&map, info, start); @@ -1969,6 +1970,8 @@ void check_collect_value_decl(Checker *c, AstNode *decl) { GB_ASSERT(fl->kind == AstNode_Ident); e->Variable.is_foreign = true; e->Variable.foreign_library_ident = fl; + } else if (c->context.in_foreign_export) { + e->flags |= EntityFlag_ForeignExport; } entities[entity_count++] = e; @@ -2027,7 +2030,9 @@ void check_collect_value_decl(Checker *c, AstNode *decl) { if (fl != nullptr) { GB_ASSERT(fl->kind == AstNode_Ident); e->Procedure.foreign_library_ident = fl; - pl->tags |= ProcTag_foreign; + e->Procedure.is_foreign = true; + } else if (c->context.in_foreign_export) { + e->flags |= EntityFlag_ForeignExport; } d->proc_lit = init; d->type_expr = pl->type; @@ -2038,13 +2043,14 @@ void check_collect_value_decl(Checker *c, AstNode *decl) { } e->identifier = name; - if (fl != nullptr && e->kind != Entity_Procedure) { - AstNodeKind kind = init->kind; - error(name, "Only procedures and variables are allowed to be in a foreign block, got %.*s", LIT(ast_node_strings[kind])); - if (kind == AstNode_ProcType) { - gb_printf_err("\tDid you forget to append `---` to the procedure?\n"); + if (e->kind != Entity_Procedure) { + if (fl != nullptr || c->context.in_foreign_export) { + AstNodeKind kind = init->kind; + error(name, "Only procedures and variables are allowed to be in a foreign block, got %.*s", LIT(ast_node_strings[kind])); + if (kind == AstNode_ProcType) { + gb_printf_err("\tDid you forget to append `---` to the procedure?\n"); + } } - // continue; } add_entity_and_decl_info(c, name, e, d); @@ -2061,13 +2067,18 @@ void check_add_foreign_block_decl(Checker *c, AstNode *decl) { fb->been_handled = true; AstNode *foreign_library = fb->foreign_library; - if (foreign_library->kind != AstNode_Ident) { - error(foreign_library, "foreign library name must be an identifier"); - foreign_library = nullptr; - } + CheckerContext prev_context = c->context; - c->context.curr_foreign_library = foreign_library; + if (foreign_library->kind == AstNode_Ident) { + c->context.curr_foreign_library = foreign_library; + } else if (foreign_library->kind == AstNode_Implicit && foreign_library->Implicit.kind == Token_export) { + c->context.in_foreign_export = true; + } else { + error(foreign_library, "Foreign block name must be an identifier or `export`"); + c->context.curr_foreign_library = nullptr; + } + c->context.collect_delayed_decls = true; check_collect_entities(c, fb->decls); c->context = prev_context; diff --git a/src/entity.cpp b/src/entity.cpp index b4ffc300b..68985e051 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -46,6 +46,7 @@ enum EntityFlag { EntityFlag_CVarArg = 1<<20, + EntityFlag_ForeignExport = 1<<23, }; // Zero value means the overloading process is not yet done diff --git a/src/ir.cpp b/src/ir.cpp index 816a3fcf9..4a6807586 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -117,6 +117,8 @@ struct irProcedure { AstNode * type_expr; AstNode * body; u64 tags; + bool is_foreign; + bool is_export; irValue * return_ptr; Array params; @@ -357,6 +359,7 @@ struct irValueGlobal { irValue * value; Array referrers; bool is_constant; + bool is_export; bool is_private; bool is_thread_local; bool is_foreign; @@ -5976,7 +5979,7 @@ void ir_build_constant_value_decl(irProcedure *proc, AstNodeValueDecl *vd) { ir_module_add_value(proc->module, e, value); ir_build_proc(value, proc); - if (value->Proc.tags & ProcTag_foreign) { + if (value->Proc.is_foreign || value->Proc.is_export) { HashKey key = hash_string(name); irValue **prev_value = map_get(&proc->module->members, key); if (prev_value == nullptr) { @@ -7349,6 +7352,14 @@ void ir_build_proc(irValue *value, irProcedure *parent) { Entity *e = proc->entity; String filename = e->token.pos.file; AstFile *f = ast_file_of_filename(info, filename); + + if (e->flags & EntityFlag_ForeignExport) { + proc->is_export = true; + } + if (e->Procedure.is_foreign) { + proc->is_foreign = true; + } + irDebugInfo *di_file = nullptr; irDebugInfo **di_file_found = map_get(&m->debug_info, hash_ast_file(f)); @@ -8109,7 +8120,7 @@ void ir_gen_tree(irGen *s) { GB_ASSERT(e == entry_point); // entry_point = e; } - if ((e->Procedure.tags & ProcTag_export) != 0 || + if ((e->flags & EntityFlag_ForeignExport) != 0 || (e->Procedure.link_name.len > 0) || (e->scope->is_file && e->Procedure.link_name.len > 0)) { if (!has_dll_main && name == "DllMain") { @@ -8162,12 +8173,14 @@ void ir_gen_tree(irGen *s) { irValue *g = ir_value_global(a, e, nullptr); g->Global.name = name; g->Global.is_thread_local = e->Variable.is_thread_local; - + g->Global.is_export = (e->flags & EntityFlag_ForeignExport) != 0; + g->Global.is_foreign = e->Variable.is_foreign; irGlobalVariable var = {}; var.var = g; var.decl = decl; + if (e->type->kind == Type_Struct && e->type->Struct.has_proc_default_values) { for_array(i, e->type->Struct.fields) { Entity *f = e->type->Struct.fields[i]; @@ -8235,7 +8248,7 @@ void ir_gen_tree(irGen *s) { String original_name = name; if (!scope->is_global || polymorphic_struct || is_type_polymorphic(e->type)) { - if (e->kind == Entity_Procedure && (e->Procedure.tags & ProcTag_export) != 0) { + if (e->kind == Entity_Procedure && (e->flags & EntityFlag_ForeignExport) != 0) { } else if (e->kind == Entity_Procedure && e->Procedure.link_name.len > 0) { // Handle later // } else if (scope->is_init && e->kind == Entity_Procedure && name == "main") { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index df1843af1..13dc87a57 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -801,7 +801,7 @@ bool ir_print_is_proc_global(irModule *m, irProcedure *proc) { return true; } } - return (proc->tags & (ProcTag_foreign|ProcTag_export)) != 0; + return proc->is_foreign || proc->is_export; } void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hint) { @@ -1673,8 +1673,7 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) { ir_write_byte(f, '\n'); ir_write_string(f, "define "); if (build_context.is_dll) { - // if (proc->tags & (ProcTag_export|ProcTag_dll_export)) { - if (proc->tags & (ProcTag_export)) { + if (proc->is_export) { ir_write_string(f, "dllexport "); } } @@ -1924,6 +1923,11 @@ void print_llvm_ir(irGen *ir) { if (g->is_foreign) { ir_write_string(f, str_lit("external ")); } + if (build_context.is_dll) { + if (g->is_export) { + ir_write_string(f, str_lit("dllexport ")); + } + } if (g->is_thread_local) { ir_write_string(f, str_lit("thread_local ")); } diff --git a/src/parser.cpp b/src/parser.cpp index 7ffa7828e..d1b9ff8d5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -95,11 +95,9 @@ enum ProcTag { ProcTag_require_results = 1<<4, - ProcTag_foreign = 1<<10, - ProcTag_export = 1<<11, - ProcTag_link_name = 1<<12, - ProcTag_inline = 1<<13, - ProcTag_no_inline = 1<<14, + ProcTag_link_name = 1<<11, + ProcTag_inline = 1<<12, + ProcTag_no_inline = 1<<13, // ProcTag_dll_import = 1<<15, // ProcTag_dll_export = 1<<16, @@ -1933,12 +1931,7 @@ AstNode *parse_ident(AstFile *f) { AstNode *parse_tag_expr(AstFile *f, AstNode *expression) { Token token = expect_token(f, Token_Hash); - Token name = {}; - if (f->curr_token.kind == Token_export) { - name = expect_token(f, Token_export); - } else { - name = expect_token(f, Token_Ident); - } + Token name = expect_token(f, Token_Ident); return ast_tag_expr(f, token, name, expression); } @@ -2088,7 +2081,6 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *link_name, ProcCallingConven } } ELSE_IF_ADD_TAG(require_results) - ELSE_IF_ADD_TAG(export) ELSE_IF_ADD_TAG(bounds_check) ELSE_IF_ADD_TAG(no_bounds_check) ELSE_IF_ADD_TAG(inline) @@ -2131,7 +2123,7 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *link_name, ProcCallingConven } if (cc == ProcCC_Invalid) { - if ((*tags) & ProcTag_foreign || f->in_foreign_block) { + if (f->in_foreign_block) { cc = ProcCC_C; } else { cc = ProcCC_Odin; @@ -2142,10 +2134,6 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *link_name, ProcCallingConven *calling_convention = cc; } - if ((*tags & ProcTag_foreign) && (*tags & ProcTag_export)) { - syntax_error(f->curr_token, "A foreign procedure cannot have #export"); - } - if ((*tags & ProcTag_inline) && (*tags & ProcTag_no_inline)) { syntax_error(f->curr_token, "You cannot apply both #inline and #no_inline to a procedure"); } @@ -2153,10 +2141,6 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *link_name, ProcCallingConven if ((*tags & ProcTag_bounds_check) && (*tags & ProcTag_no_bounds_check)) { syntax_error(f->curr_token, "You cannot apply both #bounds_check and #no_bounds_check to a procedure"); } - - if (((*tags & ProcTag_bounds_check) || (*tags & ProcTag_no_bounds_check)) && (*tags & ProcTag_foreign)) { - syntax_error(f->curr_token, "You cannot apply both #bounds_check or #no_bounds_check to a procedure without a body"); - } } @@ -2309,9 +2293,6 @@ AstNode *parse_operand(AstFile *f, bool lhs) { if (allow_token(f, Token_Undef)) { return ast_proc_lit(f, type, nullptr, tags, link_name); } else if (f->curr_token.kind == Token_OpenBrace) { - if ((tags & ProcTag_foreign) != 0) { - syntax_error(token, "A procedure tagged as `#foreign` cannot have a body"); - } AstNode *curr_proc = f->curr_proc; AstNode *body = nullptr; f->curr_proc = type; @@ -2320,9 +2301,6 @@ AstNode *parse_operand(AstFile *f, bool lhs) { return ast_proc_lit(f, type, body, tags, link_name); } else if (allow_token(f, Token_do)) { - if ((tags & ProcTag_foreign) != 0) { - syntax_error(token, "A procedure tagged as `#foreign` cannot have a body"); - } AstNode *curr_proc = f->curr_proc; AstNode *body = nullptr; f->curr_proc = type; @@ -2332,9 +2310,6 @@ AstNode *parse_operand(AstFile *f, bool lhs) { return ast_proc_lit(f, type, body, tags, link_name); } - if ((tags & ProcTag_foreign) != 0) { - return ast_proc_lit(f, type, nullptr, tags, link_name); - } if (tags != 0) { // syntax_error(token, "A procedure type cannot have tags"); } @@ -3041,7 +3016,12 @@ void parse_foreign_block_decl(AstFile *f, Array *decls) { AstNode *parse_foreign_block(AstFile *f, Token token) { CommentGroup docs = f->lead_comment; - AstNode *foreign_library = parse_ident(f); + AstNode *foreign_library = nullptr; + if (f->curr_token.kind == Token_export) { + foreign_library = ast_implicit(f, expect_token(f, Token_export)); + } else { + foreign_library = parse_ident(f); + } Token open = {}; Token close = {}; Array decls = make_ast_node_array(f); @@ -3063,7 +3043,6 @@ AstNode *parse_foreign_block(AstFile *f, Token token) { close = expect_token(f, Token_CloseBrace); } - AstNode *decl = ast_foreign_block_decl(f, token, foreign_library, open, close, decls, docs); expect_semicolon(f, decl); return decl; @@ -4409,6 +4388,7 @@ AstNode *parse_foreign_decl(AstFile *f) { Token token = expect_token(f, Token_foreign); switch (f->curr_token.kind) { + case Token_export: case Token_Ident: return parse_foreign_block(f, token); diff --git a/src/ssa.cpp b/src/ssa.cpp index 82fbdb22f..2400d9eca 100644 --- a/src/ssa.cpp +++ b/src/ssa.cpp @@ -2431,7 +2431,7 @@ bool ssa_generate(Parser *parser, CheckerInfo *info) { if (e->scope->is_init && name == "main") { entry_point = e; } - if ((e->Procedure.tags & ProcTag_export) != 0 || + if ((e->flags & EntityFlag_ForeignExport) != 0 || (e->Procedure.link_name.len > 0) || (e->scope->is_file && e->Procedure.link_name.len > 0)) { if (!has_dll_main && name == "DllMain") { @@ -2464,7 +2464,7 @@ bool ssa_generate(Parser *parser, CheckerInfo *info) { } if (!scope->is_global) { - if (e->kind == Entity_Procedure && (e->Procedure.tags & ProcTag_export) != 0) { + if (e->kind == Entity_Procedure && (e->flags & EntityFlag_ForeignExport) != 0) { } else if (e->kind == Entity_Procedure && e->Procedure.link_name.len > 0) { // Handle later } else if (scope->is_init && e->kind == Entity_Procedure && name == "main") {