diff options
| author | Ginger Bill <bill@gingerbill.org> | 2017-07-13 22:35:00 +0100 |
|---|---|---|
| committer | Ginger Bill <bill@gingerbill.org> | 2017-07-13 22:35:00 +0100 |
| commit | 1c5ddd65b4f66c4d37bd9da0facb229bbcea22eb (patch) | |
| tree | 78078fbb2cf23b31689bb6842ba30e487f199447 /src | |
| parent | b8697fb4ed34d0da0fa0888b57e6edcc37a0ce81 (diff) | |
Rudimentary support for parametric polymorphic types
Diffstat (limited to 'src')
| -rw-r--r-- | src/check_decl.cpp | 2 | ||||
| -rw-r--r-- | src/check_expr.cpp | 525 | ||||
| -rw-r--r-- | src/check_stmt.cpp | 251 | ||||
| -rw-r--r-- | src/checker.cpp | 3 | ||||
| -rw-r--r-- | src/ir.cpp | 20 | ||||
| -rw-r--r-- | src/ir_print.cpp | 2 | ||||
| -rw-r--r-- | src/parser.cpp | 60 | ||||
| -rw-r--r-- | src/types.cpp | 16 |
8 files changed, 662 insertions, 217 deletions
diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 96827590d..7c33cf9e5 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -558,7 +558,7 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count if (type_expr != nullptr) { e->type = check_type(c, type_expr); } - if (e->type != nullptr && is_type_polymorphic(e->type)) { + if (e->type != nullptr && is_type_polymorphic(base_type(e->type))) { error(e->token, "Invalid use of a polymorphic type in %.*s", LIT(context_name)); e->type = t_invalid; } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index a5557ee86..47151cc74 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -12,6 +12,7 @@ enum CallArgumentError { CallArgumentError_ParameterNotFound, CallArgumentError_ParameterMissing, CallArgumentError_DuplicateParameter, + CallArgumentError_NoneConstantParameter, }; enum CallArgumentErrorMode { @@ -972,7 +973,7 @@ Entity *make_names_field_for_record(Checker *c, Scope *scope) { return e; } -void check_struct_type(Checker *c, Type *struct_type, AstNode *node) { +void check_struct_type(Checker *c, Type *struct_type, AstNode *node, Array<Operand> *poly_operands) { GB_ASSERT(is_type_struct(struct_type)); ast_node(st, StructType, node); @@ -984,11 +985,132 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) { min_field_count += f->names.count; case_end; } - } struct_type->Record.names = make_names_field_for_record(c, c->context.scope); - auto fields = check_fields(c, node, st->fields, min_field_count, str_lit("struct")); + Type *polymorphic_params = nullptr; + bool is_polymorphic = false; + if (st->polymorphic_params != nullptr) { + ast_node(field_list, FieldList, st->polymorphic_params); + Array<AstNode *> params = field_list->list; + if (params.count != 0) { + isize variable_count = 0; + for_array(i, params) { + AstNode *field = params[i]; + if (ast_node_expect(field, AstNode_Field)) { + ast_node(f, Field, field); + variable_count += gb_max(f->names.count, 1); + } + } + + Array<Entity *> entities = {}; + array_init(&entities, c->allocator, variable_count); + + for_array(i, params) { + AstNode *param = params[i]; + if (param->kind != AstNode_Field) { + continue; + } + ast_node(p, Field, param); + AstNode *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 == AstNode_Ellipsis) { + type_expr = type_expr->Ellipsis.expr; + error(param, "A polymorphic parameter cannot be variadic"); + } + if (type_expr->kind == AstNode_TypeType) { + is_type_param = true; + type = make_type_generic(c->allocator, 0, str_lit("")); + } else { + type = check_type(c, 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 = c->context.scope; + for_array(j, p->names) { + AstNode *name = p->names[j]; + if (!ast_node_expect(name, AstNode_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); + e = make_entity_type_name(c->allocator, scope, token, operand.type); + e->TypeName.is_type_alias = true; + } else { + GB_ASSERT(operand.mode == Addressing_Constant); + e = make_entity_constant(c->allocator, scope, token, operand.type, operand.value); + } + } else { + if (is_type_param) { + e = make_entity_type_name(c->allocator, scope, token, type); + e->TypeName.is_type_alias = true; + } else { + e = make_entity_constant(c->allocator, scope, token, type, empty_exact_value); + } + } + + add_entity(c, scope, name, e); + array_add(&entities, e); + } + } + + if (entities.count > 0) { + Type *tuple = make_type_tuple(c->allocator); + tuple->Tuple.variables = entities.data; + tuple->Tuple.variable_count = entities.count; + + polymorphic_params = tuple; + } + } + } + + is_polymorphic = polymorphic_params != nullptr && poly_operands == nullptr; + + Array<Entity *> fields = {}; + + if (!is_polymorphic) { + fields = check_fields(c, node, st->fields, min_field_count, str_lit("struct")); + } struct_type->Record.scope = c->context.scope; struct_type->Record.is_packed = st->is_packed; @@ -996,6 +1118,9 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) { struct_type->Record.fields = fields.data; struct_type->Record.fields_in_src_order = fields.data; struct_type->Record.field_count = fields.count; + struct_type->Record.polymorphic_params = polymorphic_params; + struct_type->Record.is_polymorphic = is_polymorphic; + type_set_offsets(c->allocator, struct_type); @@ -1703,53 +1828,55 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari for_array(j, p->names) { AstNode *name = p->names[j]; - if (ast_node_expect(name, AstNode_Ident)) { - Entity *param = nullptr; - if (is_type_param) { - if (operands != nullptr) { - Operand o = (*operands)[variable_index]; - if (o.mode == Addressing_Type) { - type = o.type; - } else { - if (!c->context.no_polymorphic_errors) { - error(o.expr, "Expected a type to assign to the type parameter"); - } - success = false; - type = t_invalid; - } - } - param = make_entity_type_name(c->allocator, scope, name->Ident.token, type); - param->TypeName.is_type_alias = true; - } else { - if (operands != nullptr && is_type_polymorphic_type) { - Operand op = (*operands)[variable_index]; - type = determine_type_from_polymorphic(c, type, op); - if (type == t_invalid) { - success = false; - } - } + if (!ast_node_expect(name, AstNode_Ident)) { + continue; + } - if (p->flags&FieldFlag_no_alias) { - if (!is_type_pointer(type)) { - error(params[i], "`#no_alias` can only be applied to fields of pointer type"); - p->flags &= ~FieldFlag_no_alias; // Remove the flag + Entity *param = nullptr; + if (is_type_param) { + if (operands != nullptr) { + Operand o = (*operands)[variable_index]; + if (o.mode == Addressing_Type) { + type = o.type; + } else { + if (!c->context.no_polymorphic_errors) { + error(o.expr, "Expected a type to assign to the type parameter"); } + success = false; + type = t_invalid; + } + } + param = make_entity_type_name(c->allocator, scope, name->Ident.token, type); + param->TypeName.is_type_alias = true; + } else { + if (operands != nullptr && is_type_polymorphic_type) { + Operand op = (*operands)[variable_index]; + type = determine_type_from_polymorphic(c, type, op); + if (type == t_invalid) { + success = false; } - - param = make_entity_param(c->allocator, scope, name->Ident.token, type, - (p->flags&FieldFlag_using) != 0, false); - param->Variable.default_value = value; - param->Variable.default_is_nil = default_is_nil; - param->Variable.default_is_location = default_is_location; - } + if (p->flags&FieldFlag_no_alias) { - param->flags |= EntityFlag_NoAlias; + if (!is_type_pointer(type)) { + error(params[i], "`#no_alias` can only be applied to fields of pointer type"); + p->flags &= ~FieldFlag_no_alias; // Remove the flag + } } - add_entity(c, scope, name, param); - variables[variable_index++] = param; + param = make_entity_param(c->allocator, scope, name->Ident.token, type, + (p->flags&FieldFlag_using) != 0, false); + param->Variable.default_value = value; + param->Variable.default_is_nil = default_is_nil; + param->Variable.default_is_location = default_is_location; + + } + if (p->flags&FieldFlag_no_alias) { + param->flags |= EntityFlag_NoAlias; } + + add_entity(c, scope, name, param); + variables[variable_index++] = param; } } @@ -2358,7 +2485,7 @@ i64 check_array_or_map_count(Checker *c, AstNode *e, bool is_map) { if (count >= 0) { return count; } - error(e, "Invalid array count"); + error(e, "Invalid negative array count %lld", cast(long long)count); } return 0; } @@ -2663,7 +2790,7 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type) *type = make_type_struct(c->allocator); set_base_type(named_type, *type); check_open_scope(c, e); - check_struct_type(c, *type, e); + check_struct_type(c, *type, e, nullptr); check_close_scope(c); (*type)->Record.node = e; return true; @@ -6114,8 +6241,263 @@ Entity *find_using_index_expr(Type *t) { return nullptr; } +isize lookup_polymorphic_struct_parameter(TypeRecord *st, String parameter_name) { + if (!st->is_polymorphic) return -1; + + TypeTuple *params = &st->polymorphic_params->Tuple; + isize param_count = params->variable_count; + for (isize i = 0; i < param_count; i++) { + Entity *e = params->variables[i]; + String name = e->token.string; + if (is_blank_ident(name)) { + continue; + } + if (name == parameter_name) { + return i; + } + } + return -1; +} + +CallArgumentError check_polymorphic_struct_type(Checker *c, Operand *operand, AstNode *call) { + ast_node(ce, CallExpr, call); + + Type *original_type = operand->type; + Type *struct_type = base_type(operand->type); + GB_ASSERT(is_type_struct(struct_type)); + TypeRecord *st = &struct_type->Record; + GB_ASSERT(st->is_polymorphic); + + bool show_error = true; + + Array<Operand> operands = {}; + defer (array_free(&operands)); + + bool named_fields = false; + + if (is_call_expr_field_value(ce)) { + named_fields = true; + array_init_count(&operands, heap_allocator(), ce->args.count); + for_array(i, ce->args) { + AstNode *arg = ce->args[i]; + ast_node(fv, FieldValue, arg); + check_expr_or_type(c, &operands[i], fv->value); + } + + bool vari_expand = (ce->ellipsis.pos.line != 0); + if (vari_expand) { + error(ce->ellipsis, "Invalid use of `..` in a polymorphic type call`"); + } + + } else { + array_init(&operands, heap_allocator(), 2*ce->args.count); + check_unpack_arguments(c, -1, &operands, ce->args, false); + } + + CallArgumentError err = CallArgumentError_None; + + TypeTuple *tuple = &st->polymorphic_params->Tuple; + isize param_count = tuple->variable_count; + + Array<Operand> ordered_operands = operands; + if (named_fields) { + bool *visited = gb_alloc_array(c->allocator, bool, param_count); + + array_init_count(&ordered_operands, c->tmp_allocator, param_count); + + for_array(i, ce->args) { + AstNode *arg = ce->args[i]; + ast_node(fv, FieldValue, arg); + if (fv->field->kind != AstNode_Ident) { + if (show_error) { + gbString expr_str = expr_to_string(fv->field); + error(arg, "Invalid parameter name `%s` in polymorphic type call", expr_str); + gb_string_free(expr_str); + } + err = CallArgumentError_InvalidFieldValue; + continue; + } + String name = fv->field->Ident.token.string; + isize index = lookup_polymorphic_struct_parameter(st, name); + if (index < 0) { + if (show_error) { + error(arg, "No parameter named `%.*s` for this polymorphic type", LIT(name)); + } + err = CallArgumentError_ParameterNotFound; + continue; + } + if (visited[index]) { + if (show_error) { + error(arg, "Duplicate parameter `%.*s` in polymorphic type", LIT(name)); + } + err = CallArgumentError_DuplicateParameter; + continue; + } + + visited[index] = true; + ordered_operands[index] = operands[i]; + } + + for (isize i = 0; i < param_count; i++) { + if (!visited[i]) { + Entity *e = tuple->variables[i]; + if (is_blank_ident(e->token)) { + continue; + } + + if (show_error) { + if (e->kind == Entity_TypeName) { + error(call, "Type parameter `%.*s` is missing in polymorphic type call", + LIT(e->token.string)); + } else { + gbString str = type_to_string(e->type); + error(call, "Parameter `%.*s` of type `%s` is missing in polymorphic type call", + LIT(e->token.string), str); + gb_string_free(str); + } + } + err = CallArgumentError_ParameterMissing; + } + } + } + + if (err != 0) { + operand->mode = Addressing_Invalid; + return err; + } + + i64 score = 0; + for (isize i = 0; i < param_count; i++) { + Operand *o = &ordered_operands[i]; + if (o->mode == Addressing_Invalid) { + continue; + } + Entity *e = tuple->variables[i]; + + if (e->kind == Entity_TypeName) { + if (o->mode != Addressing_Type) { + if (show_error) { + error(o->expr, "Expected a type for the argument `%.*s`", LIT(e->token.string)); + } + err = CallArgumentError_WrongTypes; + } + if (are_types_identical(e->type, o->type)) { + score += assign_score_function(1); + } else { + score += assign_score_function(10); + } + } else { + i64 s = 0; + if (!check_is_assignable_to_with_score(c, o, e->type, &s)) { + if (show_error) { + check_assignment(c, o, e->type, str_lit("polymorphic type argument")); + } + err = CallArgumentError_WrongTypes; + } + o->type = e->type; + if (o->mode != Addressing_Constant) { + if (show_error) { + error(o->expr, "Expected a constant value for this polymorphic type argument"); + } + err = CallArgumentError_NoneConstantParameter; + } + score += s; + } + } + + if (param_count < ordered_operands.count) { + error(call, "Too many polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count); + err = CallArgumentError_TooManyArguments; + } else if (param_count > ordered_operands.count) { + error(call, "Too few polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count); + err = CallArgumentError_TooFewArguments; + } + + if (err == 0) { + // TODO(bill): Check for previous types + gbAllocator a = c->allocator; + + auto *found_gen_types = map_get(&c->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->Record.polymorphic_params->Tuple; + bool ok = true; + GB_ASSERT(param_count == tuple->variable_count); + for (isize j = 0; j < param_count; j++) { + Entity *p = tuple->variables[j]; + Operand o = ordered_operands[j]; + if (p->kind == Entity_TypeName) { + if (!are_types_identical(o.type, p->type)) { + ok = false; + } + } else if (p->kind == Entity_Constant) { + if (!are_types_identical(o.type, p->type)) { + ok = false; + } + if (!compare_exact_values(Token_CmpEq, o.value, p->Constant.value)) { + ok = false; + } + } else { + GB_PANIC("Unknown entity kind"); + } + } + if (ok) { + operand->mode = Addressing_Type; + operand->type = e->type; + return err; + } + } + } + + String generated_name = make_string_c(expr_to_string(call)); + + Type *named_type = make_type_named(a, generated_name, nullptr, nullptr); + Type *struct_type = make_type_struct(a); + AstNode *node = clone_ast_node(a, st->node); + set_base_type(named_type, struct_type); + check_open_scope(c, node); + check_struct_type(c, struct_type, node, &ordered_operands); + check_close_scope(c); + struct_type->Record.node = node; + + Entity *e = nullptr; + + { + Token token = ast_node_token(node); + token.kind = Token_String; + token.string = generated_name; + + AstNode *node = gb_alloc_item(a, AstNode); + node->kind = AstNode_Ident; + node->Ident.token = token; + + e = make_entity_type_name(a, st->scope->parent, token, named_type); + add_entity(c, st->scope->parent, node, e); + add_entity_use(c, node, e); + } + + named_type->Named.type_name = e; + + if (found_gen_types) { + array_add(found_gen_types, e); + } else { + Array<Entity *> array = {}; + array_init(&array, heap_allocator()); + array_add(&array, e); + map_set(&c->info.gen_types, hash_pointer(original_type), array); + } + + operand->mode = Addressing_Type; + operand->type = named_type; + } + return err; +} + + ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) { - GB_ASSERT(call->kind == AstNode_CallExpr); ast_node(ce, CallExpr, call); if (ce->proc != nullptr && ce->proc->kind == AstNode_BasicDirective) { @@ -6170,28 +6552,43 @@ 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) { - case 0: error(call, "Missing argument in conversion to `%s`", str); break; - default: error(call, "Too many arguments in conversion to `%s`", str); break; - case 1: { - AstNode *arg = ce->args[0]; - if (arg->kind == AstNode_FieldValue) { - error(call, "`field = value` cannot be used in a type conversion"); - arg = arg->FieldValue.value; - // NOTE(bill): Carry on the cast regardless + if (is_type_polymorphic_struct(t)) { + auto err = check_polymorphic_struct_type(c, operand, call); + if (err == 0) { + AstNode *ident = operand->expr; + while (ident->kind == AstNode_SelectorExpr) { + AstNode *s = ident->SelectorExpr.selector; + ident = s; + } + add_entity_use(c, ident, entity_of_ident(&c->info, ident)); + add_type_and_value(&c->info, call, Addressing_Type, operand->type, empty_exact_value); + } else { + operand->mode = Addressing_Invalid; + operand->type = t_invalid; } - check_expr(c, operand, arg); - if (operand->mode != Addressing_Invalid) { - check_cast(c, operand, t); + } else { + gbString str = type_to_string(t); + defer (gb_string_free(str)); + + operand->mode = Addressing_Invalid; + isize arg_count = ce->args.count; + switch (arg_count) { + case 0: error(call, "Missing argument in conversion to `%s`", str); break; + default: error(call, "Too many arguments in conversion to `%s`", str); break; + case 1: { + AstNode *arg = ce->args[0]; + if (arg->kind == AstNode_FieldValue) { + error(call, "`field = value` cannot be used in a type conversion"); + arg = arg->FieldValue.value; + // NOTE(bill): Carry on the cast regardless + } + check_expr(c, operand, arg); + if (operand->mode != Addressing_Invalid) { + check_cast(c, operand, t); + } + } break; } - } break; } - return Expr_Expr; } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 6bf873bd3..51e6adf79 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1666,150 +1666,151 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { case_end; case_ast_node(vd, ValueDecl, node); - if (vd->is_mutable) { - Entity **entities = gb_alloc_array(c->allocator, Entity *, vd->names.count); - isize entity_count = 0; + if (!vd->is_mutable) { + break; + } + Entity **entities = gb_alloc_array(c->allocator, Entity *, vd->names.count); + isize entity_count = 0; - if (vd->flags & VarDeclFlag_thread_local) { - vd->flags &= ~VarDeclFlag_thread_local; - error(node, "`thread_local` may only be applied to a variable declaration"); - } + if (vd->flags & VarDeclFlag_thread_local) { + vd->flags &= ~VarDeclFlag_thread_local; + error(node, "`thread_local` may only be applied to a variable declaration"); + } - for_array(i, vd->names) { - AstNode *name = vd->names[i]; - Entity *entity = nullptr; - if (name->kind != AstNode_Ident) { - error(name, "A variable declaration must be an identifier"); - } else { - Token token = name->Ident.token; - String str = token.string; - Entity *found = nullptr; - // NOTE(bill): Ignore assignments to `_` - if (!is_blank_ident(str)) { - found = current_scope_lookup_entity(c->context.scope, str); - } - if (found == nullptr) { - entity = make_entity_variable(c->allocator, c->context.scope, token, nullptr, false); - entity->identifier = name; - - AstNode *fl = c->context.curr_foreign_library; - if (fl != nullptr) { - GB_ASSERT(fl->kind == AstNode_Ident); - entity->Variable.is_foreign = true; - entity->Variable.foreign_library_ident = fl; - } - } else { - TokenPos pos = found->token.pos; - error(token, - "Redeclaration of `%.*s` in this scope\n" - "\tat %.*s(%td:%td)", - LIT(str), LIT(pos.file), pos.line, pos.column); - entity = found; - } - } - if (entity == nullptr) { - entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name)); + for_array(i, vd->names) { + AstNode *name = vd->names[i]; + Entity *entity = nullptr; + if (name->kind != AstNode_Ident) { + error(name, "A variable declaration must be an identifier"); + } else { + Token token = name->Ident.token; + String str = token.string; + Entity *found = nullptr; + // NOTE(bill): Ignore assignments to `_` + if (!is_blank_ident(str)) { + found = current_scope_lookup_entity(c->context.scope, str); } - entity->parent_proc_decl = c->context.curr_proc_decl; - entities[entity_count++] = entity; - } - - Type *init_type = nullptr; - if (vd->type) { - init_type = check_type(c, vd->type, nullptr); - if (init_type == nullptr) { - init_type = t_invalid; - } else if (is_type_polymorphic(init_type)) { - error(vd->type, "Invalid use of a polymorphic type in variable declaration"); - init_type = t_invalid; + if (found == nullptr) { + entity = make_entity_variable(c->allocator, c->context.scope, token, nullptr, false); + entity->identifier = name; + + AstNode *fl = c->context.curr_foreign_library; + if (fl != nullptr) { + GB_ASSERT(fl->kind == AstNode_Ident); + entity->Variable.is_foreign = true; + entity->Variable.foreign_library_ident = fl; + } + } else { + TokenPos pos = found->token.pos; + error(token, + "Redeclaration of `%.*s` in this scope\n" + "\tat %.*s(%td:%td)", + LIT(str), LIT(pos.file), pos.line, pos.column); + entity = found; } } + if (entity == nullptr) { + entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name)); + } + entity->parent_proc_decl = c->context.curr_proc_decl; + entities[entity_count++] = entity; + } - for (isize i = 0; i < entity_count; i++) { - Entity *e = entities[i]; - GB_ASSERT(e != nullptr); - if (e->flags & EntityFlag_Visited) { - e->type = t_invalid; - continue; - } - e->flags |= EntityFlag_Visited; + Type *init_type = nullptr; + if (vd->type != nullptr) { + init_type = check_type(c, vd->type, nullptr); + if (init_type == nullptr) { + init_type = t_invalid; + } else if (is_type_polymorphic(base_type(init_type))) { + error(vd->type, "Invalid use of a polymorphic type in variable declaration"); + init_type = t_invalid; + } + } - if (e->type == nullptr) { - e->type = init_type; - } + for (isize i = 0; i < entity_count; i++) { + Entity *e = entities[i]; + GB_ASSERT(e != nullptr); + if (e->flags & EntityFlag_Visited) { + e->type = t_invalid; + continue; } + e->flags |= EntityFlag_Visited; - check_arity_match(c, vd); - check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration")); + if (e->type == nullptr) { + e->type = init_type; + } + } - for (isize i = 0; i < entity_count; i++) { - Entity *e = entities[i]; - if (e->Variable.is_foreign) { - if (vd->values.count > 0) { - error(e->token, "A foreign variable declaration cannot have a default value"); - } - init_entity_foreign_library(c, e); + check_arity_match(c, vd); + check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration")); - String name = e->token.string; - auto *fp = &c->info.foreigns; - HashKey key = hash_string(name); - Entity **found = map_get(fp, key); - if (found) { - Entity *f = *found; - TokenPos pos = f->token.pos; - Type *this_type = base_type(e->type); - Type *other_type = base_type(f->type); - if (!are_types_identical(this_type, other_type)) { - error(e->token, - "Foreign entity `%.*s` previously declared elsewhere with a different type\n" - "\tat %.*s(%td:%td)", - LIT(name), LIT(pos.file), pos.line, pos.column); - } - } else { - map_set(fp, key, e); + for (isize i = 0; i < entity_count; i++) { + Entity *e = entities[i]; + if (e->Variable.is_foreign) { + if (vd->values.count > 0) { + error(e->token, "A foreign variable declaration cannot have a default value"); + } + init_entity_foreign_library(c, e); + + String name = e->token.string; + auto *fp = &c->info.foreigns; + HashKey key = hash_string(name); + Entity **found = map_get(fp, key); + if (found) { + Entity *f = *found; + TokenPos pos = f->token.pos; + Type *this_type = base_type(e->type); + Type *other_type = base_type(f->type); + if (!are_types_identical(this_type, other_type)) { + error(e->token, + "Foreign entity `%.*s` previously declared elsewhere with a different type\n" + "\tat %.*s(%td:%td)", + LIT(name), LIT(pos.file), pos.line, pos.column); } + } else { + map_set(fp, key, e); } - add_entity(c, c->context.scope, e->identifier, e); } + add_entity(c, c->context.scope, e->identifier, e); + } - if ((vd->flags & VarDeclFlag_using) != 0) { - Token token = ast_node_token(node); - if (vd->type != nullptr && entity_count > 1) { - error(token, "`using` can only be applied to one variable of the same type"); - // TODO(bill): Should a `continue` happen here? - } + if ((vd->flags & VarDeclFlag_using) != 0) { + Token token = ast_node_token(node); + if (vd->type != nullptr && entity_count > 1) { + error(token, "`using` can only be applied to one variable of the same type"); + // TODO(bill): Should a `continue` happen here? + } - for (isize entity_index = 0; entity_index < entity_count; entity_index++) { - Entity *e = entities[entity_index]; - if (e == nullptr) { - continue; - } - if (e->kind != Entity_Variable) { - continue; - } - bool is_immutable = e->Variable.is_immutable; - String name = e->token.string; - Type *t = base_type(type_deref(e->type)); - - if (is_type_struct(t) || is_type_raw_union(t)) { - Scope *scope = scope_of_node(&c->info, t->Record.node); - for_array(i, scope->elements.entries) { - Entity *f = scope->elements.entries[i].value; - if (f->kind == Entity_Variable) { - Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type); - uvar->Variable.is_immutable = is_immutable; - Entity *prev = scope_insert_entity(c->context.scope, uvar); - if (prev != nullptr) { - error(token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string)); - return; - } + for (isize entity_index = 0; entity_index < entity_count; entity_index++) { + Entity *e = entities[entity_index]; + if (e == nullptr) { + continue; + } + if (e->kind != Entity_Variable) { + continue; + } + bool is_immutable = e->Variable.is_immutable; + String name = e->token.string; + Type *t = base_type(type_deref(e->type)); + + if (is_type_struct(t) || is_type_raw_union(t)) { + Scope *scope = scope_of_node(&c->info, t->Record.node); + for_array(i, scope->elements.entries) { + Entity *f = scope->elements.entries[i].value; + if (f->kind == Entity_Variable) { + Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type); + uvar->Variable.is_immutable = is_immutable; + Entity *prev = scope_insert_entity(c->context.scope, uvar); + if (prev != nullptr) { + error(token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string)); + return; } } - } else { - // NOTE(bill): skip the rest to remove extra errors - error(token, "`using` can only be applied to variables of type struct or raw_union"); - return; } + } else { + // NOTE(bill): skip the rest to remove extra errors + error(token, "`using` can only be applied to variables of type struct or raw_union"); + return; } } } diff --git a/src/checker.cpp b/src/checker.cpp index d7882414a..1cbf5db40 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -301,6 +301,7 @@ struct CheckerInfo { Map<ExprInfo> untyped; // Key: AstNode * | Expression -> ExprInfo Map<Entity *> implicits; // Key: AstNode * Map<Array<Entity *> > gen_procs; // Key: AstNode * | Identifier -> Entity + Map<Array<Entity *> > gen_types; // Key: Type * Map<DeclInfo *> entities; // Key: Entity * Map<Entity *> foreigns; // Key: String Map<AstFile *> files; // Key: String (full path) @@ -742,6 +743,7 @@ void init_checker_info(CheckerInfo *i) { map_init(&i->foreigns, a); map_init(&i->implicits, a); map_init(&i->gen_procs, a); + map_init(&i->gen_types, a); map_init(&i->type_info_map, a); map_init(&i->files, a); i->type_info_count = 0; @@ -758,6 +760,7 @@ void destroy_checker_info(CheckerInfo *i) { map_destroy(&i->foreigns); map_destroy(&i->implicits); map_destroy(&i->gen_procs); + map_destroy(&i->gen_types); map_destroy(&i->type_info_map); map_destroy(&i->files); } diff --git a/src/ir.cpp b/src/ir.cpp index 8746b676b..3d9ef820a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3670,6 +3670,18 @@ void ir_pop_target_list(irProcedure *proc) { void ir_gen_global_type_name(irModule *m, Entity *e, String name) { if (e->type == nullptr) return; + if (is_type_polymorphic(base_type(e->type))) { + auto found = map_get(&m->info->gen_types, hash_pointer(e->type)); + if (found == nullptr) { + return; + } + for_array(i, *found) { + Entity *e = (*found)[i]; + ir_mangle_add_sub_type_name(m, e, name); + } + return; + } + irValue *t = ir_value_type_name(m->allocator, name, e->type); ir_module_add_value(m, e, t); map_set(&m->members, hash_string(name), t); @@ -5731,8 +5743,10 @@ void ir_build_constant_value_decl(irProcedure *proc, AstNodeValueDecl *vd) { } bool polymorphic = is_type_polymorphic(e->type); - - if (!polymorphic && map_get(&proc->module->min_dep_map, hash_pointer(e)) == nullptr) { + if (polymorphic && !is_type_struct(e->type)) { + continue; + } + if (map_get(&proc->module->min_dep_map, hash_pointer(e)) == nullptr) { // NOTE(bill): Nothing depends upon it so doesn't need to be built continue; } @@ -5741,6 +5755,7 @@ void ir_build_constant_value_decl(irProcedure *proc, AstNodeValueDecl *vd) { // NOTE(bill): Generate a new name // parent_proc.name-guid String ts_name = e->token.string; + isize name_len = proc->name.len + 1 + ts_name.len + 1 + 10 + 1; u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len); i32 guid = cast(i32)proc->module->members.entries.count; @@ -5751,6 +5766,7 @@ void ir_build_constant_value_decl(irProcedure *proc, AstNodeValueDecl *vd) { name, e->type); map_set(&proc->module->entity_names, hash_entity(e), name); ir_gen_global_type_name(proc->module, e, name); + } else if (e->kind == Entity_Procedure) { CheckerInfo *info = proc->module->info; DeclInfo *decl = decl_info_of_entity(info, e); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 7bd79fe2e..c683f4dc6 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -340,7 +340,7 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) { case Type_Named: if (is_type_struct(t) || is_type_union(t)) { String *name = map_get(&m->entity_names, hash_pointer(t->Named.type_name)); - GB_ASSERT_MSG(name != nullptr, "%.*s", LIT(t->Named.name)); + GB_ASSERT_MSG(name != nullptr, "%.*s %p", LIT(t->Named.name), t->Named.type_name); ir_print_encoded_local(f, *name); } else { ir_print_type(f, m, base_type(t)); diff --git a/src/parser.cpp b/src/parser.cpp index a2d8ab1f9..ca5e59983 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -111,6 +111,7 @@ enum StmtStateFlag { }; enum FieldFlag { + FieldFlag_NONE = 0, FieldFlag_ellipsis = 1<<0, FieldFlag_using = 1<<1, FieldFlag_no_alias = 1<<2, @@ -418,12 +419,13 @@ AST_NODE_KIND(_TypeBegin, "", i32) \ AstNode *elem; \ }) \ AST_NODE_KIND(StructType, "struct type", struct { \ - Token token; \ - Array<AstNode *> fields; \ - isize field_count; \ - bool is_packed; \ - bool is_ordered; \ - AstNode *align; \ + Token token; \ + Array<AstNode *> fields; \ + isize field_count; \ + AstNode * polymorphic_params; \ + bool is_packed; \ + bool is_ordered; \ + AstNode * align; \ }) \ AST_NODE_KIND(UnionType, "union type", struct { \ Token token; \ @@ -866,6 +868,7 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) { break; case AstNode_StructType: n->StructType.fields = clone_ast_node_array(a, n->StructType.fields); + n->StructType.polymorphic_params = clone_ast_node(a, n->StructType.polymorphic_params); n->StructType.align = clone_ast_node(a, n->StructType.align); break; case AstNode_UnionType: @@ -1459,14 +1462,15 @@ AstNode *ast_vector_type(AstFile *f, Token token, AstNode *count, AstNode *elem) } AstNode *ast_struct_type(AstFile *f, Token token, Array<AstNode *> fields, isize field_count, - bool is_packed, bool is_ordered, AstNode *align) { + AstNode *polymorphic_params, bool is_packed, bool is_ordered, AstNode *align) { AstNode *result = make_ast_node(f, AstNode_StructType); - result->StructType.token = token; - result->StructType.fields = fields; - result->StructType.field_count = field_count; - result->StructType.is_packed = is_packed; - result->StructType.is_ordered = is_ordered; - result->StructType.align = align; + result->StructType.token = token; + result->StructType.fields = fields; + result->StructType.field_count = field_count; + result->StructType.polymorphic_params = polymorphic_params; + result->StructType.is_packed = is_packed; + result->StructType.is_ordered = is_ordered; + result->StructType.align = align; return result; } @@ -2182,6 +2186,8 @@ AstNode * parse_simple_stmt (AstFile *f, StmtAllowFlag flags); AstNode * parse_type (AstFile *f); AstNode * parse_call_expr (AstFile *f, AstNode *operand); AstNode * parse_record_field_list(AstFile *f, isize *name_count_); +AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters, bool allow_type_token); + AstNode *convert_stmt_to_expr(AstFile *f, AstNode *statement, String kind) { if (statement == nullptr) { @@ -2205,7 +2211,7 @@ AstNode *convert_stmt_to_body(AstFile *f, AstNode *stmt) { syntax_error(stmt, "Expected a normal statement rather than a block statement"); return stmt; } - GB_ASSERT(is_ast_node_stmt(stmt)); + GB_ASSERT(is_ast_node_stmt(stmt) || is_ast_node_decl(stmt)); Token open = ast_node_token(stmt); Token close = ast_node_token(stmt); Array<AstNode *> stmts = make_ast_node_array(f, 1); @@ -2430,10 +2436,21 @@ AstNode *parse_operand(AstFile *f, bool lhs) { case Token_struct: { Token token = expect_token(f, Token_struct); + AstNode *polymorphic_params = nullptr; bool is_packed = false; bool is_ordered = false; AstNode *align = nullptr; + if (allow_token(f, Token_OpenParen)) { + isize param_count = 0; + polymorphic_params = parse_field_list(f, ¶m_count, 0, Token_CloseParen, false, true); + if (param_count == 0) { + syntax_error(polymorphic_params, "Expected at least 1 polymorphic parametric"); + polymorphic_params = nullptr; + } + expect_token_after(f, Token_CloseParen, "parameter list"); + } + isize prev_level = f->expr_level; f->expr_level = -1; @@ -2477,7 +2494,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) { decls = fields->FieldList.list; } - return ast_struct_type(f, token, decls, name_count, is_packed, is_ordered, align); + return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_ordered, align); } break; case Token_union: { @@ -3394,7 +3411,6 @@ AstNode *parse_block_stmt(AstFile *f, b32 is_when) { return parse_body(f); } -AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters); AstNode *parse_results(AstFile *f) { @@ -3414,7 +3430,7 @@ AstNode *parse_results(AstFile *f) { AstNode *list = nullptr; expect_token(f, Token_OpenParen); - list = parse_field_list(f, nullptr, 0, Token_CloseParen, true); + list = parse_field_list(f, nullptr, 0, Token_CloseParen, true, false); expect_token_after(f, Token_CloseParen, "parameter list"); return list; } @@ -3424,7 +3440,7 @@ AstNode *parse_proc_type(AstFile *f, Token proc_token, String *link_name_) { AstNode *results = nullptr; expect_token(f, Token_OpenParen); - params = parse_field_list(f, nullptr, FieldFlag_Signature, Token_CloseParen, true); + params = parse_field_list(f, nullptr, FieldFlag_Signature, Token_CloseParen, true, true); expect_token_after(f, Token_CloseParen, "parameter list"); results = parse_results(f); @@ -3669,7 +3685,7 @@ AstNode *parse_record_field_list(AstFile *f, isize *name_count_) { return ast_field_list(f, start_token, decls); } -AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters) { +AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters, bool allow_type_token) { TokenKind separator = Token_Comma; Token start_token = f->curr_token; @@ -3682,7 +3698,6 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok isize total_name_count = 0; bool allow_ellipsis = allowed_flags&FieldFlag_ellipsis; - bool allow_type_token = allow_default_parameters; while (f->curr_token.kind != follow && f->curr_token.kind != Token_Colon && @@ -3691,10 +3706,9 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok AstNode *param = parse_var_type(f, allow_ellipsis, allow_type_token); AstNodeAndFlags naf = {param, flags}; array_add(&list, naf); - if (f->curr_token.kind != Token_Comma) { + if (!allow_token(f, Token_Comma)) { break; } - advance_token(f); } if (f->curr_token.kind == Token_Colon) { @@ -3750,7 +3764,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok AstNode *default_value = nullptr; expect_token_after(f, Token_Colon, "field list"); if (f->curr_token.kind != Token_Eq) { - type = parse_var_type(f, allow_ellipsis, allow_default_parameters); + type = parse_var_type(f, allow_ellipsis, allow_type_token); } if (allow_token(f, Token_Eq)) { // TODO(bill): Should this be true==lhs or false==rhs? diff --git a/src/types.cpp b/src/types.cpp index d25f3fba9..5f3bee127 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -94,6 +94,8 @@ struct TypeRecord { bool are_offsets_being_processed; bool is_packed; bool is_ordered; + bool is_polymorphic; + Type * polymorphic_params; // Type_Tuple i64 custom_align; // NOTE(bill): Only used in structs at the moment Entity * names; @@ -937,6 +939,15 @@ bool is_type_indexable(Type *t) { return is_type_array(t) || is_type_slice(t) || is_type_vector(t) || is_type_string(t); } +bool is_type_polymorphic_struct(Type *t) { + t = base_type(t); + if (t->kind == Type_Record && + t->Record.kind == TypeRecord_Struct) { + return t->Record.is_polymorphic; + } + return false; +} + bool is_type_polymorphic(Type *t) { switch (t->kind) { case Type_Generic: @@ -967,7 +978,7 @@ bool is_type_polymorphic(Type *t) { if (t->Proc.is_polymorphic) { return true; } - #if 0 + #if 1 if (t->Proc.param_count > 0 && is_type_polymorphic(t->Proc.params)) { return true; @@ -995,6 +1006,9 @@ bool is_type_polymorphic(Type *t) { } break; case Type_Record: + if (t->Record.is_polymorphic) { + return true; + } for (isize i = 0; i < t->Record.field_count; i++) { if (is_type_polymorphic(t->Record.fields[i]->type)) { return true; |