From 3cd6ae311df68c1ac8b4015ebf5eef694126a59b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 8 Sep 2018 12:02:25 +0100 Subject: Parametric polymorphic union type --- src/check_type.cpp | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 171 insertions(+), 7 deletions(-) (limited to 'src/check_type.cpp') diff --git a/src/check_type.cpp b/src/check_type.cpp index 98d35cf0b..757fb7f37 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -241,13 +241,13 @@ bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) { } -Entity *find_polymorphic_struct_entity(CheckerContext *ctx, Type *original_type, isize param_count, Array ordered_operands) { +Entity *find_polymorphic_record_entity(CheckerContext *ctx, Type *original_type, isize param_count, Array ordered_operands) { auto *found_gen_types = map_get(&ctx->checker->info.gen_types, hash_pointer(original_type)); if (found_gen_types != nullptr) { for_array(i, *found_gen_types) { Entity *e = (*found_gen_types)[i]; Type *t = base_type(e->type); - TypeTuple *tuple = &t->Struct.polymorphic_params->Tuple; + TypeTuple *tuple = get_record_polymorphic_params(t); bool ok = true; GB_ASSERT(param_count == tuple->variables.count); for (isize j = 0; j < param_count; j++) { @@ -281,7 +281,7 @@ Entity *find_polymorphic_struct_entity(CheckerContext *ctx, Type *original_type, } -void add_polymorphic_struct_entity(CheckerContext *ctx, Ast *node, Type *named_type, Type *original_type) { +void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_type, Type *original_type) { GB_ASSERT(is_type_named(named_type)); gbAllocator a = heap_allocator(); Scope *s = ctx->scope->parent; @@ -472,7 +472,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< if (original_type_for_poly != nullptr) { GB_ASSERT(named_type != nullptr); - add_polymorphic_struct_entity(ctx, node, named_type, original_type_for_poly); + add_polymorphic_record_entity(ctx, node, named_type, original_type_for_poly); } } @@ -517,7 +517,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< } } } -void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node) { +void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array *poly_operands, Type *named_type, Type *original_type_for_poly) { GB_ASSERT(is_type_union(union_type)); ast_node(ut, UnionType, node); @@ -529,6 +529,170 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node) { union_type->Union.scope = ctx->scope; + Type *polymorphic_params = nullptr; + bool is_polymorphic = false; + bool can_check_fields = true; + bool is_poly_specialized = false; + + if (ut->polymorphic_params != nullptr) { + ast_node(field_list, FieldList, ut->polymorphic_params); + Array params = field_list->list; + if (params.count != 0) { + isize variable_count = 0; + for_array(i, params) { + Ast *field = params[i]; + if (ast_node_expect(field, Ast_Field)) { + ast_node(f, Field, field); + variable_count += gb_max(f->names.count, 1); + } + } + + auto entities = array_make(ctx->allocator, 0, variable_count); + + for_array(i, params) { + Ast *param = params[i]; + if (param->kind != Ast_Field) { + continue; + } + ast_node(p, Field, param); + Ast *type_expr = p->type; + Type *type = nullptr; + bool is_type_param = false; + bool is_type_polymorphic_type = false; + if (type_expr == nullptr) { + error(param, "Expected a type for this parameter"); + continue; + } + if (type_expr->kind == Ast_Ellipsis) { + type_expr = type_expr->Ellipsis.expr; + error(param, "A polymorphic parameter cannot be variadic"); + } + if (type_expr->kind == Ast_TypeidType) { + is_type_param = true; + Type *specialization = nullptr; + if (type_expr->TypeidType.specialization != nullptr) { + Ast *s = type_expr->TypeidType.specialization; + specialization = check_type(ctx, s); + } + type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization); + } else if (type_expr->kind == Ast_TypeType) { + is_type_param = true; + Type *specialization = nullptr; + if (type_expr->TypeType.specialization != nullptr) { + Ast *s = type_expr->TypeType.specialization; + specialization = check_type(ctx, s); + } + type = alloc_type_generic(ctx->scope, 0, str_lit(""), specialization); + } else { + type = check_type(ctx, type_expr); + if (is_type_polymorphic(type)) { + is_type_polymorphic_type = true; + } + } + + if (type == nullptr) { + error(params[i], "Invalid parameter type"); + type = t_invalid; + } + if (is_type_untyped(type)) { + if (is_type_untyped_undef(type)) { + error(params[i], "Cannot determine parameter type from ---"); + } else { + error(params[i], "Cannot determine parameter type from a nil"); + } + type = t_invalid; + } + + if (is_type_polymorphic_type) { + gbString str = type_to_string(type); + error(params[i], "Parameter types cannot be polymorphic, got %s", str); + gb_string_free(str); + type = t_invalid; + } + + if (!is_type_param && !is_type_constant_type(type)) { + gbString str = type_to_string(type); + error(params[i], "A parameter must be a valid constant type, got %s", str); + gb_string_free(str); + } + + Scope *scope = ctx->scope; + for_array(j, p->names) { + Ast *name = p->names[j]; + if (!ast_node_expect(name, Ast_Ident)) { + continue; + } + Entity *e = nullptr; + + Token token = name->Ident.token; + + if (poly_operands != nullptr) { + Operand operand = (*poly_operands)[entities.count]; + if (is_type_param) { + GB_ASSERT(operand.mode == Addressing_Type || + operand.mode == Addressing_Invalid); + if (is_type_polymorphic(base_type(operand.type))) { + is_polymorphic = true; + can_check_fields = false; + } + e = alloc_entity_type_name(scope, token, operand.type); + e->TypeName.is_type_alias = true; + } else { + GB_ASSERT(operand.mode == Addressing_Constant); + e = alloc_entity_constant(scope, token, operand.type, operand.value); + } + } else { + if (is_type_param) { + e = alloc_entity_type_name(scope, token, type); + e->TypeName.is_type_alias = true; + } else { + e = alloc_entity_constant(scope, token, type, empty_exact_value); + } + } + + e->state = EntityState_Resolved; + add_entity(ctx->checker, scope, name, e); + array_add(&entities, e); + } + } + + if (entities.count > 0) { + Type *tuple = alloc_type_tuple(); + tuple->Tuple.variables = entities; + polymorphic_params = tuple; + } + } + + if (original_type_for_poly != nullptr) { + GB_ASSERT(named_type != nullptr); + add_polymorphic_record_entity(ctx, node, named_type, original_type_for_poly); + } + } + + if (!is_polymorphic) { + is_polymorphic = polymorphic_params != nullptr && poly_operands == nullptr; + } + if (poly_operands != nullptr) { + is_poly_specialized = true; + for (isize i = 0; i < poly_operands->count; i++) { + Operand o = (*poly_operands)[i]; + if (is_type_polymorphic(o.type)) { + is_poly_specialized = false; + break; + } + if (union_type == o.type) { + // NOTE(bill): Cycle + is_poly_specialized = false; + break; + } + } + } + + union_type->Union.scope = ctx->scope; + union_type->Union.polymorphic_params = polymorphic_params; + union_type->Union.is_polymorphic = is_polymorphic; + union_type->Union.is_poly_specialized = is_poly_specialized; + for_array(i, ut->variants) { Ast *node = ut->variants[i]; Type *t = check_type_expr(ctx, node, nullptr); @@ -2021,7 +2185,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t *type = o.type; if (!ctx->in_polymorphic_specialization) { Type *t = base_type(o.type); - if (t != nullptr && is_type_polymorphic_struct_unspecialized(t)) { + if (t != nullptr && is_type_polymorphic_record_unspecialized(t)) { err_str = expr_to_string(e); error(e, "Invalid use of a non-specialized polymorphic type '%s'", err_str); return true; @@ -2209,7 +2373,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t *type = alloc_type_union(); set_base_type(named_type, *type); check_open_scope(&c, e); - check_union_type(&c, *type, e); + check_union_type(&c, *type, e, nullptr, named_type); check_close_scope(&c); (*type)->Union.node = e; return true; -- cgit v1.2.3