diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 4f5fe3ebe..813028ab1 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -429,6 +429,23 @@ void init_entity_foreign_library(Checker *c, Entity *e) { } } +String handle_link_name(Checker *c, Token token, String link_name, String link_prefix, bool link_prefix_overridden) { + if (link_prefix.len > 0) { + if (link_name.len > 0 && !link_prefix_overridden) { + error(token, "`link_name` and `link_prefix` cannot be used together"); + } else { + isize len = link_prefix.len + token.string.len; + u8 *name = gb_alloc_array(c->allocator, u8, len+1); + gb_memmove(name, &link_prefix[0], link_prefix.len); + gb_memmove(name+link_prefix.len, &token.string[0], token.string.len); + name[len] = 0; + + link_name = make_string(name, len); + } + } + return link_name; +} + void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { GB_ASSERT(e->type == nullptr); if (d->proc_lit->kind != AstNode_ProcLit) { @@ -462,6 +479,8 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { bool is_require_results = (pl->tags & ProcTag_require_results) != 0; String link_name = {}; + String link_prefix = e->Procedure.link_prefix; + bool link_prefix_overridden = false; if (d != nullptr && d->attributes.count > 0) { @@ -517,6 +536,18 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { } else { error(elem, "Expected a string value for `%.*s`", LIT(name)); } + } else if (name == "link_prefix") { + if (ev.kind == ExactValue_String) { + if (link_prefix.len > 0) { + link_prefix_overridden = true; + } + link_prefix = ev.value_string; + if (!is_foreign_name_valid(link_prefix)) { + error(elem, "Invalid link prefix: %.*s", LIT(link_prefix)); + } + } else { + error(elem, "Expected a string value for `%.*s`", LIT(name)); + } } else { error(elem, "Unknown attribute element name `%.*s`", LIT(name)); } @@ -525,8 +556,7 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) { } - - + link_name = handle_link_name(c, e->token, link_name, link_prefix, link_prefix_overridden); if (d->scope->file != nullptr && e->token.string == "main") { if (pt->param_count != 0 || @@ -671,7 +701,9 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count e->flags |= EntityFlag_Visited; + String link_prefix = e->Variable.link_prefix; String link_name = {}; + bool link_prefix_overridden = false; DeclInfo *decl = decl_info_of_entity(&c->info, e); if (decl != nullptr && decl->attributes.count > 0) { @@ -728,6 +760,30 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count } else { error(elem, "Expected a string value for `%.*s`", LIT(name)); } + } else if (name == "thread_local") { + if (ev.kind == ExactValue_Invalid) { + if (!e->scope->is_file) { + error(elem, "Only a variable at file scope can be thread local"); + } else if (init_expr_list.count > 0) { + error(elem, "A thread local variable declaration cannot have initialization values"); + } else { + e->Variable.is_thread_local = true; + } + } else { + error(elem, "Expected no value for `%.*s`", LIT(name)); + } + } else if (name == "link_prefix") { + if (ev.kind == ExactValue_String) { + if (link_prefix.len > 0) { + link_prefix_overridden = true; + } + link_prefix = ev.value_string; + if (!is_foreign_name_valid(link_prefix)) { + error(elem, "Invalid link prefix: %.*s", LIT(link_prefix)); + } + } else { + error(elem, "Expected a string value for `%.*s`", LIT(name)); + } } else { error(elem, "Unknown attribute element name `%.*s`", LIT(name)); } @@ -735,6 +791,9 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count } } + link_name = handle_link_name(c, e->token, link_name, link_prefix, link_prefix_overridden); + + String context_name = str_lit("variable declaration"); if (type_expr != nullptr) { diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 6bde1abb3..04308d40b 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1692,9 +1692,58 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { Entity **entities = gb_alloc_array(c->allocator, Entity *, vd->names.count); isize entity_count = 0; - if (vd->flags & VarDeclFlag_thread_local) { - vd->flags &= ~VarDeclFlag_thread_local; - error(node, "`thread_local` may only be applied to a variable declaration"); + if (vd->attributes.count > 0) { + StringSet set = {}; + string_set_init(&set, heap_allocator()); + defer (string_set_destroy(&set)); + + for_array(i, vd->attributes) { + AstNode *attr = vd->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 == "thread_local") { + error(elem, "Variable within a procedure cannot be thread local"); + } else { + error(elem, "Unknown attribute element name `%.*s`", LIT(name)); + } + } + } } for_array(i, vd->names) { diff --git a/src/checker.cpp b/src/checker.cpp index 4d7b7e83a..5e77dc90f 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -424,6 +424,7 @@ struct CheckerContext { DeclInfo * curr_proc_decl; AstNode * curr_foreign_library; ProcCallingConvention default_foreign_cc; + String foreign_link_prefix; bool in_foreign_export; bool collect_delayed_decls; @@ -1920,6 +1921,17 @@ void check_foreign_block_decl_attributes(Checker *c, AstNodeForeignBlockDecl *fb } else { error(elem, "Expected a string value for `%.*s`", LIT(name)); } + } else if (name == "link_prefix") { + if (ev.kind == ExactValue_String) { + String link_prefix = ev.value_string; + if (!is_foreign_name_valid(link_prefix)) { + error(elem, "Invalid link prefix: `%.*s`\n", LIT(link_prefix)); + } else { + c->context.foreign_link_prefix = link_prefix; + } + } else { + error(elem, "Expected a string value for `%.*s`", LIT(name)); + } } else { error(elem, "Unknown attribute element name `%.*s`", LIT(name)); } @@ -2024,11 +2036,6 @@ void check_collect_value_decl(Checker *c, AstNode *decl) { di->type_expr = vd->type; di->init_expr = vd->values[0]; di->init_expr_list = vd->values; - - - if (vd->flags & VarDeclFlag_thread_local) { - error(decl, "#thread_local variable declarations cannot have initialization values"); - } } @@ -2044,7 +2051,6 @@ void check_collect_value_decl(Checker *c, AstNode *decl) { continue; } Entity *e = make_entity_variable(c->allocator, c->context.scope, name->Ident.token, nullptr, false); - e->Variable.is_thread_local = (vd->flags & VarDeclFlag_thread_local) != 0; e->identifier = name; if (vd->flags & VarDeclFlag_using) { @@ -2058,6 +2064,8 @@ void check_collect_value_decl(Checker *c, AstNode *decl) { e->Variable.is_foreign = true; e->Variable.foreign_library_ident = fl; + e->Variable.link_prefix = c->context.foreign_link_prefix; + } else if (c->context.in_foreign_export) { e->Variable.is_export = true; } @@ -2131,6 +2139,8 @@ void check_collect_value_decl(Checker *c, AstNode *decl) { cc = c->context.default_foreign_cc; } } + e->Procedure.link_prefix = c->context.foreign_link_prefix; + GB_ASSERT(cc != ProcCC_Invalid); pl->type->ProcType.calling_convention = cc; diff --git a/src/entity.cpp b/src/entity.cpp index de0922650..b84d9a7e0 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -90,6 +90,7 @@ struct Entity { Entity * foreign_library; AstNode * foreign_library_ident; String link_name; + String link_prefix; } Variable; struct { bool is_type_alias; @@ -98,6 +99,7 @@ struct Entity { struct { OverloadKind overload_kind; String link_name; + String link_prefix; u64 tags; bool is_export; bool is_foreign; diff --git a/src/ir.cpp b/src/ir.cpp index 565329ef1..e554666f2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -8176,14 +8176,15 @@ void ir_gen_tree(irGen *s) { if (decl == nullptr) { continue; } + GB_ASSERT(e->kind == Entity_Variable); bool is_foreign = e->Variable.is_foreign; bool is_export = e->Variable.is_export; + bool no_name_mangle = e->scope->is_global || e->Variable.link_name.len > 0 || is_foreign || is_export; String name = e->token.string; - String original_name = name; - if (!e->scope->is_global && !(is_foreign || is_export)) { + if (!no_name_mangle) { name = ir_mangle_name(s, e->token.pos.file, e); } ir_add_entity_name(m, e, name); diff --git a/src/parser.cpp b/src/parser.cpp index 4595909d9..72c689674 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -116,8 +116,7 @@ enum ProcCallingConvention { }; enum VarDeclFlag { - VarDeclFlag_using = 1<<0, - VarDeclFlag_thread_local = 1<<1, + VarDeclFlag_using = 1<<0, }; enum StmtStateFlag { @@ -4650,22 +4649,6 @@ AstNode *parse_stmt(AstFile *f) { } expect_semicolon(f, s); return s; - } else if (tag == "thread_local") { - AstNode *s = parse_stmt(f); - - if (s->kind == AstNode_ValueDecl) { - if (!s->ValueDecl.is_mutable) { - syntax_error(token, "`thread_local` may only be applied to variable declarations"); - } - if (f->curr_proc != nullptr) { - syntax_error(token, "`thread_local` is only allowed at the file scope"); - } else { - s->ValueDecl.flags |= VarDeclFlag_thread_local; - } - return s; - } - syntax_error(token, "`thread_local` may only be applied to a variable declaration"); - return ast_bad_stmt(f, token, f->curr_token); } else if (tag == "bounds_check") { s = parse_stmt(f); s->stmt_state_flags |= StmtStateFlag_bounds_check;