mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-25 23:14:59 -07:00
Add foreign variables
This commit is contained in:
+83
-27
@@ -67,6 +67,7 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, Array<AstNo
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_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, Array<AstNo
|
||||
error(lhs[0]->token, "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<AstNode *> inits;
|
||||
array_init(&inits, c->allocator, 1);
|
||||
array_add(&inits, init_expr);
|
||||
|
||||
+63
-1
@@ -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) {
|
||||
|
||||
@@ -1555,6 +1555,14 @@ void check_collect_entities(Checker *c, Array<AstNode *> 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;
|
||||
|
||||
+5
-2
@@ -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;
|
||||
|
||||
+38
-21
@@ -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<irDebugInfo *> 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<irDebugInfo *> 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;
|
||||
|
||||
+64
-12
@@ -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<AstNode *> *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);
|
||||
|
||||
Reference in New Issue
Block a user