diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 783d254a8..e2f0c45a3 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -67,6 +67,7 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, Arraytmp_arena); // NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be @@ -90,6 +91,7 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, Arraytoken, "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count); } + gb_temp_arena_memory_end(tmp); } @@ -257,6 +259,46 @@ bool are_signatures_similar_enough(Type *a_, Type *b_) { return true; } +void init_entity_foreign_library(Checker *c, Entity *e) { + AstNode *ident = NULL; + Entity **foreign_library = NULL; + + switch (e->kind) { + case Entity_Procedure: + ident = e->Procedure.foreign_library_ident; + foreign_library = &e->Procedure.foreign_library; + break; + case Entity_Variable: + ident = e->Variable.foreign_library_ident; + foreign_library = &e->Variable.foreign_library; + break; + default: + return; + } + + if (ident == NULL) { + error(e->token, "foreign entiies must declare which library they are from"); + } else if (ident->kind != AstNode_Ident) { + error_node(ident, "foreign library names must be an identifier"); + } else { + String name = ident->Ident.string; + Entity *found = scope_lookup_entity(c->context.scope, name); + if (found == NULL) { + if (name == "_") { + error_node(ident, "`_` cannot be used as a value type"); + } else { + error_node(ident, "Undeclared name: %.*s", LIT(name)); + } + } else if (found->kind != Entity_LibraryName) { + error_node(ident, "`%.*s` cannot be used as a library name", LIT(name)); + } else { + // TODO(bill): Extra stuff to do with library names? + *foreign_library = found; + add_entity_use(c, ident, found); + } + } +} + void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { GB_ASSERT(e->type == NULL); if (d->proc_decl->kind != AstNode_ProcDecl) { @@ -326,38 +368,17 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { } if (is_foreign) { - auto *fp = &c->info.foreigns; String name = e->token.string; if (pd->link_name.len > 0) { name = pd->link_name; } - - AstNode *foreign_library = e->Procedure.foreign_library_ident; - if (foreign_library == NULL) { - error(e->token, "foreign procedures must declare which library they are from"); - } else if (foreign_library->kind != AstNode_Ident) { - error_node(foreign_library, "foreign library names must be an identifier"); - } else { - String name = foreign_library->Ident.string; - Entity *found = scope_lookup_entity(c->context.scope, name); - if (found == NULL) { - if (name == "_") { - error_node(foreign_library, "`_` cannot be used as a value type"); - } else { - error_node(foreign_library, "Undeclared name: %.*s", LIT(name)); - } - } else if (found->kind != Entity_LibraryName) { - error_node(foreign_library, "`%.*s` cannot be used as a library name", LIT(name)); - } else { - // TODO(bill): Extra stuff to do with library names? - e->Procedure.foreign_library = found; - add_entity_use(c, foreign_library, found); - } - } - e->Procedure.is_foreign = true; e->Procedure.link_name = name; + init_entity_foreign_library(c, e); + + + auto *fp = &c->info.foreigns; HashKey key = hash_string(name); Entity **found = map_get(fp, key); if (found) { @@ -365,9 +386,16 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { TokenPos pos = f->token.pos; Type *this_type = base_type(e->type); Type *other_type = base_type(f->type); - if (!are_signatures_similar_enough(this_type, other_type)) { + if (is_type_proc(this_type) && is_type_proc(other_type)) { + if (!are_signatures_similar_enough(this_type, other_type)) { + error_node(d->proc_decl, + "Redeclaration of foreign procedure `%.*s` with different type signatures\n" + "\tat %.*s(%td:%td)", + LIT(name), LIT(pos.file), pos.line, pos.column); + } + } else if (!are_types_identical(this_type, other_type)) { error_node(d->proc_decl, - "Redeclaration of foreign procedure `%.*s` with different type signatures\n" + "Foreign entity `%.*s` previously declared elsewhere with a different type\n" "\tat %.*s(%td:%td)", LIT(name), LIT(pos.file), pos.line, pos.column); } @@ -418,6 +446,33 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count e->type = check_type(c, type_expr); } + + if (e->Variable.is_foreign) { + if (init_expr != NULL) { + error(e->token, "A foreign variable declaration cannot have a default value"); + } + init_entity_foreign_library(c, e); + + String name = e->token.string; + auto *fp = &c->info.foreigns; + HashKey key = hash_string(name); + Entity **found = map_get(fp, key); + if (found) { + Entity *f = *found; + TokenPos pos = f->token.pos; + Type *this_type = base_type(e->type); + Type *other_type = base_type(f->type); + if (!are_types_identical(this_type, other_type)) { + error(e->token, + "Foreign entity `%.*s` previously declared elsewhere with a different type\n" + "\tat %.*s(%td:%td)", + LIT(name), LIT(pos.file), pos.line, pos.column); + } + } else { + map_set(fp, key, e); + } + } + if (init_expr == NULL) { if (type_expr == NULL) { e->type = t_invalid; @@ -438,6 +493,7 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count } } + Array inits; array_init(&inits, c->allocator, 1); array_add(&inits, init_expr); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index af5f5edeb..8b1590e59 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1535,6 +1535,35 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { check_stmt(c, pa->body, mod_flags); case_end; + case_ast_node(fb, ForeignBlockDecl, node); + AstNode *foreign_library = fb->foreign_library; + bool ok = true; + if (foreign_library->kind != AstNode_Ident) { + error_node(foreign_library, "foreign library name must be an identifier"); + ok = false; + } + + CheckerContext prev_context = c->context; + if (ok) { + c->context.curr_foreign_library = foreign_library; + } + + for_array(i, fb->decls) { + AstNode *decl = fb->decls[i]; + if (decl->kind == AstNode_GenDecl) { + switch (decl->GenDecl.token.kind) { + case Token_var: + case Token_let: + check_stmt(c, decl, flags); + break; + } + } + } + + c->context = prev_context; + case_end; + + case_ast_node(gd, GenDecl, node); GB_ASSERT(!c->context.scope->is_file); for_array(i, gd->specs) { @@ -1568,6 +1597,13 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { if (found == NULL) { entity = make_entity_variable(c->allocator, c->context.scope, token, NULL, (gd->flags&VarDeclFlag_immutable) != 0); entity->identifier = name; + + AstNode *fl = c->context.curr_foreign_library; + if (fl != NULL) { + GB_ASSERT(fl->kind == AstNode_Ident); + entity->Variable.is_foreign = true; + entity->Variable.foreign_library_ident = fl; + } } else { TokenPos pos = found->token.pos; error(token, @@ -1610,7 +1646,33 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration")); for (isize i = 0; i < entity_count; i++) { - add_entity(c, c->context.scope, entities[i]->identifier, entities[i]); + Entity *e = entities[i]; + if (e->Variable.is_foreign) { + if (vd->values.count > 0) { + error(e->token, "A foreign variable declaration cannot have a default value"); + } + init_entity_foreign_library(c, e); + + String name = e->token.string; + auto *fp = &c->info.foreigns; + HashKey key = hash_string(name); + Entity **found = map_get(fp, key); + if (found) { + Entity *f = *found; + TokenPos pos = f->token.pos; + Type *this_type = base_type(e->type); + Type *other_type = base_type(f->type); + if (!are_types_identical(this_type, other_type)) { + error(e->token, + "Foreign entity `%.*s` previously declared elsewhere with a different type\n" + "\tat %.*s(%td:%td)", + LIT(name), LIT(pos.file), pos.line, pos.column); + } + } else { + map_set(fp, key, e); + } + } + add_entity(c, c->context.scope, e->identifier, e); } if ((gd->flags & VarDeclFlag_using) != 0) { diff --git a/src/checker.cpp b/src/checker.cpp index 53d9c77aa..3dabbb694 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1555,6 +1555,14 @@ void check_collect_entities(Checker *c, Array nodes, bool is_file_sco gd->flags &= ~VarDeclFlag_using; // NOTE(bill): This error will be only caught once error_node(name, "`using` is not allowed at the file scope"); } + + AstNode *fl = c->context.curr_foreign_library; + if (fl != NULL) { + GB_ASSERT(fl->kind == AstNode_Ident); + e->Variable.is_foreign = true; + e->Variable.foreign_library_ident = fl; + } + entities[entity_count++] = e; DeclInfo *d = di; diff --git a/src/entity.cpp b/src/entity.cpp index 44464b49c..d6703f5b7 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -84,10 +84,13 @@ struct Entity { struct { i32 field_index; i32 field_src_index; - bool is_immutable; - bool is_thread_local; ExactValue default_value; bool default_is_nil; + bool is_immutable; + bool is_thread_local; + bool is_foreign; + Entity * foreign_library; + AstNode * foreign_library_ident; } Variable; struct { bool is_type_alias; diff --git a/src/ir.cpp b/src/ir.cpp index a14da8288..b9f07c166 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -480,8 +480,8 @@ struct irDebugInfo { union { struct { - AstFile * file; - String producer; + AstFile * file; + String producer; irDebugInfo *all_procs; } CompileUnit; struct { @@ -492,14 +492,14 @@ struct irDebugInfo { struct { irDebugInfo *parent; irDebugInfo *file; - TokenPos pos; - Scope * scope; // Actual scope + TokenPos pos; + Scope * scope; // Actual scope } Scope; struct { - Entity * entity; - String name; + Entity * entity; + String name; irDebugInfo *file; - TokenPos pos; + TokenPos pos; } Proc; struct { Array procs; @@ -507,9 +507,9 @@ struct irDebugInfo { struct { - String name; - i32 size; - i32 align; + String name; + i32 size; + i32 align; irDebugEncoding encoding; } BasicType; struct { @@ -522,12 +522,12 @@ struct irDebugInfo { } DerivedType; struct { irDebugEncoding encoding; - String name; - String identifier; + String name; + String identifier; irDebugInfo * file; - TokenPos pos; - i32 size; - i32 align; + TokenPos pos; + i32 size; + i32 align; Array elements; } CompositeType; struct { @@ -535,20 +535,20 @@ struct irDebugInfo { i64 value; } Enumerator; struct { - String name; - String linkage_name; + String name; + String linkage_name; irDebugInfo *scope; irDebugInfo *file; - TokenPos pos; + TokenPos pos; irValue *variable; irDebugInfo *declaration; } GlobalVariable; struct { - String name; + String name; irDebugInfo *scope; irDebugInfo *file; - TokenPos pos; - i32 arg; // Non-zero if proc parameter + TokenPos pos; + i32 arg; // Non-zero if proc parameter irDebugInfo *type; } LocalVariable; }; @@ -1292,6 +1292,23 @@ irValue *ir_add_local_for_identifier(irProcedure *proc, AstNode *name, bool zero Entity *e = entity_of_ident(proc->module->info, name); if (e != NULL) { ir_emit_comment(proc, e->token.string); + if (e->kind == Entity_Variable && + e->Variable.is_foreign) { + HashKey key = hash_string(e->token.string); + irValue **prev_value = map_get(&proc->module->members, key); + if (prev_value == NULL) { + // NOTE(bill): Don't do mutliple declarations in the IR + irValue *g = ir_value_global(proc->module->allocator, e, NULL); + + g->Global.name = e->token.string; + g->Global.is_foreign = true; + ir_module_add_value(proc->module, e, g); + map_set(&proc->module->members, key, g); + return g; + } else { + return *prev_value; + } + } return ir_add_local(proc, e, name); } return NULL; diff --git a/src/parser.cpp b/src/parser.cpp index c22e0e19c..4f0595d78 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1704,6 +1704,24 @@ bool is_semicolon_optional_for_node(AstFile *f, AstNode *s) { case AstNode_ProcDecl: return s->ProcDecl.body != NULL; + case AstNode_GenDecl: + if (s->GenDecl.close.pos.line != 0) { + return true; + } + if (s->GenDecl.specs.count == 1) { + return is_semicolon_optional_for_node(f, s->GenDecl.specs[0]); + } + break; + + case AstNode_ForeignBlockDecl: + if (s->ForeignBlockDecl.close.pos.line != 0) { + return true; + } + if (s->ForeignBlockDecl.decls.count == 1) { + return is_semicolon_optional_for_node(f, s->ForeignBlockDecl.decls[0]); + } + break; + case AstNode_TypeSpec: return is_semicolon_optional_for_node(f, s->TypeSpec.type); } @@ -1716,6 +1734,9 @@ void expect_semicolon(AstFile *f, AstNode *s) { return; } Token prev_token = f->prev_token; + if (prev_token.kind == Token_Semicolon) { + return; + } switch (f->curr_token.kind) { case Token_EOF: @@ -1736,8 +1757,32 @@ void expect_semicolon(AstFile *f, AstNode *s) { // break; // } } + String node_string = ast_node_strings[s->kind]; + if (s->kind == AstNode_GenDecl) { + switch (s->GenDecl.token.kind) { + case Token_var: + case Token_let: + node_string = str_lit("variable declaration"); + break; + case Token_const: + node_string = str_lit("const declaration"); + break; + case Token_type: + node_string = str_lit("type declaration"); + break; + case Token_import: + case Token_import_load: + node_string = str_lit("import declaration"); + break; + case Token_foreign_library: + case Token_foreign_system_library: + node_string = str_lit("foreign library declaration"); + break; + } + } + syntax_error(prev_token, "Expected `;` after %.*s, got %.*s", - LIT(ast_node_strings[s->kind]), LIT(token_strings[prev_token.kind])); + LIT(node_string), LIT(token_strings[prev_token.kind])); } else { syntax_error(prev_token, "Expected `;`"); } @@ -2572,7 +2617,8 @@ AstNode *parse_gen_decl(AstFile *f, Token token, ParseSpecFunc *func) { } } else { array_init(&specs, heap_allocator(), 1); - array_add(&specs, func(f, token)); + AstNode *spec = func(f, token); + array_add(&specs, spec); } if (specs.count == 0) { @@ -2764,25 +2810,29 @@ PARSE_SPEC_FUNC(parse_foreign_library_spec) { AstNode *parse_decl(AstFile *f); void parse_foreign_block_decl(AstFile *f, Array *decls) { - AstNode *decl = parse_decl(f); + AstNode *decl = parse_stmt(f); switch (decl->kind) { + case AstNode_EmptyStmt: + case AstNode_BadStmt: case AstNode_BadDecl: - break; + return; case AstNode_ProcDecl: array_add(decls, decl); - break; + return; - // case AstNode_GenDecl: - // if (decl->GenDecl.token.kind == Token_var) { - // array_add(decls, decl); - // break; - // } + case AstNode_GenDecl: + switch (decl->GenDecl.token.kind) { + case Token_var: + case Token_let: + array_add(decls, decl); + return; + } /* fallthrough */ default: error_node(decl, "Only procedures declarations are allowed within a foreign block at the moment"); - break; + return; } } @@ -3944,7 +3994,9 @@ AstNode *parse_stmt(AstFile *f) { case Token_foreign: case Token_foreign_library: case Token_foreign_system_library: - return parse_decl(f); + s = parse_decl(f); + expect_semicolon(f, s); + return s; case Token_if: return parse_if_stmt(f);