From 9a8759efefa24f5ae1e6d91b67dca72357a99ff9 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Mon, 17 Jul 2017 15:08:36 +0100 Subject: Polymorphic type specialization for procedures --- src/check_expr.cpp | 147 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 95 insertions(+), 52 deletions(-) (limited to 'src/check_expr.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 239d9d482..9e7f66330 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -156,6 +156,7 @@ bool find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, Typ // // /////////////////////////////////////////////////////////////////////////////// + if (base_entity == nullptr) { return false; } @@ -163,6 +164,7 @@ bool find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, Typ if (!is_type_proc(base_entity->type)) { return false; } + String name = base_entity->token.string; Type *src = base_type(base_entity->type); Type *dst = nullptr; @@ -372,6 +374,9 @@ bool find_or_generate_polymorphic_procedure_from_parameters(Checker *c, Entity * return find_or_generate_polymorphic_procedure(c, base_entity, nullptr, operands, poly_proc_data, false); } +bool check_type_specialization_to(Checker *c, Type *type, Type *specialization, bool modify_type = false); +bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool compound, bool modify_type); + i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) { if (operand->mode == Addressing_Invalid || @@ -467,10 +472,11 @@ i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) { } } +#if 0 if (are_types_identical(dst, src) && (!is_type_named(dst) || !is_type_named(src))) { return 1; } - +#endif if (is_type_bit_field_value(operand->type) && is_type_integer(type)) { Type *bfv = base_type(operand->type); @@ -504,6 +510,13 @@ i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) { } #endif + if (is_type_polymorphic(dst) && !is_type_polymorphic(src)) { + bool modify_type = !c->context.no_polymorphic_errors; + if (is_polymorphic_type_assignable(c, type, s, false, modify_type)) { + return 2; + } + } + if (is_type_union(dst)) { for (isize i = 0; i < dst->Union.variant_count; i++) { Type *vt = dst->Union.variants[i]; @@ -679,21 +692,21 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n 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? + switch (operand->mode) { + case Addressing_Builtin: // TODO(bill): Actually allow built in procedures to be passed around and thus be created on use error(operand->expr, "Cannot assign built-in procedure `%s` in %.*s", expr_str, LIT(context_name)); - } else if (operand->mode == Addressing_Type) { - // TODO(bill): is this a good enough error message? - // TODO(bill): Actually allow built in procedures to be passed around and thus be created on use + break; + case Addressing_Type: error(operand->expr, "Cannot assign `%s` which is a type in %.*s", op_type_str, LIT(context_name)); - } else { + break; + default: // TODO(bill): is this a good enough error message? error(operand->expr, "Cannot assign value `%s` of type `%s` to `%s` in %.*s", @@ -701,6 +714,7 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n op_type_str, type_str, LIT(context_name)); + break; } operand->mode = Addressing_Invalid; @@ -1087,7 +1101,8 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node, Arraykind != s->kind) { return false; } + // gb_printf_err("#1 %s %s\n", type_to_string(type), type_to_string(specialization)); + if (t->kind != Type_Record) { + return false; + } + bool show_stuff = false && modify_type; + + if (show_stuff) gb_printf_err("#1 %s %s\n", type_to_string(type), type_to_string(specialization)); + if (t->Record.polymorphic_parent == specialization) { + return true; + } + if (show_stuff) gb_printf_err("#2 %s %s\n", type_to_string(t->Record.polymorphic_parent), type_to_string(specialization)); + if (t->Record.polymorphic_parent == s->Record.polymorphic_parent) { + GB_ASSERT(s->Record.polymorphic_params != nullptr); + GB_ASSERT(t->Record.polymorphic_params != nullptr); + + if (show_stuff) gb_printf_err("#3 %s -> %s\n", type_to_string(type), type_to_string(specialization)); + + TypeTuple *s_tuple = &s->Record.polymorphic_params->Tuple; + TypeTuple *t_tuple = &t->Record.polymorphic_params->Tuple; + GB_ASSERT(t_tuple->variable_count == s_tuple->variable_count); + for (isize i = 0; i < s_tuple->variable_count; i++) { + Entity *s_e = s_tuple->variables[i]; + Entity *t_e = t_tuple->variables[i]; + Type *st = s_e->type; + Type *tt = t_e->type; + if (show_stuff) gb_printf_err("\t@ %s -> %s\n", type_to_string(st), type_to_string(tt)); + if (show_stuff && base_type(st)->kind == Type_Generic) gb_printf_err("\t$%.*s\n", LIT(base_type(st)->Generic.name)); + bool ok = is_polymorphic_type_assignable(c, st, tt, true, modify_type); + if (show_stuff) gb_printf_err("\t$ %s -> %s\n\n", type_to_string(st), type_to_string(tt)); + } + + + if (modify_type) { + // NOTE(bill): This is needed in order to change the actual type but still have the types defined within it + gb_memmove(specialization, type, gb_size_of(Type)); + } - source = base_type(source); - GB_ASSERT(source->kind == Type_Record && source->Record.kind == TypeRecord_Struct); + return true; + } - return are_types_identical(source->Record.polymorphic_parent, specific); + return false; } bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool compound, bool modify_type) { @@ -1565,17 +1620,21 @@ bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool c o.type = source; switch (poly->kind) { case Type_Basic: - if (compound) return are_types_identical(poly, source); + if (compound) return are_types_identical(source, poly); return check_is_assignable_to(c, &o, poly); - case Type_Named: + case Type_Named: { + if (check_type_specialization_to(c, source, poly, modify_type)) { + return true; + } if (compound) return are_types_identical(poly, source); return check_is_assignable_to(c, &o, poly); + } case Type_Generic: { - if (poly->Generic.specific != nullptr) { - Type *s = poly->Generic.specific; - if (!is_polymorphic_type_assignable_to_specific(c, source, s)) { + if (poly->Generic.specialized != nullptr) { + Type *s = poly->Generic.specialized; + if (!check_type_specialization_to(c, source, s, modify_type)) { return false; } } @@ -1587,7 +1646,7 @@ bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool c } case Type_Pointer: if (source->kind == Type_Pointer) { - return is_polymorphic_type_assignable(c, poly->Pointer.elem, source->Atomic.elem, true, modify_type); + return is_polymorphic_type_assignable(c, poly->Pointer.elem, source->Pointer.elem, true, modify_type); } return false; case Type_Atomic: @@ -1640,7 +1699,6 @@ bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool c case Type_Record: if (source->kind == Type_Record) { - // TODO(bill): Polymorphic type assignment // return check_is_assignable_to(c, &o, poly); } return false; @@ -1718,33 +1776,6 @@ Type *determine_type_from_polymorphic(Checker *c, Type *poly_type, Operand opera return t_invalid; } -bool check_type_specialization_to(Checker *c, Type *type, Type *specialization) { - if (type == nullptr || - type == t_invalid) { - return true; - } - - Type *t = base_type(type); - Type *s = base_type(specialization); - if (t->kind != s->kind) { - return false; - } - // gb_printf_err("#1 %s %s\n", type_to_string(type), type_to_string(specialization)); - if (t->kind != Type_Record) { - return false; - } - // gb_printf_err("#2 %s %s\n", type_to_string(type), type_to_string(specialization)); - if (t->Record.polymorphic_parent == specialization) { - return true; - } - // gb_printf_err("#3 %s %s\n", type_to_string(t->Record.polymorphic_parent), type_to_string(specialization)); - if (t->Record.polymorphic_parent == s->Record.polymorphic_parent) { - return true; - } - - - return false; -} Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_variadic_, bool *success_, Array *operands) { if (_params == nullptr) { @@ -4323,7 +4354,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h Entity *e = scope_lookup_entity(c->context.scope, op_name); bool is_alias = false; - while (e->kind == Entity_Alias) { + while (e != nullptr && e->kind == Entity_Alias) { GB_ASSERT(e->Alias.base != nullptr); e = e->Alias.base; is_alias = true; @@ -6549,6 +6580,10 @@ CallArgumentError check_polymorphic_struct_type(Checker *c, Operand *operand, As Entity *p = tuple->variables[j]; Operand o = ordered_operands[j]; if (p->kind == Entity_TypeName) { + if (is_type_polymorphic(o.type)) { + // NOTE(bill): Do not add polymorphic version to the gen_types + ok = false; + } if (!are_types_identical(o.type, p->type)) { ok = false; } @@ -7619,12 +7654,14 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t if (!valid) { gbString str = expr_to_string(o->expr); + gbString type_str = type_to_string(o->type); + defer (gb_string_free(str)); + defer (gb_string_free(type_str)); if (is_const) { error(o->expr, "Cannot index a constant `%s`", str); } else { - error(o->expr, "Cannot index `%s`", str); + error(o->expr, "Cannot index `%s` of type `%s`", str, type_str); } - gb_string_free(str); o->mode = Addressing_Invalid; o->expr = node; return kind; @@ -8009,6 +8046,12 @@ gbString write_expr_to_string(gbString str, AstNode *node) { str = write_expr_to_string(str, ta->type); str = gb_string_appendc(str, ")"); case_end; + case_ast_node(tc, TypeCast, node); + str = gb_string_appendc(str, "cast("); + str = write_expr_to_string(str, tc->type); + str = gb_string_appendc(str, ")"); + str = write_expr_to_string(str, tc->expr); + case_end; case_ast_node(ie, IndexExpr, node); str = write_expr_to_string(str, ie->expr); -- cgit v1.2.3