foreign export block

```
foreign export {
    my_i32: i32;
    my_foo :: proc() -> i32 {
        return 123;
    }
}
```
This commit is contained in:
gingerBill
2017-10-15 16:05:42 +01:00
parent 85f7c2d040
commit 3d8bf36a30
7 changed files with 72 additions and 59 deletions
+7 -3
View File
@@ -425,9 +425,9 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
TypeProc *pt = &proc_type->Proc; 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_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_inline = (pl->tags & ProcTag_inline) != 0;
bool is_no_inline = (pl->tags & ProcTag_no_inline) != 0; bool is_no_inline = (pl->tags & ProcTag_no_inline) != 0;
bool is_require_results = (pl->tags & ProcTag_require_results) != 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); check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pl->body, pl->tags);
} }
} else if (!is_foreign) { } 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) { if (pt->result_count == 0 && is_require_results) {
+26 -15
View File
@@ -423,6 +423,7 @@ struct CheckerContext {
DeclInfo * curr_proc_decl; DeclInfo * curr_proc_decl;
AstNode * curr_foreign_library; AstNode * curr_foreign_library;
bool in_foreign_export;
bool collect_delayed_decls; bool collect_delayed_decls;
bool allow_polymorphic_types; bool allow_polymorphic_types;
bool no_polymorphic_errors; bool no_polymorphic_errors;
@@ -1481,13 +1482,13 @@ PtrSet<Entity *> generate_minimum_dependency_set(CheckerInfo *info, Entity *star
add_dependency_to_map(&map, info, e); add_dependency_to_map(&map, info, e);
} }
} else if (e->kind == Entity_Procedure) { } 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) { if (e->Procedure.is_foreign) {
add_dependency_to_map(&map, info, e->Procedure.foreign_library); 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); 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); GB_ASSERT(fl->kind == AstNode_Ident);
e->Variable.is_foreign = true; e->Variable.is_foreign = true;
e->Variable.foreign_library_ident = fl; e->Variable.foreign_library_ident = fl;
} else if (c->context.in_foreign_export) {
e->flags |= EntityFlag_ForeignExport;
} }
entities[entity_count++] = e; entities[entity_count++] = e;
@@ -2027,7 +2030,9 @@ void check_collect_value_decl(Checker *c, AstNode *decl) {
if (fl != nullptr) { if (fl != nullptr) {
GB_ASSERT(fl->kind == AstNode_Ident); GB_ASSERT(fl->kind == AstNode_Ident);
e->Procedure.foreign_library_ident = fl; 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->proc_lit = init;
d->type_expr = pl->type; d->type_expr = pl->type;
@@ -2038,13 +2043,14 @@ void check_collect_value_decl(Checker *c, AstNode *decl) {
} }
e->identifier = name; e->identifier = name;
if (fl != nullptr && e->kind != Entity_Procedure) { if (e->kind != Entity_Procedure) {
AstNodeKind kind = init->kind; if (fl != nullptr || c->context.in_foreign_export) {
error(name, "Only procedures and variables are allowed to be in a foreign block, got %.*s", LIT(ast_node_strings[kind])); AstNodeKind kind = init->kind;
if (kind == AstNode_ProcType) { error(name, "Only procedures and variables are allowed to be in a foreign block, got %.*s", LIT(ast_node_strings[kind]));
gb_printf_err("\tDid you forget to append `---` to the procedure?\n"); 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); 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; fb->been_handled = true;
AstNode *foreign_library = fb->foreign_library; 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; 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; c->context.collect_delayed_decls = true;
check_collect_entities(c, fb->decls); check_collect_entities(c, fb->decls);
c->context = prev_context; c->context = prev_context;
+1
View File
@@ -46,6 +46,7 @@ enum EntityFlag {
EntityFlag_CVarArg = 1<<20, EntityFlag_CVarArg = 1<<20,
EntityFlag_ForeignExport = 1<<23,
}; };
// Zero value means the overloading process is not yet done // Zero value means the overloading process is not yet done
+17 -4
View File
@@ -117,6 +117,8 @@ struct irProcedure {
AstNode * type_expr; AstNode * type_expr;
AstNode * body; AstNode * body;
u64 tags; u64 tags;
bool is_foreign;
bool is_export;
irValue * return_ptr; irValue * return_ptr;
Array<irValue *> params; Array<irValue *> params;
@@ -357,6 +359,7 @@ struct irValueGlobal {
irValue * value; irValue * value;
Array<irValue *> referrers; Array<irValue *> referrers;
bool is_constant; bool is_constant;
bool is_export;
bool is_private; bool is_private;
bool is_thread_local; bool is_thread_local;
bool is_foreign; 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_module_add_value(proc->module, e, value);
ir_build_proc(value, proc); 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); HashKey key = hash_string(name);
irValue **prev_value = map_get(&proc->module->members, key); irValue **prev_value = map_get(&proc->module->members, key);
if (prev_value == nullptr) { if (prev_value == nullptr) {
@@ -7349,6 +7352,14 @@ void ir_build_proc(irValue *value, irProcedure *parent) {
Entity *e = proc->entity; Entity *e = proc->entity;
String filename = e->token.pos.file; String filename = e->token.pos.file;
AstFile *f = ast_file_of_filename(info, filename); 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 = nullptr;
irDebugInfo **di_file_found = map_get(&m->debug_info, hash_ast_file(f)); 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); GB_ASSERT(e == entry_point);
// entry_point = e; // entry_point = e;
} }
if ((e->Procedure.tags & ProcTag_export) != 0 || if ((e->flags & EntityFlag_ForeignExport) != 0 ||
(e->Procedure.link_name.len > 0) || (e->Procedure.link_name.len > 0) ||
(e->scope->is_file && e->Procedure.link_name.len > 0)) { (e->scope->is_file && e->Procedure.link_name.len > 0)) {
if (!has_dll_main && name == "DllMain") { if (!has_dll_main && name == "DllMain") {
@@ -8162,12 +8173,14 @@ void ir_gen_tree(irGen *s) {
irValue *g = ir_value_global(a, e, nullptr); irValue *g = ir_value_global(a, e, nullptr);
g->Global.name = name; g->Global.name = name;
g->Global.is_thread_local = e->Variable.is_thread_local; 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 = {}; irGlobalVariable var = {};
var.var = g; var.var = g;
var.decl = decl; var.decl = decl;
if (e->type->kind == Type_Struct && e->type->Struct.has_proc_default_values) { if (e->type->kind == Type_Struct && e->type->Struct.has_proc_default_values) {
for_array(i, e->type->Struct.fields) { for_array(i, e->type->Struct.fields) {
Entity *f = e->type->Struct.fields[i]; Entity *f = e->type->Struct.fields[i];
@@ -8235,7 +8248,7 @@ void ir_gen_tree(irGen *s) {
String original_name = name; String original_name = name;
if (!scope->is_global || polymorphic_struct || is_type_polymorphic(e->type)) { 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) { } else if (e->kind == Entity_Procedure && e->Procedure.link_name.len > 0) {
// Handle later // Handle later
// } else if (scope->is_init && e->kind == Entity_Procedure && name == "main") { // } else if (scope->is_init && e->kind == Entity_Procedure && name == "main") {
+7 -3
View File
@@ -801,7 +801,7 @@ bool ir_print_is_proc_global(irModule *m, irProcedure *proc) {
return true; 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) { 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_byte(f, '\n');
ir_write_string(f, "define "); ir_write_string(f, "define ");
if (build_context.is_dll) { if (build_context.is_dll) {
// if (proc->tags & (ProcTag_export|ProcTag_dll_export)) { if (proc->is_export) {
if (proc->tags & (ProcTag_export)) {
ir_write_string(f, "dllexport "); ir_write_string(f, "dllexport ");
} }
} }
@@ -1924,6 +1923,11 @@ void print_llvm_ir(irGen *ir) {
if (g->is_foreign) { if (g->is_foreign) {
ir_write_string(f, str_lit("external ")); 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) { if (g->is_thread_local) {
ir_write_string(f, str_lit("thread_local ")); ir_write_string(f, str_lit("thread_local "));
} }
+12 -32
View File
@@ -95,11 +95,9 @@ enum ProcTag {
ProcTag_require_results = 1<<4, ProcTag_require_results = 1<<4,
ProcTag_foreign = 1<<10, ProcTag_link_name = 1<<11,
ProcTag_export = 1<<11, ProcTag_inline = 1<<12,
ProcTag_link_name = 1<<12, ProcTag_no_inline = 1<<13,
ProcTag_inline = 1<<13,
ProcTag_no_inline = 1<<14,
// ProcTag_dll_import = 1<<15, // ProcTag_dll_import = 1<<15,
// ProcTag_dll_export = 1<<16, // ProcTag_dll_export = 1<<16,
@@ -1933,12 +1931,7 @@ AstNode *parse_ident(AstFile *f) {
AstNode *parse_tag_expr(AstFile *f, AstNode *expression) { AstNode *parse_tag_expr(AstFile *f, AstNode *expression) {
Token token = expect_token(f, Token_Hash); Token token = expect_token(f, Token_Hash);
Token name = {}; Token name = expect_token(f, Token_Ident);
if (f->curr_token.kind == Token_export) {
name = expect_token(f, Token_export);
} else {
name = expect_token(f, Token_Ident);
}
return ast_tag_expr(f, token, name, expression); 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(require_results)
ELSE_IF_ADD_TAG(export)
ELSE_IF_ADD_TAG(bounds_check) ELSE_IF_ADD_TAG(bounds_check)
ELSE_IF_ADD_TAG(no_bounds_check) ELSE_IF_ADD_TAG(no_bounds_check)
ELSE_IF_ADD_TAG(inline) 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 (cc == ProcCC_Invalid) {
if ((*tags) & ProcTag_foreign || f->in_foreign_block) { if (f->in_foreign_block) {
cc = ProcCC_C; cc = ProcCC_C;
} else { } else {
cc = ProcCC_Odin; cc = ProcCC_Odin;
@@ -2142,10 +2134,6 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *link_name, ProcCallingConven
*calling_convention = cc; *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)) { if ((*tags & ProcTag_inline) && (*tags & ProcTag_no_inline)) {
syntax_error(f->curr_token, "You cannot apply both #inline and #no_inline to a procedure"); 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)) { 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"); 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)) { if (allow_token(f, Token_Undef)) {
return ast_proc_lit(f, type, nullptr, tags, link_name); return ast_proc_lit(f, type, nullptr, tags, link_name);
} else if (f->curr_token.kind == Token_OpenBrace) { } 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 *curr_proc = f->curr_proc;
AstNode *body = nullptr; AstNode *body = nullptr;
f->curr_proc = type; 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); return ast_proc_lit(f, type, body, tags, link_name);
} else if (allow_token(f, Token_do)) { } 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 *curr_proc = f->curr_proc;
AstNode *body = nullptr; AstNode *body = nullptr;
f->curr_proc = type; 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); 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) { if (tags != 0) {
// syntax_error(token, "A procedure type cannot have tags"); // syntax_error(token, "A procedure type cannot have tags");
} }
@@ -3041,7 +3016,12 @@ void parse_foreign_block_decl(AstFile *f, Array<AstNode *> *decls) {
AstNode *parse_foreign_block(AstFile *f, Token token) { AstNode *parse_foreign_block(AstFile *f, Token token) {
CommentGroup docs = f->lead_comment; 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 open = {};
Token close = {}; Token close = {};
Array<AstNode *> decls = make_ast_node_array(f); Array<AstNode *> decls = make_ast_node_array(f);
@@ -3063,7 +3043,6 @@ AstNode *parse_foreign_block(AstFile *f, Token token) {
close = expect_token(f, Token_CloseBrace); close = expect_token(f, Token_CloseBrace);
} }
AstNode *decl = ast_foreign_block_decl(f, token, foreign_library, open, close, decls, docs); AstNode *decl = ast_foreign_block_decl(f, token, foreign_library, open, close, decls, docs);
expect_semicolon(f, decl); expect_semicolon(f, decl);
return decl; return decl;
@@ -4409,6 +4388,7 @@ AstNode *parse_foreign_decl(AstFile *f) {
Token token = expect_token(f, Token_foreign); Token token = expect_token(f, Token_foreign);
switch (f->curr_token.kind) { switch (f->curr_token.kind) {
case Token_export:
case Token_Ident: case Token_Ident:
return parse_foreign_block(f, token); return parse_foreign_block(f, token);
+2 -2
View File
@@ -2431,7 +2431,7 @@ bool ssa_generate(Parser *parser, CheckerInfo *info) {
if (e->scope->is_init && name == "main") { if (e->scope->is_init && name == "main") {
entry_point = e; entry_point = e;
} }
if ((e->Procedure.tags & ProcTag_export) != 0 || if ((e->flags & EntityFlag_ForeignExport) != 0 ||
(e->Procedure.link_name.len > 0) || (e->Procedure.link_name.len > 0) ||
(e->scope->is_file && e->Procedure.link_name.len > 0)) { (e->scope->is_file && e->Procedure.link_name.len > 0)) {
if (!has_dll_main && name == "DllMain") { if (!has_dll_main && name == "DllMain") {
@@ -2464,7 +2464,7 @@ bool ssa_generate(Parser *parser, CheckerInfo *info) {
} }
if (!scope->is_global) { 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) { } else if (e->kind == Entity_Procedure && e->Procedure.link_name.len > 0) {
// Handle later // Handle later
} else if (scope->is_init && e->kind == Entity_Procedure && name == "main") { } else if (scope->is_init && e->kind == Entity_Procedure && name == "main") {