diff --git a/src/check_decl.cpp b/src/check_decl.cpp index bc8d35a40..4f5fe3ebe 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -459,8 +459,6 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { bool is_foreign = e->Procedure.is_foreign; bool is_export = e->Procedure.is_export; - 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; String link_name = {}; @@ -513,6 +511,9 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { if (name == "link_name") { if (ev.kind == ExactValue_String) { link_name = ev.value_string; + if (!is_foreign_name_valid(link_name)) { + error(elem, "Invalid link name: %.*s", LIT(link_name)); + } } else { error(elem, "Expected a string value for `%.*s`", LIT(name)); } @@ -548,10 +549,6 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { } } - if (is_inline && is_no_inline) { - error(pl->type, "You cannot apply both `inline` and `no_inline` to a procedure"); - } - if (is_foreign && is_export) { error(pl->type, "A foreign procedure cannot have an `export` tag"); } @@ -673,9 +670,69 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count } e->flags |= EntityFlag_Visited; + + String link_name = {}; + DeclInfo *decl = decl_info_of_entity(&c->info, e); if (decl != nullptr && decl->attributes.count > 0) { - error(decl->attributes[0], "Attributes are not allowed on variable declarations, yet"); + StringSet set = {}; + string_set_init(&set, heap_allocator()); + defer (string_set_destroy(&set)); + + for_array(i, decl->attributes) { + AstNode *attr = decl->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 == "link_name") { + if (ev.kind == ExactValue_String) { + link_name = ev.value_string; + if (!is_foreign_name_valid(link_name)) { + error(elem, "Invalid link name: %.*s", LIT(link_name)); + } + } else { + error(elem, "Expected a string value for `%.*s`", LIT(name)); + } + } else { + error(elem, "Unknown attribute element name `%.*s`", LIT(name)); + } + } + } } String context_name = str_lit("variable declaration"); @@ -704,8 +761,15 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count } init_entity_foreign_library(c, e); } + if (link_name.len > 0) { + e->Variable.link_name = link_name; + } + if (e->Variable.is_foreign || e->Variable.is_export) { String name = e->token.string; + if (link_name.len > 0) { + name = link_name; + } auto *fp = &c->info.foreigns; HashKey key = hash_string(name); Entity **found = map_get(fp, key); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 3add868ec..6bde1abb3 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1670,7 +1670,7 @@ 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; + c->context.default_foreign_cc = ProcCC_CDecl; } check_foreign_block_decl_attributes(c, fb); diff --git a/src/check_type.cpp b/src/check_type.cpp index 5d3382f47..b2536e685 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1890,7 +1890,7 @@ 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; + cc = ProcCC_CDecl; if (c->context.default_foreign_cc > 0) { cc = c->context.default_foreign_cc; } @@ -1911,14 +1911,10 @@ bool check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node, Array if (param_count > 0) { Entity *end = params->Tuple.variables[param_count-1]; if (end->flags&EntityFlag_CVarArg) { - if (cc == ProcCC_Odin) { - error(end->token, "Odin calling convention does not support #c_vararg"); - } else if (cc == ProcCC_Contextless) { - error(end->token, "Odin's contextless calling convention does not support #c_vararg"); - } else if (cc == ProcCC_Fast) { - error(end->token, "Fast calling convention does not support #c_vararg"); - } else { + if (cc == ProcCC_StdCall || cc == ProcCC_CDecl) { type->Proc.c_vararg = true; + } else { + error(end->token, "Calling convention does not support #c_vararg"); } } } diff --git a/src/checker.cpp b/src/checker.cpp index 180c99a73..4d7b7e83a 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2126,7 +2126,7 @@ void check_collect_value_decl(Checker *c, AstNode *decl) { GB_ASSERT(pl->type->kind == AstNode_ProcType); auto cc = pl->type->ProcType.calling_convention; if (cc == ProcCC_ForeignBlockDefault) { - cc = ProcCC_C; + cc = ProcCC_CDecl; if (c->context.default_foreign_cc > 0) { cc = c->context.default_foreign_cc; } diff --git a/src/ir.cpp b/src/ir.cpp index b0a4985db..565329ef1 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -117,6 +117,7 @@ struct irProcedure { AstNode * type_expr; AstNode * body; u64 tags; + ProcInlining inlining; bool is_foreign; bool is_export; bool is_entry_point; @@ -3695,6 +3696,7 @@ irValue *ir_gen_anonymous_proc_lit(irModule *m, String prefix_name, AstNode *exp m, nullptr, type, pl->type, pl->body, name); value->Proc.tags = pl->tags; + value->Proc.inlining = pl->inlining; value->Proc.parent = proc; array_add(&m->procs_to_generate, value); @@ -5902,6 +5904,7 @@ void ir_build_poly_proc(irProcedure *proc, AstNodeProcLit *pd, Entity *e) { proc->module, e, e->type, pd->type, pd->body, name); value->Proc.tags = pd->tags; + value->Proc.inlining = pd->inlining; value->Proc.parent = proc; ir_module_add_value(proc->module, e, value); @@ -5987,6 +5990,7 @@ void ir_build_constant_value_decl(irProcedure *proc, AstNodeValueDecl *vd) { proc->module, e, e->type, pl->type, pl->body, name); value->Proc.tags = pl->tags; + value->Proc.inlining = pl->inlining; ir_module_add_value(proc->module, e, value); ir_build_proc(value, proc); @@ -8295,6 +8299,7 @@ void ir_gen_tree(irGen *s) { irValue *p = ir_value_procedure(a, m, e, e->type, type_expr, body, name); p->Proc.tags = pl->tags; + p->Proc.inlining = pl->inlining; ir_module_add_value(m, e, p); HashKey hash_name = hash_string(name); @@ -8363,7 +8368,7 @@ void ir_gen_tree(irGen *s) { Type *proc_type = make_type_proc(a, proc_scope, proc_params, 3, - proc_results, 1, false, ProcCC_Std); + proc_results, 1, false, ProcCC_StdCall); // TODO(bill): make this more robust proc_type->Proc.abi_compat_params = gb_alloc_array(a, Type *, proc_params->Tuple.variables.count); @@ -8380,7 +8385,7 @@ void ir_gen_tree(irGen *s) { map_set(&m->members, hash_string(name), p); irProcedure *proc = &p->Proc; - proc->tags = ProcTag_no_inline; // TODO(bill): is no_inline a good idea? + proc->inlining = ProcInlining_no_inline; // TODO(bill): is no_inline a good idea? proc->is_entry_point = true; e->Procedure.link_name = name; @@ -8434,7 +8439,7 @@ void ir_gen_tree(irGen *s) { Type *proc_type = make_type_proc(a, proc_scope, proc_params, 2, - proc_results, 1, false, ProcCC_C); + proc_results, 1, false, ProcCC_CDecl); // TODO(bill): make this more robust proc_type->Proc.abi_compat_params = gb_alloc_array(a, Type *, proc_params->Tuple.variables.count); @@ -8451,7 +8456,7 @@ void ir_gen_tree(irGen *s) { map_set(&m->members, hash_string(name), p); irProcedure *proc = &p->Proc; - proc->tags = ProcTag_no_inline; // TODO(bill): is no_inline a good idea? + proc->inlining = ProcInlining_no_inline; // TODO(bill): is no_inline a good idea? proc->is_entry_point = true; e->Procedure.link_name = name; @@ -8543,7 +8548,7 @@ void ir_gen_tree(irGen *s) { irProcedure *proc = &p->Proc; - proc->tags = ProcTag_no_inline; // TODO(bill): is no_inline a good idea? + proc->inlining = ProcInlining_no_inline; // TODO(bill): is no_inline a good idea? ir_begin_procedure_body(proc); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 725c08d02..637486912 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -882,9 +882,9 @@ void ir_print_calling_convention(irFileBuffer *f, irModule *m, ProcCallingConven switch (cc) { case ProcCC_Odin: ir_write_string(f, ""); break; case ProcCC_Contextless: ir_write_string(f, ""); break; - case ProcCC_C: ir_write_string(f, "ccc "); break; - case ProcCC_Std: ir_write_string(f, "cc 64 "); break; - case ProcCC_Fast: ir_write_string(f, "cc 65 "); break; + case ProcCC_CDecl: ir_write_string(f, "ccc "); break; + case ProcCC_StdCall: ir_write_string(f, "cc 64 "); break; + case ProcCC_FastCall: ir_write_string(f, "cc 65 "); break; default: GB_PANIC("unknown calling convention: %d", cc); } } @@ -1751,14 +1751,15 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) { ir_write_string(f, ") "); - if (proc->tags & ProcTag_inline) { - ir_write_string(f, "alwaysinline "); - } - if (proc->tags & ProcTag_no_inline) { + switch (proc->inlining) { + case ProcInlining_no_inline: ir_write_string(f, "noinline "); + break; + case ProcInlining_inline: + ir_write_string(f, "alwaysinline "); + break; } - if (proc->entity != nullptr) { if (proc->body != nullptr) { irDebugInfo **di_ = map_get(&proc->module->debug_info, hash_pointer(proc->entity)); diff --git a/src/parser.cpp b/src/parser.cpp index fef9a95a0..4595909d9 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -88,28 +88,29 @@ struct Parser { gbMutex file_decl_mutex; }; +enum ProcInlining { + ProcInlining_none = 0, + ProcInlining_inline = 1, + ProcInlining_no_inline = 2, +}; + enum ProcTag { ProcTag_bounds_check = 1<<0, ProcTag_no_bounds_check = 1<<1, - - ProcTag_require_results = 1<<4, - - ProcTag_link_name = 1<<11, - ProcTag_inline = 1<<12, - ProcTag_no_inline = 1<<13, - - // ProcTag_dll_import = 1<<15, - // ProcTag_dll_export = 1<<16, }; enum ProcCallingConvention { - ProcCC_Invalid = 0, - ProcCC_Odin = 1, - ProcCC_Contextless = 2, - ProcCC_C = 3, - ProcCC_Std = 4, - ProcCC_Fast = 5, + ProcCC_Invalid = 0, + ProcCC_Odin, + ProcCC_Contextless, + ProcCC_CDecl, + ProcCC_StdCall, + ProcCC_FastCall, + + // TODO(bill): Add extra calling conventions + // ProcCC_VectorCall, + // ProcCC_ClrCall, ProcCC_ForeignBlockDefault = -1, }; @@ -172,6 +173,7 @@ Array make_ast_node_array(AstFile *f, isize init_capacity = 8) { AstNode *type; \ AstNode *body; \ u64 tags; \ + ProcInlining inlining; \ }) \ AST_NODE_KIND(CompoundLit, "compound literal", struct { \ AstNode *type; \ @@ -2275,11 +2277,20 @@ AstNode *parse_operand(AstFile *f, bool lhs) { syntax_error(expr, "%.*s must be followed by a procedure literal, got %.*s", LIT(token.string), LIT(ast_node_strings[expr->kind])); return ast_bad_expr(f, token, f->curr_token); } + ProcInlining pi = ProcInlining_none; if (token.kind == Token_inline) { - expr->ProcLit.tags |= ProcTag_inline; + pi = ProcInlining_inline; } else if (token.kind == Token_no_inline) { - expr->ProcLit.tags |= ProcTag_no_inline; + pi = ProcInlining_no_inline; } + if (pi != ProcInlining_none) { + if (expr->ProcLit.inlining != ProcInlining_none && + expr->ProcLit.inlining != pi) { + syntax_error(expr, "You cannot apply both `inline` and `no_inline` to a procedure literal"); + } + expr->ProcLit.inlining = pi; + } + return expr; } break; @@ -3255,17 +3266,14 @@ AstNode *parse_results(AstFile *f) { 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; - } + if (s == "odin") return ProcCC_Odin; + if (s == "contextless") return ProcCC_Contextless; + if (s == "cdecl") return ProcCC_CDecl; + if (s == "c") return ProcCC_CDecl; + if (s == "stdcall") return ProcCC_StdCall; + if (s == "std") return ProcCC_StdCall; + if (s == "fastcall") return ProcCC_FastCall; + if (s == "fast") return ProcCC_FastCall; return ProcCC_Invalid; } diff --git a/src/string_set.cpp b/src/string_set.cpp new file mode 100644 index 000000000..9e44e1b1e --- /dev/null +++ b/src/string_set.cpp @@ -0,0 +1,184 @@ +struct StringSetFindResult { + isize hash_index; + isize entry_prev; + isize entry_index; +}; + +struct StringSetEntry { + HashKey key; + isize next; + String value; +}; + +struct StringSet { + Array hashes; + Array entries; +}; + + +void string_set_init (StringSet *s, gbAllocator a, isize capacity = 16); +void string_set_destroy(StringSet *s); +void string_set_add (StringSet *s, String str); +bool string_set_exists (StringSet *s, String str); +void string_set_remove (StringSet *s, String str); +void string_set_clear (StringSet *s); +void string_set_grow (StringSet *s); +void string_set_rehash (StringSet *s, isize new_count); + + +gb_inline void string_set_init(StringSet *s, gbAllocator a, isize capacity) { + array_init(&s->hashes, a); + array_init(&s->entries, a); +} + +gb_inline void string_set_destroy(StringSet *s) { + array_free(&s->entries); + array_free(&s->hashes); +} + +gb_internal isize string_set__add_entry(StringSet *s, HashKey key) { + StringSetEntry e = {}; + e.key = key; + e.next = -1; + array_add(&s->entries, e); + return s->entries.count-1; +} + +gb_internal StringSetFindResult string_set__find(StringSet *s, HashKey key) { + StringSetFindResult fr = {-1, -1, -1}; + if (s->hashes.count > 0) { + // fr.hash_index = u128_to_i64(key.key % u128_from_i64(s->hashes.count)); + fr.hash_index = key.key % s->hashes.count; + fr.entry_index = s->hashes[fr.hash_index]; + while (fr.entry_index >= 0) { + if (hash_key_equal(s->entries[fr.entry_index].key, key)) { + return fr; + } + fr.entry_prev = fr.entry_index; + fr.entry_index = s->entries[fr.entry_index].next; + } + } + return fr; +} + +gb_internal StringSetFindResult string_set__find_from_entry(StringSet *s, StringSetEntry *e) { + StringSetFindResult fr = {-1, -1, -1}; + if (s->hashes.count > 0) { + fr.hash_index = e->key.key % s->hashes.count; + fr.entry_index = s->hashes[fr.hash_index]; + while (fr.entry_index >= 0) { + if (&s->entries[fr.entry_index] == e) { + return fr; + } + fr.entry_prev = fr.entry_index; + fr.entry_index = s->entries[fr.entry_index].next; + } + } + return fr; +} + +gb_internal b32 string_set__full(StringSet *s) { + return 0.75f * s->hashes.count <= s->entries.count; +} + +gb_inline void string_set_grow(StringSet *s) { + isize new_count = ARRAY_GROW_FORMULA(s->entries.count); + string_set_rehash(s, new_count); +} + +void string_set_rehash(StringSet *s, isize new_count) { + isize i, j; + StringSet ns = {}; + string_set_init(&ns, s->hashes.allocator); + array_resize(&ns.hashes, new_count); + array_reserve(&ns.entries, s->entries.count); + for (i = 0; i < new_count; i++) { + ns.hashes[i] = -1; + } + for (i = 0; i < s->entries.count; i++) { + StringSetEntry *e = &s->entries[i]; + StringSetFindResult fr; + if (ns.hashes.count == 0) { + string_set_grow(&ns); + } + fr = string_set__find(&ns, e->key); + j = string_set__add_entry(&ns, e->key); + if (fr.entry_prev < 0) { + ns.hashes[fr.hash_index] = j; + } else { + ns.entries[fr.entry_prev].next = j; + } + ns.entries[j].next = fr.entry_index; + ns.entries[j].value = e->value; + if (string_set__full(&ns)) { + string_set_grow(&ns); + } + } + string_set_destroy(s); + *s = ns; +} + +gb_inline bool string_set_exists(StringSet *s, String str) { + HashKey key = hash_string(str); + isize index = string_set__find(s, key).entry_index; + return index >= 0; +} + +void string_set_add(StringSet *s, String str) { + isize index; + StringSetFindResult fr; + HashKey key = hash_string(str); + if (s->hashes.count == 0) { + string_set_grow(s); + } + fr = string_set__find(s, key); + if (fr.entry_index >= 0) { + index = fr.entry_index; + } else { + index = string_set__add_entry(s, key); + if (fr.entry_prev >= 0) { + s->entries[fr.entry_prev].next = index; + } else { + s->hashes[fr.hash_index] = index; + } + } + s->entries[index].value = str; + + if (string_set__full(s)) { + string_set_grow(s); + } +} + + +void string_set__erase(StringSet *s, StringSetFindResult fr) { + StringSetFindResult last; + if (fr.entry_prev < 0) { + s->hashes[fr.hash_index] = s->entries[fr.entry_index].next; + } else { + s->entries[fr.entry_prev].next = s->entries[fr.entry_index].next; + } + if (fr.entry_index == s->entries.count-1) { + array_pop(&s->entries); + return; + } + s->entries[fr.entry_index] = s->entries[s->entries.count-1]; + last = string_set__find(s, s->entries[fr.entry_index].key); + if (last.entry_prev >= 0) { + s->entries[last.entry_prev].next = fr.entry_index; + } else { + s->hashes[last.hash_index] = fr.entry_index; + } +} + +void string_set_remove(StringSet *s, String str) { + HashKey key = hash_string(str); + StringSetFindResult fr = string_set__find(s, key); + if (fr.entry_index >= 0) { + string_set__erase(s, fr); + } +} + +gb_inline void string_set_clear(StringSet *s) { + array_clear(&s->hashes); + array_clear(&s->entries); +} diff --git a/src/types.cpp b/src/types.cpp index fcfc91544..09fcc98b7 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2436,7 +2436,31 @@ gbString write_type_to_string(gbString str, Type *type) { break; case Type_Proc: - str = gb_string_appendc(str, "proc("); + str = gb_string_appendc(str, "proc"); + + switch (type->Proc.calling_convention) { + case ProcCC_Odin: + break; + case ProcCC_Contextless: + str = gb_string_appendc(str, " \"contextless\" "); + break; + case ProcCC_CDecl: + str = gb_string_appendc(str, " \"cdecl\" "); + break; + case ProcCC_StdCall: + str = gb_string_appendc(str, " \"stdcall\" "); + break; + case ProcCC_FastCall: + str = gb_string_appendc(str, " \"fastcall\" "); + break; + // case ProcCC_VectorCall: + // str = gb_string_appendc(str, " \"vectorcall\" "); + // break; + // case ProcCC_ClrCall: + // str = gb_string_appendc(str, " \"clrcall\" "); + // break; + } + str = gb_string_appendc(str, "("); if (type->Proc.params) { str = write_type_to_string(str, type->Proc.params); } @@ -2445,20 +2469,6 @@ gbString write_type_to_string(gbString str, Type *type) { str = gb_string_appendc(str, " -> "); str = write_type_to_string(str, type->Proc.results); } - switch (type->Proc.calling_convention) { - case ProcCC_Odin: - // str = gb_string_appendc(str, " #cc_odin"); - break; - case ProcCC_C: - str = gb_string_appendc(str, " #cc_c"); - break; - case ProcCC_Std: - str = gb_string_appendc(str, " #cc_std"); - break; - case ProcCC_Fast: - str = gb_string_appendc(str, " #cc_fast"); - break; - } break; case Type_BitField: