diff options
| author | Ginger Bill <bill@gingerbill.org> | 2017-04-30 16:22:24 +0100 |
|---|---|---|
| committer | Ginger Bill <bill@gingerbill.org> | 2017-04-30 16:22:24 +0100 |
| commit | 1430ca30a3a0677fb395d2c5c190e86f02ed2b06 (patch) | |
| tree | 6dcd158f5c66a356edb1fac6922668b8da1f49b6 /src | |
| parent | e63393e3941f43aca0976367d36f22207758e4a1 (diff) | |
Fix subtype polymorphism implicit conversion
Diffstat (limited to 'src')
| -rw-r--r-- | src/check_expr.c | 50 | ||||
| -rw-r--r-- | src/entity.c | 8 | ||||
| -rw-r--r-- | src/ir.c | 56 | ||||
| -rw-r--r-- | src/tokenizer.c | 3 |
4 files changed, 72 insertions, 45 deletions
diff --git a/src/check_expr.c b/src/check_expr.c index 3722652b6..db0ea2eb8 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -79,32 +79,37 @@ void check_scope_decls(Checker *c, AstNodeArray nodes, isize reserve_size) { } -bool check_is_assignable_to_using_subtype(Type *dst, Type *src) { - bool src_is_ptr; +bool check_is_assignable_to_using_subtype(Type *src, Type *dst) { + bool src_is_ptr = false; Type *prev_src = src; src = type_deref(src); src_is_ptr = src != prev_src; src = base_type(src); - if (is_type_struct(src) || is_type_union(src)) { - for (isize i = 0; i < src->Record.field_count; i++) { - Entity *f = src->Record.fields[i]; - if (f->kind == Entity_Variable && (f->flags & EntityFlag_Using) != 0) { - if (are_types_identical(dst, f->type)) { - return true; - } - if (src_is_ptr && is_type_pointer(dst)) { - if (are_types_identical(type_deref(dst), f->type)) { - return true; - } - } - bool ok = check_is_assignable_to_using_subtype(dst, f->type); - if (ok) { - return true; - } + if (!is_type_struct(src) && !is_type_union(src)) { + return false; + } + + for (isize i = 0; i < src->Record.field_count; i++) { + Entity *f = src->Record.fields[i]; + if (f->kind != Entity_Variable || (f->flags&EntityFlag_Using) == 0) { + continue; + } + + if (are_types_identical(f->type, dst)) { + return true; + } + if (src_is_ptr && is_type_pointer(dst)) { + if (are_types_identical(f->type, type_deref(dst))) { + return true; } } + bool ok = check_is_assignable_to_using_subtype(f->type, dst); + if (ok) { + return true; + } } + return false; } @@ -113,6 +118,7 @@ bool check_is_assignable_to_using_subtype(Type *dst, Type *src) { // -1 is not convertable // 0 is exact // >0 is convertable + i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) { if (operand->mode == Addressing_Invalid || type == t_invalid) { @@ -383,10 +389,12 @@ isize check_fields(Checker *c, AstNode *node, AstNodeArray decls, ast_node(f, Field, decl); Type *type = check_type(c, f->type); + bool is_using = (f->flags&FieldFlag_using) != 0; - if (f->flags&FieldFlag_using) { + if (is_using) { if (f->names.count > 1) { error_node(f->names.e[0], "Cannot apply `using` to more than one of the same type"); + is_using = false; } } @@ -398,7 +406,7 @@ isize check_fields(Checker *c, AstNode *node, AstNodeArray decls, Token name_token = name->Ident; - Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, f->flags&FieldFlag_using, cast(i32)field_index); + Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, is_using, cast(i32)field_index); e->identifier = name; if (str_eq(name_token.string, str_lit("_"))) { fields[field_index++] = e; @@ -421,7 +429,7 @@ isize check_fields(Checker *c, AstNode *node, AstNodeArray decls, } - if (f->flags&FieldFlag_using) { + if (is_using) { Type *t = base_type(type_deref(type)); if (!is_type_struct(t) && !is_type_raw_union(t) && f->names.count >= 1 && diff --git a/src/entity.c b/src/entity.c index 41d11371e..18b318a1f 100644 --- a/src/entity.c +++ b/src/entity.c @@ -185,20 +185,20 @@ Entity *make_entity_type_name(gbAllocator a, Scope *scope, Token token, Type *ty return entity; } -Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, bool is_immutable) { +Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool is_using, bool is_immutable) { Entity *entity = make_entity_variable(a, scope, token, type, is_immutable); entity->flags |= EntityFlag_Used; - if (anonymous) entity->flags |= EntityFlag_Using; + if (is_using) entity->flags |= EntityFlag_Using; entity->flags |= EntityFlag_Param; return entity; } -Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, i32 field_src_index) { +Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool is_using, i32 field_src_index) { Entity *entity = make_entity_variable(a, scope, token, type, false); entity->Variable.field_src_index = field_src_index; entity->Variable.field_index = field_src_index; + if (is_using) entity->flags |= EntityFlag_Using; entity->flags |= EntityFlag_Field; - entity->flags |= EntityFlag_Using*(anonymous != 0); return entity; } @@ -2613,7 +2613,7 @@ irValue *ir_add_local_slice(irProcedure *proc, Type *slice_type, irValue *base, -String lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) { +String ir_lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) { Type *prev_src = src; // Type *prev_dst = dst; src = base_type(type_deref(src)); @@ -2621,7 +2621,7 @@ String lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) { bool src_is_ptr = src != prev_src; // bool dst_is_ptr = dst != prev_dst; - GB_ASSERT(is_type_struct(src)); + GB_ASSERT(is_type_struct(src) || is_type_union(src)); for (isize i = 0; i < src->Record.field_count; i++) { Entity *f = src->Record.fields[i]; if (f->kind == Entity_Variable && f->flags & EntityFlag_Using) { @@ -2634,7 +2634,7 @@ String lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) { } } if (is_type_struct(f->type)) { - String name = lookup_polymorphic_field(info, dst, f->type); + String name = ir_lookup_polymorphic_field(info, dst, f->type); if (name.len > 0) { return name; } @@ -2805,23 +2805,45 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { } } - // NOTE(bill): This has to be done beofre `Pointer <-> Pointer` as it's + // NOTE(bill): This has to be done before `Pointer <-> Pointer` as it's // subtype polymorphism casting - { - Type *sb = base_type(type_deref(src)); - bool src_is_ptr = src != sb; - if (is_type_struct(sb)) { - String field_name = lookup_polymorphic_field(proc->module->info, t, src); - // gb_printf("field_name: %.*s\n", LIT(field_name)); - if (field_name.len > 0) { - // NOTE(bill): It can be casted - Selection sel = lookup_field(proc->module->allocator, sb, field_name, false); - if (sel.entity != NULL) { - ir_emit_comment(proc, str_lit("cast - polymorphism")); - if (src_is_ptr) { - value = ir_emit_load(proc, value); + if (check_is_assignable_to_using_subtype(src_type, t)) { + Type *st = type_deref(src_type); + Type *pst = st; + st = type_deref(st); + + bool st_is_ptr = st != pst; + st = base_type(st); + + Type *dt = t; + bool dt_is_ptr = is_type_pointer(dt); + + GB_ASSERT(is_type_struct(st) || is_type_union(st)); + String field_name = ir_lookup_polymorphic_field(proc->module->info, t, st); + // gb_printf("field_name: %.*s\n", LIT(field_name)); + if (field_name.len > 0) { + // NOTE(bill): It can be casted + Selection sel = lookup_field(proc->module->allocator, st, field_name, false); + if (sel.entity != NULL) { + ir_emit_comment(proc, str_lit("cast - polymorphism")); + if (st_is_ptr) { + irValue *res = ir_emit_deep_field_gep(proc, value, sel); + if (!dt_is_ptr) { + res = ir_emit_load(proc, res); } + return res; + } else { + if (is_type_pointer(ir_type(value))) { + if (!dt_is_ptr) { + value = ir_emit_load(proc, value); + } else { + value = ir_emit_deep_field_gep(proc, value, sel); + return ir_emit_load(proc, value); + } + } + return ir_emit_deep_field_ev(proc, value, sel); + } } } diff --git a/src/tokenizer.c b/src/tokenizer.c index 56186f914..9dcade195 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -107,9 +107,6 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \ TOKEN_KIND(Token_using, "using"), \ TOKEN_KIND(Token_no_alias, "no_alias"), \ TOKEN_KIND(Token_immutable, "immutable"), \ - /* TOKEN_KIND(Token_cast, "cast"), */ \ - /* TOKEN_KIND(Token_transmute, "transmute"), */ \ - /* TOKEN_KIND(Token_union_cast, "union_cast"), */ \ TOKEN_KIND(Token_context, "context"), \ TOKEN_KIND(Token_push_context, "push_context"), \ TOKEN_KIND(Token_push_allocator, "push_allocator"), \ |