Browse Source

using x in bar;

gingerBill 7 years ago
parent
commit
32a502d14e
4 changed files with 175 additions and 10 deletions
  1. 150 6
      src/check_stmt.cpp
  2. 1 0
      src/check_type.cpp
  3. 1 2
      src/checker.cpp
  4. 23 2
      src/parser.cpp

+ 150 - 6
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);

+ 1 - 0
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 *> entity_map = {}; // Key: String
 	map_init(&entity_map, c->tmp_allocator, 2*(et->fields.count));

+ 1 - 2
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 {

+ 23 - 2
src/parser.cpp

@@ -307,6 +307,12 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
 		Token token;   \
 		Array<AstNode *> list; \
 	}) \
+	AST_NODE_KIND(UsingInStmt, "using in statement",  struct { \
+		Token using_token;     \
+		Array<AstNode *> 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<AstNode *> list) {
 	result->UsingStmt.list  = list;
 	return result;
 }
+AstNode *ast_using_in_stmt(AstFile *f, Token using_token, Array<AstNode *> 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) {