aboutsummaryrefslogtreecommitdiff
path: root/src/check_expr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/check_expr.cpp')
-rw-r--r--src/check_expr.cpp180
1 files changed, 157 insertions, 23 deletions
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 557b57d8c..98a0614a7 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -1073,14 +1073,105 @@ void check_bit_field_type(Checker *c, Type *bit_field_type, Type *named_type, As
}
}
+bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool compound) {
+ Operand o = {Addressing_Value};
+ o.type = source;
+ switch (poly->kind) {
+ case Type_Basic:
+ if (compound) return are_types_identical(poly, source);
+ return check_is_assignable_to(c, &o, poly);
+ case Type_Named:
+ if (compound) return are_types_identical(poly, source);
+ return check_is_assignable_to(c, &o, poly);
+
+ case Type_Generic: {
+ Type *ds = default_type(source);
+ gb_memmove(poly, ds, gb_size_of(Type));
+ return true;
+ }
+ case Type_Pointer:
+ if (source->kind == Type_Pointer) {
+ return is_polymorphic_type_assignable(c, poly->Pointer.elem, source->Pointer.elem, true);
+ }
+ return false;
+ case Type_Atomic:
+ if (source->kind == Type_Atomic) {
+ return is_polymorphic_type_assignable(c, poly->Atomic.elem, source->Atomic.elem, true);
+ }
+ return false;
+ case Type_Array:
+ if (source->kind == Type_Array &&
+ poly->Array.count == source->Array.count) {
+ return is_polymorphic_type_assignable(c, poly->Array.elem, source->Array.elem, true);
+ }
+ return false;
+ case Type_DynamicArray:
+ if (source->kind == Type_DynamicArray) {
+ return is_polymorphic_type_assignable(c, poly->DynamicArray.elem, source->DynamicArray.elem, true);
+ }
+ return false;
+ case Type_Vector:
+ if (source->kind == Type_Vector &&
+ poly->Vector.count == source->Vector.count) {
+ return is_polymorphic_type_assignable(c, poly->Vector.elem, source->Vector.elem, true);
+ }
+ return false;
+ case Type_Slice:
+ if (source->kind == Type_Slice) {
+ return is_polymorphic_type_assignable(c, poly->Slice.elem, source->Slice.elem, true);
+ }
+ return false;
+ case Type_Record:
+ if (source->kind == Type_Record) {
+ // TODO(bill): Polymorphic type assignment
+ }
+ return false;
+ case Type_Tuple:
+ GB_PANIC("This should never happen");
+ return false;
+ case Type_Proc:
+ if (source->kind == Type_Proc) {
+ // TODO(bill): Polymorphic type assignment
+ }
+ return false;
+ case Type_Map:
+ if (source->kind == Type_Map) {
+ bool key = is_polymorphic_type_assignable(c, poly->Map.key, source->Map.key, true);
+ bool value = is_polymorphic_type_assignable(c, poly->Map.value, source->Map.value, true);
+ return key || value;
+ }
+ return false;
+ }
+ return false;
+}
+Type *determine_type_from_polymorphic(Checker *c, Type *poly_type, Operand operand) {
+ if (!is_operand_value(operand)) {
+ error(operand.expr, "Cannot determine polymorphic type from parameter");
+ return t_invalid;
+ }
+ if (is_polymorphic_type_assignable(c, poly_type, operand.type, false)) {
+ return poly_type;
+ }
+ gbString pts = type_to_string(poly_type);
+ gbString ots = type_to_string(operand.type);
+ defer (gb_string_free(pts));
+ defer (gb_string_free(ots));
+ error(operand.expr,
+ "Cannot determine polymorphic type from parameter: `%s` to `%s`\n"
+ "\tNote: Record and procedure types are not yet supported",
+ ots, pts);
+ return t_invalid;
+}
Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_variadic_, bool *success_, Array<Operand> *operands) {
if (_params == NULL) {
return NULL;
}
+ bool allow_polymorphic_types = c->context.allow_polymorphic_types;
+
bool success = true;
ast_node(field_list, FieldList, _params);
Array<AstNode *> params = field_list->list;
@@ -1123,6 +1214,8 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
bool default_is_nil = false;
bool default_is_location = false;
bool is_type_param = false;
+ bool is_type_polymorphic_type = false;
+ bool detemine_type_from_operand = false;
if (type_expr == NULL) {
@@ -1157,24 +1250,23 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
if (type_expr->kind == AstNode_HelperType) {
is_type_param = true;
if (operands != NULL) {
- Operand o = (*operands)[variable_index];
- if (o.mode == Addressing_Type) {
- type = o.type;
- } else {
- error(o.expr, "Expected a type to assign to the type parameter");
- type = t_invalid;
- success = false;
- }
+ detemine_type_from_operand = true;
+ type = t_invalid;
} else {
type = make_type_generic(c->allocator, 0, str_lit(""));
}
} else {
- type = check_type(c, type_expr);
- if (p->flags&FieldFlag_dollar) {
- error(type_expr, "`$` is only allowed for polymorphic type parameters at the moment");
- type = NULL;
+ bool prev = c->context.allow_polymorphic_types;
+ if (operands != NULL) {
+ c->context.allow_polymorphic_types = true;
}
+ type = check_type(c, type_expr);
+
+ c->context.allow_polymorphic_types = prev;
+ if (is_type_polymorphic(type)) {
+ is_type_polymorphic_type = true;
+ }
}
if (default_value != NULL) {
@@ -1218,12 +1310,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
type = t_invalid;
}
- 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
- }
- }
+
if (p->flags&FieldFlag_c_vararg) {
if (p->type == NULL ||
p->type->kind != AstNode_Ellipsis) {
@@ -1234,19 +1321,44 @@ 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 = NULL;
if (is_type_param) {
+ if (operands != NULL) {
+ Operand o = (*operands)[variable_index];
+ if (o.mode == Addressing_Type) {
+ type = o.type;
+ } else {
+ error(o.expr, "Expected a type to assign to the type parameter");
+ type = t_invalid;
+ }
+ }
param = make_entity_type_name(c->allocator, scope, name->Ident.token, type);
param->TypeName.is_type_alias = true;
} else {
+ if (operands != NULL && is_type_polymorphic_type) {
+ type = determine_type_from_polymorphic(c, type, (*operands)[variable_index]);
+ 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;
@@ -1629,11 +1741,12 @@ bool check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node, Array
if (e->kind != Entity_Variable) {
is_polymorphic = true;
break;
+ } else if (is_type_polymorphic(e->type)) {
+ is_polymorphic = true;
+ break;
}
}
- if (operands == NULL) {
- // GB_ASSERT(type->Proc.is_polymorphic == is_polymorphic);
- }
+ type->Proc.is_polymorphic = is_polymorphic;
type->Proc.abi_compat_params = gb_alloc_array(c->allocator, Type *, param_count);
@@ -2005,6 +2118,26 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type)
}
case_end;
+ case_ast_node(pt, PolyType, e);
+ AstNode *ident = pt->type;
+ if (ident->kind != AstNode_Ident) {
+ error(ident, "Expected an identifier after the $");
+ *type = t_invalid;
+ return false;
+ }
+
+ Token token = ident->Ident.token;
+ Type *t = make_type_generic(c->allocator, 0, token.string);
+ if (c->context.allow_polymorphic_types) {
+ Scope *s = c->context.scope;
+ Entity *e = make_entity_type_name(c->allocator, s, token, t);
+ e->TypeName.is_type_alias = true;
+ add_entity(c, s, ident, e);
+ }
+ *type = t;
+ return true;
+ case_end;
+
case_ast_node(se, SelectorExpr, e);
Operand o = {};
check_selector(c, &o, e, NULL);
@@ -2224,13 +2357,14 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type) {
}
}
- // if (is_type_polymorphic(type)) {
- if (is_type_poly_proc(type)) {
+ #if 0
+ if (!c->context.allow_polymorphic_types && is_type_polymorphic(type)) {
gbString str = type_to_string(type);
error(e, "Invalid use of a polymorphic type `%s`", str);
gb_string_free(str);
type = t_invalid;
}
+ #endif
if (is_type_typed(type)) {
add_type_and_value(&c->info, e, Addressing_Type, type, empty_exact_value);