mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-14 15:11:25 -07:00
@(default_calling_convention = ...) for foreign blocks
This commit is contained in:
+11
-4
@@ -467,6 +467,10 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
|
||||
|
||||
|
||||
if (d != nullptr && d->attributes.count > 0) {
|
||||
StringSet set = {};
|
||||
string_set_init(&set, heap_allocator());
|
||||
defer (string_set_destroy(&set));
|
||||
|
||||
for_array(i, d->attributes) {
|
||||
AstNode *attr = d->attributes[i];
|
||||
if (attr->kind != AstNode_Attribute) continue;
|
||||
@@ -500,14 +504,17 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
|
||||
}
|
||||
}
|
||||
|
||||
if (string_set_exists(&set, name)) {
|
||||
error(elem, "Previous declaration of `%.*s`", LIT(name));
|
||||
} else {
|
||||
string_set_add(&set, name);
|
||||
}
|
||||
|
||||
if (name == "link_name") {
|
||||
if (link_name.len > 0) {
|
||||
error(elem, "Previous declaration of `link_name`");
|
||||
}
|
||||
if (ev.kind == ExactValue_String) {
|
||||
link_name = ev.value_string;
|
||||
} else {
|
||||
error(elem, "Expected a string value for `link_name`");
|
||||
error(elem, "Expected a string value for `%.*s`", LIT(name));
|
||||
}
|
||||
} else {
|
||||
error(elem, "Unknown attribute element name `%.*s`", LIT(name));
|
||||
|
||||
@@ -1670,8 +1670,11 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
CheckerContext prev_context = c->context;
|
||||
if (ok) {
|
||||
c->context.curr_foreign_library = foreign_library;
|
||||
c->context.default_foreign_cc = ProcCC_C;
|
||||
}
|
||||
|
||||
check_foreign_block_decl_attributes(c, fb);
|
||||
|
||||
for_array(i, fb->decls) {
|
||||
AstNode *decl = fb->decls[i];
|
||||
if (decl->kind == AstNode_ValueDecl && decl->ValueDecl.is_mutable) {
|
||||
|
||||
+13
-4
@@ -1888,6 +1888,15 @@ bool check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node, Array
|
||||
}
|
||||
}
|
||||
|
||||
ProcCallingConvention cc = pt->calling_convention;
|
||||
if (cc == ProcCC_ForeignBlockDefault) {
|
||||
cc = ProcCC_C;
|
||||
if (c->context.default_foreign_cc > 0) {
|
||||
cc = c->context.default_foreign_cc;
|
||||
}
|
||||
}
|
||||
GB_ASSERT(cc > 0);
|
||||
|
||||
type->Proc.node = proc_type_node;
|
||||
type->Proc.scope = c->context.scope;
|
||||
type->Proc.params = params;
|
||||
@@ -1895,18 +1904,18 @@ bool check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node, Array
|
||||
type->Proc.results = results;
|
||||
type->Proc.result_count = cast(i32)result_count;
|
||||
type->Proc.variadic = variadic;
|
||||
type->Proc.calling_convention = pt->calling_convention;
|
||||
type->Proc.calling_convention = cc;
|
||||
type->Proc.is_polymorphic = pt->generic;
|
||||
type->Proc.specialization_count = specialization_count;
|
||||
|
||||
if (param_count > 0) {
|
||||
Entity *end = params->Tuple.variables[param_count-1];
|
||||
if (end->flags&EntityFlag_CVarArg) {
|
||||
if (pt->calling_convention == ProcCC_Odin) {
|
||||
if (cc == ProcCC_Odin) {
|
||||
error(end->token, "Odin calling convention does not support #c_vararg");
|
||||
} else if (pt->calling_convention == ProcCC_Contextless) {
|
||||
} else if (cc == ProcCC_Contextless) {
|
||||
error(end->token, "Odin's contextless calling convention does not support #c_vararg");
|
||||
} else if (pt->calling_convention == ProcCC_Fast) {
|
||||
} else if (cc == ProcCC_Fast) {
|
||||
error(end->token, "Fast calling convention does not support #c_vararg");
|
||||
} else {
|
||||
type->Proc.c_vararg = true;
|
||||
|
||||
@@ -423,6 +423,7 @@ struct CheckerContext {
|
||||
Type * type_hint;
|
||||
DeclInfo * curr_proc_decl;
|
||||
AstNode * curr_foreign_library;
|
||||
ProcCallingConvention default_foreign_cc;
|
||||
|
||||
bool in_foreign_export;
|
||||
bool collect_delayed_decls;
|
||||
@@ -1854,6 +1855,7 @@ void check_procedure_overloading(Checker *c, Entity *e) {
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
}
|
||||
|
||||
void check_foreign_block_decl_attributes(Checker *c, AstNodeForeignBlockDecl *fb);
|
||||
|
||||
#include "check_expr.cpp"
|
||||
#include "check_type.cpp"
|
||||
@@ -1861,6 +1863,71 @@ void check_procedure_overloading(Checker *c, Entity *e) {
|
||||
#include "check_stmt.cpp"
|
||||
|
||||
|
||||
void check_foreign_block_decl_attributes(Checker *c, AstNodeForeignBlockDecl *fb) {
|
||||
if (fb->attributes.count == 0) return;
|
||||
StringSet set = {};
|
||||
string_set_init(&set, heap_allocator());
|
||||
defer (string_set_destroy(&set));
|
||||
|
||||
for_array(i, fb->attributes) {
|
||||
AstNode *attr = fb->attributes[i];
|
||||
if (attr->kind != AstNode_Attribute) continue;
|
||||
for_array(j, attr->Attribute.elems) {
|
||||
AstNode *elem = attr->Attribute.elems[j];
|
||||
String name = {};
|
||||
AstNode *value = nullptr;
|
||||
|
||||
switch (elem->kind) {
|
||||
case_ast_node(i, Ident, elem);
|
||||
name = i->token.string;
|
||||
case_end;
|
||||
case_ast_node(fv, FieldValue, elem);
|
||||
GB_ASSERT(fv->field->kind == AstNode_Ident);
|
||||
name = fv->field->Ident.token.string;
|
||||
value = fv->value;
|
||||
case_end;
|
||||
default:
|
||||
error(elem, "Invalid attribute element");
|
||||
continue;
|
||||
}
|
||||
|
||||
ExactValue ev = {};
|
||||
if (value != nullptr) {
|
||||
Operand op = {};
|
||||
check_expr(c, &op, value);
|
||||
if (op.mode != Addressing_Constant) {
|
||||
error(value, "An attribute element must be constant");
|
||||
} else {
|
||||
ev = op.value;
|
||||
}
|
||||
}
|
||||
|
||||
if (string_set_exists(&set, name)) {
|
||||
error(elem, "Previous declaration of `%.*s`", LIT(name));
|
||||
continue;
|
||||
} else {
|
||||
string_set_add(&set, name);
|
||||
}
|
||||
|
||||
if (name == "default_calling_convention") {
|
||||
if (ev.kind == ExactValue_String) {
|
||||
auto cc = string_to_calling_convention(ev.value_string);
|
||||
if (cc == ProcCC_Invalid) {
|
||||
error(elem, "Unknown procedure calling convention: `%.*s`\n", LIT(ev.value_string));
|
||||
} else {
|
||||
c->context.default_foreign_cc = cc;
|
||||
}
|
||||
} else {
|
||||
error(elem, "Expected a string value for `%.*s`", LIT(name));
|
||||
}
|
||||
} else {
|
||||
error(elem, "Unknown attribute element name `%.*s`", LIT(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool check_arity_match(Checker *c, AstNodeValueDecl *vd, bool is_global) {
|
||||
isize lhs = vd->names.count;
|
||||
@@ -1990,6 +2057,7 @@ 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->Variable.is_export = true;
|
||||
}
|
||||
@@ -2054,6 +2122,18 @@ void check_collect_value_decl(Checker *c, AstNode *decl) {
|
||||
GB_ASSERT(fl->kind == AstNode_Ident);
|
||||
e->Procedure.foreign_library_ident = fl;
|
||||
e->Procedure.is_foreign = true;
|
||||
|
||||
GB_ASSERT(pl->type->kind == AstNode_ProcType);
|
||||
auto cc = pl->type->ProcType.calling_convention;
|
||||
if (cc == ProcCC_ForeignBlockDefault) {
|
||||
cc = ProcCC_C;
|
||||
if (c->context.default_foreign_cc > 0) {
|
||||
cc = c->context.default_foreign_cc;
|
||||
}
|
||||
}
|
||||
GB_ASSERT(cc != ProcCC_Invalid);
|
||||
pl->type->ProcType.calling_convention = cc;
|
||||
|
||||
} else if (c->context.in_foreign_export) {
|
||||
e->Procedure.is_export = true;
|
||||
}
|
||||
@@ -2103,6 +2183,8 @@ void check_add_foreign_block_decl(Checker *c, AstNode *decl) {
|
||||
c->context.curr_foreign_library = nullptr;
|
||||
}
|
||||
|
||||
check_foreign_block_decl_attributes(c, fb);
|
||||
|
||||
c->context.collect_delayed_decls = true;
|
||||
check_collect_entities(c, fb->decls);
|
||||
c->context = prev_context;
|
||||
|
||||
@@ -114,6 +114,7 @@ u128 fnv128a(void const *data, isize len) {
|
||||
|
||||
#include "map.cpp"
|
||||
#include "ptr_set.cpp"
|
||||
#include "string_set.cpp"
|
||||
#include "priority_queue.cpp"
|
||||
|
||||
|
||||
|
||||
+33
-17
@@ -110,6 +110,8 @@ enum ProcCallingConvention {
|
||||
ProcCC_C = 3,
|
||||
ProcCC_Std = 4,
|
||||
ProcCC_Fast = 5,
|
||||
|
||||
ProcCC_ForeignBlockDefault = -1,
|
||||
};
|
||||
|
||||
enum VarDeclFlag {
|
||||
@@ -332,6 +334,7 @@ AST_NODE_KIND(_DeclBegin, "", i32) \
|
||||
AstNode * foreign_library; \
|
||||
Token open, close; \
|
||||
Array<AstNode *> decls; \
|
||||
Array<AstNode *> attributes; \
|
||||
bool been_handled; \
|
||||
CommentGroup docs; \
|
||||
}) \
|
||||
@@ -842,6 +845,7 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) {
|
||||
case AstNode_ForeignBlockDecl:
|
||||
n->ForeignBlockDecl.foreign_library = clone_ast_node(a, n->ForeignBlockDecl.foreign_library);
|
||||
n->ForeignBlockDecl.decls = clone_ast_node_array(a, n->ForeignBlockDecl.decls);
|
||||
n->ForeignBlockDecl.attributes = clone_ast_node_array(a, n->ForeignBlockDecl.attributes);
|
||||
break;
|
||||
case AstNode_Label:
|
||||
n->Label.name = clone_ast_node(a, n->Label.name);
|
||||
@@ -1548,6 +1552,8 @@ AstNode *ast_foreign_block_decl(AstFile *f, Token token, AstNode *foreign_librar
|
||||
result->ForeignBlockDecl.close = close;
|
||||
result->ForeignBlockDecl.decls = decls;
|
||||
result->ForeignBlockDecl.docs = docs;
|
||||
|
||||
result->ForeignBlockDecl.attributes.allocator = heap_allocator();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -3247,6 +3253,22 @@ AstNode *parse_results(AstFile *f) {
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
ProcCallingConvention string_to_calling_convention(String s) {
|
||||
if (s == "odin") {
|
||||
return ProcCC_Odin;
|
||||
} else if (s == "contextless") {
|
||||
return ProcCC_Contextless;
|
||||
} else if (s == "cdecl" || s == "c") {
|
||||
return ProcCC_C;
|
||||
} else if (s == "stdcall" || s == "std") {
|
||||
return ProcCC_Std;
|
||||
} else if (s == "fastcall" || s == "fast") {
|
||||
return ProcCC_Fast;
|
||||
}
|
||||
return ProcCC_Invalid;
|
||||
}
|
||||
|
||||
AstNode *parse_proc_type(AstFile *f, Token proc_token) {
|
||||
AstNode *params = nullptr;
|
||||
AstNode *results = nullptr;
|
||||
@@ -3254,24 +3276,16 @@ AstNode *parse_proc_type(AstFile *f, Token proc_token) {
|
||||
ProcCallingConvention cc = ProcCC_Invalid;
|
||||
if (f->curr_token.kind == Token_String) {
|
||||
Token token = expect_token(f, Token_String);
|
||||
String conv = token.string;
|
||||
if (conv == "odin") {
|
||||
cc = ProcCC_Odin;
|
||||
} else if (conv == "contextless") {
|
||||
cc = ProcCC_Contextless;
|
||||
} else if (conv == "cdecl" || conv == "c") {
|
||||
cc = ProcCC_C;
|
||||
} else if (conv == "stdcall" || conv == "std") {
|
||||
cc = ProcCC_Std;
|
||||
} else if (conv == "fastcall" || conv == "fast") {
|
||||
cc = ProcCC_Fast;
|
||||
auto c = string_to_calling_convention(token.string);
|
||||
if (c == ProcCC_Invalid) {
|
||||
syntax_error(token, "Unknown procedure calling convention: `%.*s`\n", LIT(token.string));
|
||||
} else {
|
||||
syntax_error(token, "Unknown procedure tag #%.*s\n", LIT(conv));
|
||||
cc = c;
|
||||
}
|
||||
}
|
||||
if (cc == ProcCC_Invalid) {
|
||||
if (f->in_foreign_block) {
|
||||
cc = ProcCC_C;
|
||||
cc = ProcCC_ForeignBlockDefault;
|
||||
} else {
|
||||
cc = ProcCC_Odin;
|
||||
}
|
||||
@@ -4578,7 +4592,6 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
f->expr_level++;
|
||||
if (f->curr_token.kind != Token_CloseParen) {
|
||||
elems = make_ast_node_array(f);
|
||||
|
||||
while (f->curr_token.kind != Token_CloseParen &&
|
||||
f->curr_token.kind != Token_EOF) {
|
||||
AstNode *elem = parse_ident(f);
|
||||
@@ -4601,12 +4614,15 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
AstNode *attribute = ast_attribute(f, token, open, close, elems);
|
||||
|
||||
AstNode *decl = parse_stmt(f);
|
||||
if (decl->kind != AstNode_ValueDecl) {
|
||||
syntax_error(decl, "Expected a value declaration after an attribute, got %.*s", LIT(ast_node_strings[decl->kind]));
|
||||
if (decl->kind == AstNode_ValueDecl) {
|
||||
array_add(&decl->ValueDecl.attributes, attribute);
|
||||
} else if (decl->kind == AstNode_ForeignBlockDecl) {
|
||||
array_add(&decl->ForeignBlockDecl.attributes, attribute);
|
||||
} else {
|
||||
syntax_error(decl, "Expected a value or foreign declaration after an attribute, got %.*s", LIT(ast_node_strings[decl->kind]));
|
||||
return ast_bad_stmt(f, token, f->curr_token);
|
||||
}
|
||||
|
||||
array_add(&decl->ValueDecl.attributes, attribute);
|
||||
return decl;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user