mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-17 11:22:22 -07:00
Rudimentary support for parametric polymorphic types
This commit is contained in:
+1
-1
@@ -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;
|
||||
}
|
||||
|
||||
+468
-71
@@ -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 (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
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if (!ast_node_expect(name, AstNode_Ident)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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 (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
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
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;
|
||||
}
|
||||
} 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
|
||||
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;
|
||||
}
|
||||
check_expr(c, operand, arg);
|
||||
if (operand->mode != Addressing_Invalid) {
|
||||
check_cast(c, operand, t);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
return Expr_Expr;
|
||||
}
|
||||
|
||||
|
||||
+129
-128
@@ -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");
|
||||
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 {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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"
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (e->type == nullptr) {
|
||||
e->type = init_type;
|
||||
}
|
||||
}
|
||||
|
||||
check_arity_match(c, vd);
|
||||
check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration"));
|
||||
|
||||
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(str), LIT(pos.file), pos.line, pos.column);
|
||||
entity = found;
|
||||
LIT(name), LIT(pos.file), pos.line, pos.column);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
} else {
|
||||
map_set(fp, key, e);
|
||||
}
|
||||
}
|
||||
add_entity(c, c->context.scope, e->identifier, e);
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
e->flags |= EntityFlag_Visited;
|
||||
|
||||
if (e->type == nullptr) {
|
||||
e->type = init_type;
|
||||
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));
|
||||
|
||||
check_arity_match(c, vd);
|
||||
check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration"));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+18
-2
@@ -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);
|
||||
|
||||
+1
-1
@@ -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));
|
||||
|
||||
+37
-23
@@ -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?
|
||||
|
||||
+15
-1
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user