diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 31caca15a..2f82d98e8 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -522,27 +522,25 @@ bool check_using_stmt_entity(Checker *c, AstNodeUsingStmt *us, AstNode *expr, bo case Entity_Variable: { Type *t = base_type(type_deref(e->type)); - if (is_type_struct(t) || is_type_raw_union(t) || is_type_union(t)) { + if (t->kind == Type_Struct) { // TODO(bill): Make it work for unions too Scope *found = scope_of_node(&c->info, t->Struct.node); for_array(i, found->elements.entries) { Entity *f = found->elements.entries[i].value; if (f->kind == Entity_Variable) { Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type); - // if (is_selector) { - uvar->using_expr = expr; - // } + uvar->using_expr = expr; Entity *prev = scope_insert_entity(c->context.scope, uvar); if (prev != nullptr) { gbString expr_str = expr_to_string(expr); - error(us->token, "Namespace collision while 'using' '%s' of: %.*s", expr_str, LIT(prev->token.string)); + error(us->token, "Namespace collision while using '%s' of: '%.*s'", expr_str, LIT(prev->token.string)); gb_string_free(expr_str); return false; } } } } else { - error(us->token, "'using' can only be applied to variables of type `struct`"); + error(us->token, "'using' can only be applied to variables of type 'struct'"); return false; } @@ -554,6 +552,7 @@ bool check_using_stmt_entity(Checker *c, AstNodeUsingStmt *us, AstNode *expr, bo break; case Entity_Procedure: + case Entity_ProcGroup: case Entity_Builtin: error(us->token, "'using' cannot be applied to a procedure"); break; @@ -1646,6 +1645,151 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { } case_end; + case_ast_node(uis, UsingInStmt, node); + if (uis->list.count == 0) { + error(node, "Empty 'using' list"); + return; + } + AstNode *expr = uis->expr; + Entity *e = nullptr; + Operand o = {}; + if (expr->kind == AstNode_Ident) { + e = check_ident(c, &o, expr, nullptr, nullptr, true); + } else if (expr->kind == AstNode_SelectorExpr) { + e = check_selector(c, &o, expr, nullptr); + } + if (e == nullptr) { + error(expr, "'using' applied to an unknown entity"); + return; + } + add_entity_use(c, expr, e); + + + switch (e->kind) { + case Entity_TypeName: { + Type *t = base_type(e->type); + if (t->kind == Type_Enum) { + GB_ASSERT(t->Enum.scope != nullptr); + for_array(list_index, uis->list) { + AstNode *node = uis->list[list_index]; + ast_node(ident, Ident, node); + String name = ident->token.string; + Entity *f = scope_lookup_entity(t->Enum.scope, name); + + if (f == nullptr || !is_entity_exported(f)) { + if (is_blank_ident(name)) { + error(node, "'_' cannot be used as a value"); + } else { + error(node, "Undeclared name in this enumeration: '%.*s'", LIT(name)); + } + continue; + } + + add_entity_use(c, node, f); + add_entity(c, c->context.scope, node, f); + } + } else { + error(node, "'using' can be only applied to enum type entities"); + } + + break; + } + + case Entity_ImportName: { + Scope *scope = e->ImportName.scope; + for_array(list_index, uis->list) { + AstNode *node = uis->list[list_index]; + ast_node(ident, Ident, node); + String name = ident->token.string; + + Entity *f = scope_lookup_entity(scope, name); + if (f == nullptr) { + if (is_blank_ident(name)) { + error(node, "'_' cannot be used as a value"); + } else { + error(node, "Undeclared name in this import name: '%.*s'", LIT(name)); + } + continue; + } + + bool implicit_is_found = ptr_set_exists(&scope->implicit, f); + if (is_entity_exported(f) && !implicit_is_found) { + add_entity_use(c, node, f); + add_entity(c, c->context.scope, node, f); + } else { + error(node, "'%.*s' is exported from '%.*s'", LIT(f->token.string), LIT(e->token.string)); + continue; + } + } + + break; + } + + case Entity_Variable: { + Type *t = base_type(type_deref(e->type)); + if (t->kind == Type_Struct) { + // TODO(bill): Make it work for unions too + Scope *found = scope_of_node(&c->info, t->Struct.node); + for_array(list_index, uis->list) { + AstNode *node = uis->list[list_index]; + ast_node(ident, Ident, node); + String name = ident->token.string; + + Entity *f = scope_lookup_entity(found, name); + if (f == nullptr || f->kind != Entity_Variable) { + if (is_blank_ident(name)) { + error(node, "'_' cannot be used as a value"); + } else { + error(node, "Undeclared name in this variable: '%.*s'", LIT(name)); + } + continue; + } + + Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type); + uvar->using_expr = expr; + Entity *prev = scope_insert_entity(c->context.scope, uvar); + if (prev != nullptr) { + gbString expr_str = expr_to_string(expr); + error(node, "Namespace collision while using '%s' of: '%.*s'", expr_str, LIT(prev->token.string)); + gb_string_free(expr_str); + continue; + } + } + } else { + error(node, "'using' can only be applied to variables of type `struct`"); + return; + } + + break; + } + + case Entity_Constant: + error(node, "'using' cannot be applied to a constant"); + break; + + case Entity_Procedure: + case Entity_ProcGroup: + case Entity_Builtin: + error(node, "'using' cannot be applied to a procedure"); + break; + + case Entity_Nil: + error(node, "'using' cannot be applied to 'nil'"); + break; + + case Entity_Label: + error(node, "'using' cannot be applied to a label"); + break; + + case Entity_Invalid: + error(node, "'using' cannot be applied to an invalid entity"); + break; + + default: + GB_PANIC("TODO(bill): 'using' other expressions?"); + } + case_end; + case_ast_node(pa, PushContext, node); diff --git a/src/check_type.cpp b/src/check_type.cpp index 8600f1a55..0bee3171f 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -697,6 +697,7 @@ void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *nod // NOTE(bill): Must be up here for the 'check_init_constant' system enum_type->Enum.base_type = base_type; + enum_type->Enum.scope = c->context.scope; Map entity_map = {}; // Key: String map_init(&entity_map, c->tmp_allocator, 2*(et->fields.count)); diff --git a/src/checker.cpp b/src/checker.cpp index d8ada0bfc..47d550070 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2688,8 +2688,7 @@ void check_add_import_decl(Checker *c, AstNodeImportDecl *id) { bool implicit_is_found = ptr_set_exists(&scope->implicit, e); if (is_entity_exported(e) && !implicit_is_found) { - Entity *prev = scope_lookup_entity(parent_scope, e->token.string); - // if (prev) gb_printf_err("%.*s\n", LIT(prev->token.string)); + add_entity_use(c, node, e); bool ok = add_entity(c, parent_scope, e->identifier, e); if (ok) ptr_set_add(&parent_scope->implicit, e); } else { diff --git a/src/parser.cpp b/src/parser.cpp index 8c96b1fb2..e8dbd1a05 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -307,6 +307,12 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \ Token token; \ Array list; \ }) \ + AST_NODE_KIND(UsingInStmt, "using in statement", struct { \ + Token using_token; \ + Array list; \ + Token in_token; \ + AstNode *expr; \ + }) \ AST_NODE_KIND(AsmOperand, "assembly operand", struct { \ Token string; \ AstNode *operand; \ @@ -598,6 +604,7 @@ Token ast_node_token(AstNode *node) { case AstNode_DeferStmt: return node->DeferStmt.token; case AstNode_BranchStmt: return node->BranchStmt.token; case AstNode_UsingStmt: return node->UsingStmt.token; + case AstNode_UsingInStmt: return node->UsingInStmt.using_token; case AstNode_AsmStmt: return node->AsmStmt.token; case AstNode_PushContext: return node->PushContext.token; @@ -827,6 +834,10 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) { case AstNode_UsingStmt: n->UsingStmt.list = clone_ast_node_array(a, n->UsingStmt.list); break; + case AstNode_UsingInStmt: + n->UsingInStmt.list = clone_ast_node_array(a, n->UsingInStmt.list); + n->UsingInStmt.expr = clone_ast_node(a, n->UsingInStmt.expr); + break; case AstNode_AsmOperand: n->AsmOperand.operand = clone_ast_node(a, n->AsmOperand.operand); break; @@ -1341,6 +1352,14 @@ AstNode *ast_using_stmt(AstFile *f, Token token, Array list) { result->UsingStmt.list = list; return result; } +AstNode *ast_using_in_stmt(AstFile *f, Token using_token, Array list, Token in_token, AstNode *expr) { + AstNode *result = make_ast_node(f, AstNode_UsingInStmt); + result->UsingInStmt.using_token = using_token; + result->UsingInStmt.list = list; + result->UsingInStmt.in_token = in_token; + result->UsingInStmt.expr = expr; + return result; +} AstNode *ast_asm_operand(AstFile *f, Token string, AstNode *operand) { @@ -4535,8 +4554,10 @@ AstNode *parse_stmt(AstFile *f) { } return export_decl; } - syntax_error(token, "Illegal use of 'using' statement"); - return ast_bad_stmt(f, token, f->curr_token); + + AstNode *expr = parse_expr(f, true); + expect_semicolon(f, expr); + return ast_using_in_stmt(f, token, list, in_token, expr); } if (f->curr_token.kind != Token_Colon) {