Browse Source

`cast(Type)expr`; Fix overloaded procedure determination on assignment

Ginger Bill 8 years ago
parent
commit
c4c6975f1b
8 changed files with 184 additions and 28 deletions
  1. 1 3
      core/fmt.odin
  2. 14 0
      src/check_decl.cpp
  3. 128 15
      src/check_expr.cpp
  4. 16 2
      src/checker.cpp
  5. 5 0
      src/ir.cpp
  6. 19 6
      src/parser.cpp
  7. 1 0
      src/tokenizer.cpp
  8. 0 2
      src/types.cpp

+ 1 - 3
core/fmt.odin

@@ -363,9 +363,7 @@ _parse_int :: proc(s: string, offset: int) -> (result: int, offset: int, ok: boo
 
 
 _arg_number :: proc(fi: ^FmtInfo, arg_index: int, format: string, offset, arg_count: int) -> (index, offset: int, ok: bool) {
 _arg_number :: proc(fi: ^FmtInfo, arg_index: int, format: string, offset, arg_count: int) -> (index, offset: int, ok: bool) {
 	parse_arg_number :: proc(format: string) -> (int, int, bool) {
 	parse_arg_number :: proc(format: string) -> (int, int, bool) {
-		if len(format) < 3 {
-			return 0, 1, false;
-		}
+		if len(format) < 3 do return 0, 1, false;
 
 
 		for i in 1...len(format) {
 		for i in 1...len(format) {
 			if format[i] == ']' {
 			if format[i] == ']' {

+ 14 - 0
src/check_decl.cpp

@@ -23,13 +23,27 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
 		}
 		}
 
 
 
 
+		if (operand->mode == Addressing_Overload) {
+			if (e->type == nullptr) {
+				error(operand->expr, "Cannot determine type from overloaded procedure `%.*s`", LIT(operand->overload_entities[0]->token.string));
+			} else {
+				check_assignment(c, operand, e->type, str_lit("variable assignment"));
+				if (operand->mode != Addressing_Type) {
+					return operand->type;
+				}
+			}
+		}
+
 		if (e->type == nullptr) {
 		if (e->type == nullptr) {
 			e->type = t_invalid;
 			e->type = t_invalid;
 		}
 		}
 		return nullptr;
 		return nullptr;
 	}
 	}
 
 
+
+
 	if (e->type == nullptr) {
 	if (e->type == nullptr) {
+
 		// NOTE(bill): Use the type of the operand
 		// NOTE(bill): Use the type of the operand
 		Type *t = operand->type;
 		Type *t = operand->type;
 		if (is_type_untyped(t)) {
 		if (is_type_untyped(t)) {

+ 128 - 15
src/check_expr.cpp

@@ -192,7 +192,9 @@ bool find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, Typ
 
 
 
 
 	DeclInfo *old_decl = decl_info_of_entity(&c->info, base_entity);
 	DeclInfo *old_decl = decl_info_of_entity(&c->info, base_entity);
-	GB_ASSERT(old_decl != nullptr);
+	if (old_decl == nullptr) {
+		return false;
+	}
 
 
 
 
 
 
@@ -225,6 +227,9 @@ bool find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, Typ
 	scope->is_proc = true;
 	scope->is_proc = true;
 	c->context.scope = scope;
 	c->context.scope = scope;
 	c->context.allow_polymorphic_types = true;
 	c->context.allow_polymorphic_types = true;
+	if (c->context.polymorphic_scope == nullptr) {
+		c->context.polymorphic_scope = scope;
+	}
 	if (param_operands == nullptr) {
 	if (param_operands == nullptr) {
 		// c->context.no_polymorphic_errors = false;
 		// c->context.no_polymorphic_errors = false;
 	}
 	}
@@ -617,10 +622,61 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
 		return;
 		return;
 	}
 	}
 
 
+	if (operand->mode == Addressing_Overload) {
+		// GB_PANIC("HERE!\n");
+
+		gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
+		defer (gb_temp_arena_memory_end(tmp));
+
+		Entity **procs = operand->overload_entities;
+		isize overload_count = operand->overload_count;
+
+		bool good = false;
+		// NOTE(bill): These should be done
+		for (isize i = 0; i < overload_count; i++) {
+			Type *t = base_type(procs[i]->type);
+			if (t == t_invalid) {
+				continue;
+			}
+			Operand x = {};
+			x.mode = Addressing_Value;
+			x.type = t;
+			if (check_is_assignable_to(c, &x, type)) {
+				Entity *e = procs[i];
+				add_entity_use(c, operand->expr, e);
+				good = true;
+				break;
+			}
+		}
+
+		if (!good) {
+			gbString expr_str    = expr_to_string(operand->expr);
+			gbString op_type_str = type_to_string(operand->type);
+			gbString type_str    = type_to_string(type);
+
+			defer (gb_string_free(type_str));
+			defer (gb_string_free(op_type_str));
+			defer (gb_string_free(expr_str));
+
+			// TODO(bill): is this a good enough error message?
+			error(operand->expr,
+			      "Cannot assign overloaded procedure `%s` to `%s` in %.*s",
+			      expr_str,
+			      op_type_str,
+			      LIT(context_name));
+			operand->mode = Addressing_Invalid;
+		}
+		return;
+	}
+
 	if (!check_is_assignable_to(c, operand, type)) {
 	if (!check_is_assignable_to(c, operand, type)) {
-		gbString type_str    = type_to_string(type);
-		gbString op_type_str = type_to_string(operand->type);
 		gbString expr_str    = expr_to_string(operand->expr);
 		gbString expr_str    = expr_to_string(operand->expr);
+		gbString op_type_str = type_to_string(operand->type);
+		gbString type_str    = type_to_string(type);
+
+		defer (gb_string_free(type_str));
+		defer (gb_string_free(op_type_str));
+		defer (gb_string_free(expr_str));
 
 
 		if (operand->mode == Addressing_Builtin) {
 		if (operand->mode == Addressing_Builtin) {
 			// TODO(bill): is this a good enough error message?
 			// TODO(bill): is this a good enough error message?
@@ -647,9 +703,6 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
 		}
 		}
 		operand->mode = Addressing_Invalid;
 		operand->mode = Addressing_Invalid;
 
 
-		gb_string_free(expr_str);
-		gb_string_free(op_type_str);
-		gb_string_free(type_str);
 		return;
 		return;
 	}
 	}
 }
 }
@@ -1040,13 +1093,21 @@ void check_union_type(Checker *c, Type *named_type, Type *union_type, AstNode *n
 		Type *t = check_type(c, node);
 		Type *t = check_type(c, node);
 		if (t != nullptr && t != t_invalid) {
 		if (t != nullptr && t != t_invalid) {
 			bool ok = true;
 			bool ok = true;
-			for_array(j, variants) {
-				if (are_types_identical(t, variants[j])) {
-					ok = false;
-					gbString str = type_to_string(t);
-					error(node, "Duplicate variant type `%s`", str);
-					gb_string_free(str);
-					break;
+			t = default_type(t);
+			if (is_type_untyped(t)) {
+				ok = false;
+				gbString str = type_to_string(t);
+				error(node, "Invalid type in union `%s`", str);
+				gb_string_free(str);
+			} else {
+				for_array(j, variants) {
+					if (are_types_identical(t, variants[j])) {
+						ok = false;
+						gbString str = type_to_string(t);
+						error(node, "Duplicate variant type `%s`", str);
+						gb_string_free(str);
+						break;
+					}
 				}
 				}
 			}
 			}
 			if (ok) array_add(&variants, t);
 			if (ok) array_add(&variants, t);
@@ -1380,6 +1441,26 @@ bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool c
 		}
 		}
 		return false;
 		return false;
 
 
+	case Type_Enum:
+		return false;
+
+	case Type_Union:
+		if (source->kind == Type_Union) {
+			TypeUnion *x = &poly->Union;
+			TypeUnion *y = &source->Union;
+			if (x->variant_count != y->variant_count) {
+				return false;
+			}
+			for (isize i = 1; i < x->variant_count; i++) {
+				Type *a = x->variants[i];
+				Type *b = y->variants[i];
+				bool ok = is_polymorphic_type_assignable(c, a, b, false, modify_type);
+				if (!ok) return false;
+			}
+			return true;
+		}
+		return false;
+
 	case Type_Record:
 	case Type_Record:
 		if (source->kind == Type_Record) {
 		if (source->kind == Type_Record) {
 			// TODO(bill): Polymorphic type assignment
 			// TODO(bill): Polymorphic type assignment
@@ -2001,6 +2082,10 @@ bool abi_compat_return_by_value(gbAllocator a, ProcCallingConvention cc, Type *a
 bool check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node, Array<Operand> *operands) {
 bool check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node, Array<Operand> *operands) {
 	ast_node(pt, ProcType, proc_type_node);
 	ast_node(pt, ProcType, proc_type_node);
 
 
+	if (c->context.polymorphic_scope == nullptr && c->context.allow_polymorphic_types) {
+		c->context.polymorphic_scope = c->context.scope;
+	}
+
 	bool variadic = false;
 	bool variadic = false;
 	bool success = true;
 	bool success = true;
 	Type *params  = check_get_params(c, c->context.scope, pt->params, &variadic, &success, operands);
 	Type *params  = check_get_params(c, c->context.scope, pt->params, &variadic, &success, operands);
@@ -2444,9 +2529,16 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type)
 		Token token = ident->Ident.token;
 		Token token = ident->Ident.token;
 		Type *t = make_type_generic(c->allocator, 0, token.string);
 		Type *t = make_type_generic(c->allocator, 0, token.string);
 		if (c->context.allow_polymorphic_types) {
 		if (c->context.allow_polymorphic_types) {
+			Scope *ps = c->context.polymorphic_scope;
 			Scope *s = c->context.scope;
 			Scope *s = c->context.scope;
-			Entity *e = make_entity_type_name(c->allocator, s, token, t);
+			Scope *entity_scope = s;
+			if (ps != nullptr && ps != s) {
+				GB_ASSERT(is_scope_an_ancestor(ps, s) >= 0);
+				entity_scope = ps;
+			}
+			Entity *e = make_entity_type_name(c->allocator, entity_scope, token, t);
 			e->TypeName.is_type_alias = true;
 			e->TypeName.is_type_alias = true;
+			add_entity(c, ps, ident, e);
 			add_entity(c, s, ident, e);
 			add_entity(c, s, ident, e);
 		}
 		}
 		*type = t;
 		*type = t;
@@ -6057,6 +6149,8 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
 	if (operand->mode == Addressing_Type) {
 	if (operand->mode == Addressing_Type) {
 		Type *t = operand->type;
 		Type *t = operand->type;
 		gbString str = type_to_string(t);
 		gbString str = type_to_string(t);
+		defer (gb_string_free(str));
+
 		operand->mode = Addressing_Invalid;
 		operand->mode = Addressing_Invalid;
 		isize arg_count = ce->args.count;
 		isize arg_count = ce->args.count;
 		switch (arg_count) {
 		switch (arg_count) {
@@ -6076,7 +6170,6 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
 		} break;
 		} break;
 		}
 		}
 
 
-		gb_string_free(str);
 		return Expr_Expr;
 		return Expr_Expr;
 	}
 	}
 
 
@@ -6892,6 +6985,26 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t
 		}
 		}
 	case_end;
 	case_end;
 
 
+	case_ast_node(tc, TypeCast, node);
+		check_expr_or_type(c, o, tc->type);
+		if (o->mode != Addressing_Type) {
+			gbString str = expr_to_string(tc->type);
+			error(tc->type, "Expected a type, got %s", str);
+			gb_string_free(str);
+			o->mode = Addressing_Invalid;
+		}
+		if (o->mode == Addressing_Invalid) {
+			o->expr = node;
+			return kind;
+		}
+		Type *type = o->type;
+		check_expr_base(c, o, tc->expr, type);
+		if (o->mode != Addressing_Invalid) {
+			check_cast(c, o, type);
+		}
+		return Expr_Expr;
+	case_end;
+
 	case_ast_node(ue, UnaryExpr, node);
 	case_ast_node(ue, UnaryExpr, node);
 		check_expr_base(c, o, ue->expr, type_hint);
 		check_expr_base(c, o, ue->expr, type_hint);
 		if (o->mode == Addressing_Invalid) {
 		if (o->mode == Addressing_Invalid) {

+ 16 - 2
src/checker.cpp

@@ -250,6 +250,18 @@ void scope_reset(Scope *scope) {
 	array_clear(&scope->imported);
 	array_clear(&scope->imported);
 }
 }
 
 
+i32 is_scope_an_ancestor(Scope *parent, Scope *child) {
+	isize i = 0;
+	while (child != nullptr) {
+		if (parent == child) {
+			return true;
+		}
+		child = child->parent;
+		i++;
+	}
+	return -1;
+}
+
 
 
 struct DelayedDecl {
 struct DelayedDecl {
 	Scope *  parent;
 	Scope *  parent;
@@ -269,12 +281,14 @@ struct CheckerContext {
 	DeclInfo * decl;
 	DeclInfo * decl;
 	u32        stmt_state_flags;
 	u32        stmt_state_flags;
 	bool       in_defer; // TODO(bill): Actually handle correctly
 	bool       in_defer; // TODO(bill): Actually handle correctly
-	bool       allow_polymorphic_types;
-	bool       no_polymorphic_errors;
 	String     proc_name;
 	String     proc_name;
 	Type *     type_hint;
 	Type *     type_hint;
 	DeclInfo * curr_proc_decl;
 	DeclInfo * curr_proc_decl;
 	AstNode *  curr_foreign_library;
 	AstNode *  curr_foreign_library;
+
+	bool       allow_polymorphic_types;
+	bool       no_polymorphic_errors;
+	Scope *    polymorphic_scope;
 };
 };
 
 
 
 

+ 5 - 0
src/ir.cpp

@@ -4625,6 +4625,11 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 		}
 		}
 	case_end;
 	case_end;
 
 
+	case_ast_node(tc, TypeCast, expr);
+		irValue *e = ir_build_expr(proc, tc->expr);
+		return ir_emit_conv(proc, e, tv.type);
+	case_end;
+
 	case_ast_node(ue, UnaryExpr, expr);
 	case_ast_node(ue, UnaryExpr, expr);
 		switch (ue->op.kind) {
 		switch (ue->op.kind) {
 		case Token_And:
 		case Token_And:

+ 19 - 6
src/parser.cpp

@@ -201,6 +201,7 @@ AST_NODE_KIND(_ExprBegin,  "",  i32) \
 	AST_NODE_KIND(FieldValue,    "field value",         struct { Token eq; AstNode *field, *value; }) \
 	AST_NODE_KIND(FieldValue,    "field value",         struct { Token eq; AstNode *field, *value; }) \
 	AST_NODE_KIND(TernaryExpr,   "ternary expression",  struct { AstNode *cond, *x, *y; }) \
 	AST_NODE_KIND(TernaryExpr,   "ternary expression",  struct { AstNode *cond, *x, *y; }) \
 	AST_NODE_KIND(TypeAssertion, "type assertion",      struct { AstNode *expr; Token dot; AstNode *type; }) \
 	AST_NODE_KIND(TypeAssertion, "type assertion",      struct { AstNode *expr; Token dot; AstNode *type; }) \
+	AST_NODE_KIND(TypeCast,      "type cast",           struct { Token token; AstNode *type, *expr; }) \
 AST_NODE_KIND(_ExprEnd,       "", i32) \
 AST_NODE_KIND(_ExprEnd,       "", i32) \
 AST_NODE_KIND(_StmtBegin,     "", i32) \
 AST_NODE_KIND(_StmtBegin,     "", i32) \
 	AST_NODE_KIND(BadStmt,    "bad statement",                 struct { Token begin, end; }) \
 	AST_NODE_KIND(BadStmt,    "bad statement",                 struct { Token begin, end; }) \
@@ -542,6 +543,7 @@ Token ast_node_token(AstNode *node) {
 	case AstNode_DerefExpr:     return node->DerefExpr.op;
 	case AstNode_DerefExpr:     return node->DerefExpr.op;
 	case AstNode_TernaryExpr:   return ast_node_token(node->TernaryExpr.cond);
 	case AstNode_TernaryExpr:   return ast_node_token(node->TernaryExpr.cond);
 	case AstNode_TypeAssertion: return ast_node_token(node->TypeAssertion.expr);
 	case AstNode_TypeAssertion: return ast_node_token(node->TypeAssertion.expr);
+	case AstNode_TypeCast:      return node->TypeCast.token;
 
 
 	case AstNode_BadStmt:       return node->BadStmt.begin;
 	case AstNode_BadStmt:       return node->BadStmt.begin;
 	case AstNode_EmptyStmt:     return node->EmptyStmt.token;
 	case AstNode_EmptyStmt:     return node->EmptyStmt.token;
@@ -1146,6 +1148,14 @@ AstNode *ast_type_assertion(AstFile *f, AstNode *expr, Token dot, AstNode *type)
 	result->TypeAssertion.type = type;
 	result->TypeAssertion.type = type;
 	return result;
 	return result;
 }
 }
+AstNode *ast_type_cast(AstFile *f, Token token, AstNode *type, AstNode *expr) {
+	AstNode *result = make_ast_node(f, AstNode_TypeCast);
+	result->TypeCast.token = token;
+	result->TypeCast.type  = type;
+	result->TypeCast.expr  = expr;
+	return result;
+}
+
 
 
 
 
 
 
@@ -1875,12 +1885,8 @@ void expect_semicolon(AstFile *f, AstNode *s) {
 			if (is_semicolon_optional_for_node(f, s)) {
 			if (is_semicolon_optional_for_node(f, s)) {
 				return;
 				return;
 			}
 			}
-		} else {
-			if (s->kind == AstNode_ValueDecl) {
-				if (f->curr_token.kind == Token_CloseBrace) {
-					return;
-				}
-			}
+		} else if (f->curr_token.kind == Token_CloseBrace) {
+			return;
 		}
 		}
 		String node_string = ast_node_strings[s->kind];
 		String node_string = ast_node_strings[s->kind];
 		if (s->kind == AstNode_GenDecl) {
 		if (s->kind == AstNode_GenDecl) {
@@ -2581,6 +2587,13 @@ AstNode *parse_unary_expr(AstFile *f, bool lhs) {
 		next_token(f);
 		next_token(f);
 		return ast_unary_expr(f, op, parse_unary_expr(f, lhs));
 		return ast_unary_expr(f, op, parse_unary_expr(f, lhs));
 	} break;
 	} break;
+	case Token_cast: {
+		Token token = expect_token(f, Token_cast);
+		Token open  = expect_token_after(f, Token_OpenParen, "cast");
+		AstNode *type = parse_type(f);
+		Token close = expect_token(f, Token_CloseParen);
+		return ast_type_cast(f, token, type, parse_unary_expr(f, lhs));
+	} break;
 	}
 	}
 
 
 	AstNode *operand = parse_operand(f, lhs);
 	AstNode *operand = parse_operand(f, lhs);

+ 1 - 0
src/tokenizer.cpp

@@ -114,6 +114,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
 	TOKEN_KIND(Token_map,                    "map"),                    \
 	TOKEN_KIND(Token_map,                    "map"),                    \
 	TOKEN_KIND(Token_static,                 "static"),                 \
 	TOKEN_KIND(Token_static,                 "static"),                 \
 	TOKEN_KIND(Token_dynamic,                "dynamic"),                \
 	TOKEN_KIND(Token_dynamic,                "dynamic"),                \
+	TOKEN_KIND(Token_cast,                   "cast"),                   \
 	TOKEN_KIND(Token_using,                  "using"),                  \
 	TOKEN_KIND(Token_using,                  "using"),                  \
 	TOKEN_KIND(Token_context,                "context"),                \
 	TOKEN_KIND(Token_context,                "context"),                \
 	TOKEN_KIND(Token_push_context,           "push_context"),           \
 	TOKEN_KIND(Token_push_context,           "push_context"),           \

+ 0 - 2
src/types.cpp

@@ -73,8 +73,6 @@ enum TypeRecordKind {
 
 
 	TypeRecord_Struct,
 	TypeRecord_Struct,
 	TypeRecord_RawUnion,
 	TypeRecord_RawUnion,
-	// TypeRecord_Union, // Tagged
-	// TypeRecord_Enum,
 
 
 	TypeRecord_Count,
 	TypeRecord_Count,
 };
 };