From c4c6975f1b36eb4848aacf81c7be3584c51f9ab6 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Tue, 11 Jul 2017 14:40:27 +0100 Subject: [PATCH] `cast(Type)expr`; Fix overloaded procedure determination on assignment --- core/fmt.odin | 4 +- src/check_decl.cpp | 14 +++++ src/check_expr.cpp | 143 ++++++++++++++++++++++++++++++++++++++++----- src/checker.cpp | 18 +++++- src/ir.cpp | 5 ++ src/parser.cpp | 25 ++++++-- src/tokenizer.cpp | 1 + src/types.cpp | 2 - 8 files changed, 184 insertions(+), 28 deletions(-) diff --git a/core/fmt.odin b/core/fmt.odin index ce57abd06..3a6948410 100644 --- a/core/fmt.odin +++ b/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) { 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) { if format[i] == ']' { diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 1ce518318..52f785ef6 100644 --- a/src/check_decl.cpp +++ b/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) { e->type = t_invalid; } return nullptr; } + + if (e->type == nullptr) { + // NOTE(bill): Use the type of the operand Type *t = operand->type; if (is_type_untyped(t)) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 0ab8e3571..215299f96 100644 --- a/src/check_expr.cpp +++ b/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); - 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; c->context.scope = scope; c->context.allow_polymorphic_types = true; + if (c->context.polymorphic_scope == nullptr) { + c->context.polymorphic_scope = scope; + } if (param_operands == nullptr) { // c->context.no_polymorphic_errors = false; } @@ -617,10 +622,61 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n 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)) { - 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 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) { // 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; - gb_string_free(expr_str); - gb_string_free(op_type_str); - gb_string_free(type_str); 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); if (t != nullptr && t != t_invalid) { 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); @@ -1380,6 +1441,26 @@ bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool c } 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: if (source->kind == Type_Record) { // 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 *operands) { 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 success = true; 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; Type *t = make_type_generic(c->allocator, 0, token.string); if (c->context.allow_polymorphic_types) { + Scope *ps = c->context.polymorphic_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; + add_entity(c, ps, ident, e); add_entity(c, s, ident, e); } *type = t; @@ -6057,6 +6149,8 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) { if (operand->mode == Addressing_Type) { Type *t = operand->type; gbString str = type_to_string(t); + defer (gb_string_free(str)); + operand->mode = Addressing_Invalid; isize arg_count = ce->args.count; switch (arg_count) { @@ -6076,7 +6170,6 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) { } break; } - gb_string_free(str); return Expr_Expr; } @@ -6892,6 +6985,26 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t } 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); check_expr_base(c, o, ue->expr, type_hint); if (o->mode == Addressing_Invalid) { diff --git a/src/checker.cpp b/src/checker.cpp index e47ad9aa2..e2133144f 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -250,6 +250,18 @@ void scope_reset(Scope *scope) { 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 { Scope * parent; @@ -269,12 +281,14 @@ struct CheckerContext { DeclInfo * decl; u32 stmt_state_flags; bool in_defer; // TODO(bill): Actually handle correctly - bool allow_polymorphic_types; - bool no_polymorphic_errors; String proc_name; Type * type_hint; DeclInfo * curr_proc_decl; AstNode * curr_foreign_library; + + bool allow_polymorphic_types; + bool no_polymorphic_errors; + Scope * polymorphic_scope; }; diff --git a/src/ir.cpp b/src/ir.cpp index 980788f3e..b11255cf1 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4625,6 +4625,11 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { } 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); switch (ue->op.kind) { case Token_And: diff --git a/src/parser.cpp b/src/parser.cpp index 8a55a31ea..6836e901f 100644 --- a/src/parser.cpp +++ b/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(TernaryExpr, "ternary expression", struct { AstNode *cond, *x, *y; }) \ 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(_StmtBegin, "", i32) \ 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_TernaryExpr: return ast_node_token(node->TernaryExpr.cond); 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_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; 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)) { 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]; if (s->kind == AstNode_GenDecl) { @@ -2581,6 +2587,13 @@ AstNode *parse_unary_expr(AstFile *f, bool lhs) { next_token(f); return ast_unary_expr(f, op, parse_unary_expr(f, lhs)); } 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); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index b1fff3305..4c66f17a2 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -114,6 +114,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \ TOKEN_KIND(Token_map, "map"), \ TOKEN_KIND(Token_static, "static"), \ TOKEN_KIND(Token_dynamic, "dynamic"), \ + TOKEN_KIND(Token_cast, "cast"), \ TOKEN_KIND(Token_using, "using"), \ TOKEN_KIND(Token_context, "context"), \ TOKEN_KIND(Token_push_context, "push_context"), \ diff --git a/src/types.cpp b/src/types.cpp index 786c4b13a..1604ec3f2 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -73,8 +73,6 @@ enum TypeRecordKind { TypeRecord_Struct, TypeRecord_RawUnion, - // TypeRecord_Union, // Tagged - // TypeRecord_Enum, TypeRecord_Count, };