mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-19 04:50:29 +00:00
Polymorphic type specialization for procedures
This commit is contained in:
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2016 Ginger Bill. All rights reserved.
|
||||
Copyright (c) 2016-2017 Ginger Bill. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
168
code/demo.odin
168
code/demo.odin
@@ -1,5 +1,116 @@
|
||||
import "fmt.odin";
|
||||
|
||||
Table :: struct(Key, Value: type) {
|
||||
Slot :: struct {
|
||||
occupied: bool;
|
||||
hash: u32;
|
||||
key: Key;
|
||||
value: Value;
|
||||
}
|
||||
SIZE_MIN :: 32;
|
||||
|
||||
count: int;
|
||||
allocator: Allocator;
|
||||
slots: []Slot;
|
||||
}
|
||||
|
||||
allocate :: proc(table: ^$T/Table, capacity: int) {
|
||||
c := context;
|
||||
if table.allocator.procedure != nil do c.allocator = table.allocator;
|
||||
|
||||
push_context c {
|
||||
table.slots = make([]T.Slot, max(capacity, T.SIZE_MIN));
|
||||
}
|
||||
}
|
||||
|
||||
expand :: proc(table: ^$T/Table) {
|
||||
c := context;
|
||||
if table.allocator.procedure != nil do c.allocator = table.allocator;
|
||||
|
||||
push_context c {
|
||||
old_slots := table.slots;
|
||||
|
||||
cap := max(2*cap(table.slots), T.SIZE_MIN);
|
||||
allocate(table, cap);
|
||||
|
||||
for s in old_slots do if s.occupied {
|
||||
put(table, s.key, s.value);
|
||||
}
|
||||
|
||||
free(old_slots);
|
||||
}
|
||||
}
|
||||
|
||||
put :: proc(table: ^$T/Table, key: T.Key, value: T.Value) {
|
||||
// put :: proc(table: ^Table($K, $V), key: K, value: V) {
|
||||
hash := get_hash(key); // Ad-hoc method which would fail in differentcope
|
||||
index := find_index(table, key, hash);
|
||||
if index < 0 {
|
||||
if f64(table.count) >= 0.75*cast(f64)cap(table.slots) {
|
||||
expand(table);
|
||||
}
|
||||
assert(table.count <= cap(table.slots));
|
||||
|
||||
hash := get_hash(key);
|
||||
index = cast(int)(hash % cast(u32)cap(table.slots));
|
||||
|
||||
for table.slots[index].occupied {
|
||||
index += 1;
|
||||
if index >= cap(table.slots) {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
table.count++;
|
||||
}
|
||||
|
||||
slot := &table.slots[index];
|
||||
slot.occupied = true;
|
||||
slot.hash = hash;
|
||||
slot.key = key;
|
||||
slot.value = value;
|
||||
}
|
||||
|
||||
|
||||
// find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) {
|
||||
find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) {
|
||||
hash := get_hash(key);
|
||||
index := find_index(table, key, hash);
|
||||
if index < 0 {
|
||||
return Value{}, false;
|
||||
}
|
||||
return table.slots[index].value, true;
|
||||
}
|
||||
|
||||
find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int {
|
||||
if cap(table.slots) <= 0 do return -1;
|
||||
|
||||
slot := int(hash % cast(u32)cap(table.slots));
|
||||
|
||||
index := slot;
|
||||
for table.slots[index].occupied {
|
||||
if table.slots[index].hash == hash {
|
||||
if table.slots[index].key == key {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
if index >= cap(table.slots) do index = 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
get_hash :: proc(s: string) -> u32 {
|
||||
// djb2
|
||||
hash: u32 = 5381;
|
||||
for i in 0..len(s) do hash = (hash<<5) + hash + u32(s[i]);
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vector :: struct(N: int, T: type) {
|
||||
using _: raw_union {
|
||||
using e: [N]T;
|
||||
@@ -16,14 +127,51 @@ Vector :: struct(N: int, T: type) {
|
||||
|
||||
Vector3 :: Vector(3, f32);
|
||||
|
||||
main :: proc() {
|
||||
v: Vector3;
|
||||
v[0] = 1;
|
||||
v[1] = 4;
|
||||
v[2] = 9;
|
||||
fmt.println(v.e);
|
||||
v.x = 4;
|
||||
v.y = 9;
|
||||
v.z = 16;
|
||||
fmt.println(v.v);
|
||||
add :: proc(a, b: $T/Vector) -> T {
|
||||
c := a;
|
||||
for i in 0..3 {
|
||||
c[i] += b[i];
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
foo1 :: proc(a: type/Vector) { fmt.println("foo1", a{}); }
|
||||
// foo2 :: proc(a: type/Vector(3, f32)) {}
|
||||
foo3 :: proc(a: type/Vector(3, $T)) {fmt.println("foo3", a{}); }
|
||||
// foo4 :: proc(a: type/Vector3) {}
|
||||
|
||||
|
||||
|
||||
main :: proc() {
|
||||
foo1(Vector(3, f32));
|
||||
foo1(Vector3);
|
||||
foo3(Vector(3, f32));
|
||||
foo3(Vector3);
|
||||
|
||||
|
||||
a, b: Vector3;
|
||||
a[0] = 1;
|
||||
a[1] = 4;
|
||||
a[2] = 9;
|
||||
|
||||
b.x = 3;
|
||||
b.y = 4;
|
||||
b.z = 5;
|
||||
|
||||
v := add(a, b);
|
||||
fmt.println(v.v);
|
||||
|
||||
|
||||
table: Table(string, int);
|
||||
|
||||
for i in 0..36 do put(&table, "Hellope", i);
|
||||
for i in 0..42 do put(&table, "World!", i);
|
||||
|
||||
|
||||
found, _ := find(&table, "Hellope");
|
||||
fmt.printf("found is %v\n", found);
|
||||
|
||||
found, _ = find(&table, "World!");
|
||||
fmt.printf("found is %v\n", found);
|
||||
|
||||
}
|
||||
|
||||
@@ -60,7 +60,9 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
|
||||
t = default_type(t);
|
||||
}
|
||||
if (is_type_polymorphic(t)) {
|
||||
error(e->token, "Invalid use of a polymorphic type in %.*s", LIT(context_name));
|
||||
gbString str = type_to_string(t);
|
||||
defer (gb_string_free(str));
|
||||
error(e->token, "Invalid use of a polymorphic type `%s` in %.*s", str, LIT(context_name));
|
||||
e->type = t_invalid;
|
||||
return nullptr;
|
||||
}
|
||||
@@ -400,17 +402,16 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
|
||||
check_open_scope(c, pl->type);
|
||||
defer (check_close_scope(c));
|
||||
|
||||
#if 0
|
||||
if (e->token.string == "sort") {
|
||||
gb_printf_err("%.*s\n", LIT(e->token.string));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
auto prev_context = c->context;
|
||||
c->context.allow_polymorphic_types = true;
|
||||
check_procedure_type(c, proc_type, pl->type);
|
||||
c->context = prev_context;
|
||||
|
||||
TypeProc *pt = &proc_type->Proc;
|
||||
|
||||
bool is_foreign = (pl->tags & ProcTag_foreign) != 0;
|
||||
bool is_link_name = (pl->tags & ProcTag_link_name) != 0;
|
||||
bool is_export = (pl->tags & ProcTag_export) != 0;
|
||||
@@ -419,7 +420,6 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
|
||||
bool is_require_results = (pl->tags & ProcTag_require_results) != 0;
|
||||
|
||||
|
||||
TypeProc *pt = &proc_type->Proc;
|
||||
|
||||
if (d->scope->is_file && e->token.string == "main") {
|
||||
if (pt->param_count != 0 ||
|
||||
@@ -559,7 +559,9 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count
|
||||
e->type = check_type(c, type_expr);
|
||||
}
|
||||
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));
|
||||
gbString str = type_to_string(e->type);
|
||||
defer (gb_string_free(str));
|
||||
error(e->token, "Invalid use of a polymorphic type `%s` in %.*s", str, LIT(context_name));
|
||||
e->type = t_invalid;
|
||||
}
|
||||
|
||||
|
||||
@@ -156,6 +156,7 @@ bool find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, Typ
|
||||
// //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
if (base_entity == nullptr) {
|
||||
return false;
|
||||
}
|
||||
@@ -163,6 +164,7 @@ bool find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, Typ
|
||||
if (!is_type_proc(base_entity->type)) {
|
||||
return false;
|
||||
}
|
||||
String name = base_entity->token.string;
|
||||
|
||||
Type *src = base_type(base_entity->type);
|
||||
Type *dst = nullptr;
|
||||
@@ -372,6 +374,9 @@ bool find_or_generate_polymorphic_procedure_from_parameters(Checker *c, Entity *
|
||||
return find_or_generate_polymorphic_procedure(c, base_entity, nullptr, operands, poly_proc_data, false);
|
||||
}
|
||||
|
||||
bool check_type_specialization_to(Checker *c, Type *type, Type *specialization, bool modify_type = false);
|
||||
bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool compound, bool modify_type);
|
||||
|
||||
|
||||
i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) {
|
||||
if (operand->mode == Addressing_Invalid ||
|
||||
@@ -467,10 +472,11 @@ i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) {
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (are_types_identical(dst, src) && (!is_type_named(dst) || !is_type_named(src))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (is_type_bit_field_value(operand->type) && is_type_integer(type)) {
|
||||
Type *bfv = base_type(operand->type);
|
||||
@@ -504,6 +510,13 @@ i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (is_type_polymorphic(dst) && !is_type_polymorphic(src)) {
|
||||
bool modify_type = !c->context.no_polymorphic_errors;
|
||||
if (is_polymorphic_type_assignable(c, type, s, false, modify_type)) {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_type_union(dst)) {
|
||||
for (isize i = 0; i < dst->Union.variant_count; i++) {
|
||||
Type *vt = dst->Union.variants[i];
|
||||
@@ -679,21 +692,21 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
|
||||
defer (gb_string_free(op_type_str));
|
||||
defer (gb_string_free(expr_str));
|
||||
|
||||
if (operand->mode == Addressing_Builtin) {
|
||||
// TODO(bill): is this a good enough error message?
|
||||
switch (operand->mode) {
|
||||
case Addressing_Builtin:
|
||||
// TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
|
||||
error(operand->expr,
|
||||
"Cannot assign built-in procedure `%s` in %.*s",
|
||||
expr_str,
|
||||
LIT(context_name));
|
||||
} else if (operand->mode == Addressing_Type) {
|
||||
// TODO(bill): is this a good enough error message?
|
||||
// TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
|
||||
break;
|
||||
case Addressing_Type:
|
||||
error(operand->expr,
|
||||
"Cannot assign `%s` which is a type in %.*s",
|
||||
op_type_str,
|
||||
LIT(context_name));
|
||||
} else {
|
||||
break;
|
||||
default:
|
||||
// TODO(bill): is this a good enough error message?
|
||||
error(operand->expr,
|
||||
"Cannot assign value `%s` of type `%s` to `%s` in %.*s",
|
||||
@@ -701,6 +714,7 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
|
||||
op_type_str,
|
||||
type_str,
|
||||
LIT(context_name));
|
||||
break;
|
||||
}
|
||||
operand->mode = Addressing_Invalid;
|
||||
|
||||
@@ -1087,7 +1101,8 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node, Array<Opera
|
||||
if (poly_operands != nullptr) {
|
||||
Operand operand = (*poly_operands)[entities.count];
|
||||
if (is_type_param) {
|
||||
GB_ASSERT(operand.mode == Addressing_Type);
|
||||
GB_ASSERT(operand.mode == Addressing_Type ||
|
||||
operand.mode == Addressing_Invalid);
|
||||
if (is_type_polymorphic(base_type(operand.type))) {
|
||||
is_polymorphic = true;
|
||||
can_check_fields = false;
|
||||
@@ -1545,19 +1560,59 @@ void check_bit_field_type(Checker *c, Type *bit_field_type, Type *named_type, As
|
||||
}
|
||||
}
|
||||
|
||||
bool is_polymorphic_type_assignable_to_specific(Checker *c, Type *source, Type *specific) {
|
||||
if (!is_type_struct(specific)) {
|
||||
return false;
|
||||
|
||||
bool check_type_specialization_to(Checker *c, Type *type, Type *specialization, bool modify_type) {
|
||||
if (type == nullptr ||
|
||||
type == t_invalid) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!is_type_struct(source)) {
|
||||
Type *t = base_type(type);
|
||||
Type *s = base_type(specialization);
|
||||
if (t->kind != s->kind) {
|
||||
return false;
|
||||
}
|
||||
// gb_printf_err("#1 %s %s\n", type_to_string(type), type_to_string(specialization));
|
||||
if (t->kind != Type_Record) {
|
||||
return false;
|
||||
}
|
||||
bool show_stuff = false && modify_type;
|
||||
|
||||
source = base_type(source);
|
||||
GB_ASSERT(source->kind == Type_Record && source->Record.kind == TypeRecord_Struct);
|
||||
if (show_stuff) gb_printf_err("#1 %s %s\n", type_to_string(type), type_to_string(specialization));
|
||||
if (t->Record.polymorphic_parent == specialization) {
|
||||
return true;
|
||||
}
|
||||
if (show_stuff) gb_printf_err("#2 %s %s\n", type_to_string(t->Record.polymorphic_parent), type_to_string(specialization));
|
||||
if (t->Record.polymorphic_parent == s->Record.polymorphic_parent) {
|
||||
GB_ASSERT(s->Record.polymorphic_params != nullptr);
|
||||
GB_ASSERT(t->Record.polymorphic_params != nullptr);
|
||||
|
||||
return are_types_identical(source->Record.polymorphic_parent, specific);
|
||||
if (show_stuff) gb_printf_err("#3 %s -> %s\n", type_to_string(type), type_to_string(specialization));
|
||||
|
||||
TypeTuple *s_tuple = &s->Record.polymorphic_params->Tuple;
|
||||
TypeTuple *t_tuple = &t->Record.polymorphic_params->Tuple;
|
||||
GB_ASSERT(t_tuple->variable_count == s_tuple->variable_count);
|
||||
for (isize i = 0; i < s_tuple->variable_count; i++) {
|
||||
Entity *s_e = s_tuple->variables[i];
|
||||
Entity *t_e = t_tuple->variables[i];
|
||||
Type *st = s_e->type;
|
||||
Type *tt = t_e->type;
|
||||
if (show_stuff) gb_printf_err("\t@ %s -> %s\n", type_to_string(st), type_to_string(tt));
|
||||
if (show_stuff && base_type(st)->kind == Type_Generic) gb_printf_err("\t$%.*s\n", LIT(base_type(st)->Generic.name));
|
||||
bool ok = is_polymorphic_type_assignable(c, st, tt, true, modify_type);
|
||||
if (show_stuff) gb_printf_err("\t$ %s -> %s\n\n", type_to_string(st), type_to_string(tt));
|
||||
}
|
||||
|
||||
|
||||
if (modify_type) {
|
||||
// NOTE(bill): This is needed in order to change the actual type but still have the types defined within it
|
||||
gb_memmove(specialization, type, gb_size_of(Type));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool compound, bool modify_type) {
|
||||
@@ -1565,17 +1620,21 @@ bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool c
|
||||
o.type = source;
|
||||
switch (poly->kind) {
|
||||
case Type_Basic:
|
||||
if (compound) return are_types_identical(poly, source);
|
||||
if (compound) return are_types_identical(source, poly);
|
||||
return check_is_assignable_to(c, &o, poly);
|
||||
|
||||
case Type_Named:
|
||||
case Type_Named: {
|
||||
if (check_type_specialization_to(c, source, poly, modify_type)) {
|
||||
return true;
|
||||
}
|
||||
if (compound) return are_types_identical(poly, source);
|
||||
return check_is_assignable_to(c, &o, poly);
|
||||
}
|
||||
|
||||
case Type_Generic: {
|
||||
if (poly->Generic.specific != nullptr) {
|
||||
Type *s = poly->Generic.specific;
|
||||
if (!is_polymorphic_type_assignable_to_specific(c, source, s)) {
|
||||
if (poly->Generic.specialized != nullptr) {
|
||||
Type *s = poly->Generic.specialized;
|
||||
if (!check_type_specialization_to(c, source, s, modify_type)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1587,7 +1646,7 @@ bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool c
|
||||
}
|
||||
case Type_Pointer:
|
||||
if (source->kind == Type_Pointer) {
|
||||
return is_polymorphic_type_assignable(c, poly->Pointer.elem, source->Atomic.elem, true, modify_type);
|
||||
return is_polymorphic_type_assignable(c, poly->Pointer.elem, source->Pointer.elem, true, modify_type);
|
||||
}
|
||||
return false;
|
||||
case Type_Atomic:
|
||||
@@ -1640,7 +1699,6 @@ bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool c
|
||||
|
||||
case Type_Record:
|
||||
if (source->kind == Type_Record) {
|
||||
// TODO(bill): Polymorphic type assignment
|
||||
// return check_is_assignable_to(c, &o, poly);
|
||||
}
|
||||
return false;
|
||||
@@ -1718,33 +1776,6 @@ Type *determine_type_from_polymorphic(Checker *c, Type *poly_type, Operand opera
|
||||
return t_invalid;
|
||||
}
|
||||
|
||||
bool check_type_specialization_to(Checker *c, Type *type, Type *specialization) {
|
||||
if (type == nullptr ||
|
||||
type == t_invalid) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Type *t = base_type(type);
|
||||
Type *s = base_type(specialization);
|
||||
if (t->kind != s->kind) {
|
||||
return false;
|
||||
}
|
||||
// gb_printf_err("#1 %s %s\n", type_to_string(type), type_to_string(specialization));
|
||||
if (t->kind != Type_Record) {
|
||||
return false;
|
||||
}
|
||||
// gb_printf_err("#2 %s %s\n", type_to_string(type), type_to_string(specialization));
|
||||
if (t->Record.polymorphic_parent == specialization) {
|
||||
return true;
|
||||
}
|
||||
// gb_printf_err("#3 %s %s\n", type_to_string(t->Record.polymorphic_parent), type_to_string(specialization));
|
||||
if (t->Record.polymorphic_parent == s->Record.polymorphic_parent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_variadic_, bool *success_, Array<Operand> *operands) {
|
||||
if (_params == nullptr) {
|
||||
@@ -4323,7 +4354,7 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h
|
||||
Entity *e = scope_lookup_entity(c->context.scope, op_name);
|
||||
|
||||
bool is_alias = false;
|
||||
while (e->kind == Entity_Alias) {
|
||||
while (e != nullptr && e->kind == Entity_Alias) {
|
||||
GB_ASSERT(e->Alias.base != nullptr);
|
||||
e = e->Alias.base;
|
||||
is_alias = true;
|
||||
@@ -6549,6 +6580,10 @@ CallArgumentError check_polymorphic_struct_type(Checker *c, Operand *operand, As
|
||||
Entity *p = tuple->variables[j];
|
||||
Operand o = ordered_operands[j];
|
||||
if (p->kind == Entity_TypeName) {
|
||||
if (is_type_polymorphic(o.type)) {
|
||||
// NOTE(bill): Do not add polymorphic version to the gen_types
|
||||
ok = false;
|
||||
}
|
||||
if (!are_types_identical(o.type, p->type)) {
|
||||
ok = false;
|
||||
}
|
||||
@@ -7619,12 +7654,14 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t
|
||||
|
||||
if (!valid) {
|
||||
gbString str = expr_to_string(o->expr);
|
||||
gbString type_str = type_to_string(o->type);
|
||||
defer (gb_string_free(str));
|
||||
defer (gb_string_free(type_str));
|
||||
if (is_const) {
|
||||
error(o->expr, "Cannot index a constant `%s`", str);
|
||||
} else {
|
||||
error(o->expr, "Cannot index `%s`", str);
|
||||
error(o->expr, "Cannot index `%s` of type `%s`", str, type_str);
|
||||
}
|
||||
gb_string_free(str);
|
||||
o->mode = Addressing_Invalid;
|
||||
o->expr = node;
|
||||
return kind;
|
||||
@@ -8009,6 +8046,12 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
|
||||
str = write_expr_to_string(str, ta->type);
|
||||
str = gb_string_appendc(str, ")");
|
||||
case_end;
|
||||
case_ast_node(tc, TypeCast, node);
|
||||
str = gb_string_appendc(str, "cast(");
|
||||
str = write_expr_to_string(str, tc->type);
|
||||
str = gb_string_appendc(str, ")");
|
||||
str = write_expr_to_string(str, tc->expr);
|
||||
case_end;
|
||||
|
||||
case_ast_node(ie, IndexExpr, node);
|
||||
str = write_expr_to_string(str, ie->expr);
|
||||
|
||||
@@ -1608,16 +1608,21 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
Entity *e = nullptr;
|
||||
|
||||
bool is_selector = false;
|
||||
if (expr->kind == AstNode_Ident) {
|
||||
Operand o = {};
|
||||
Operand o = {};
|
||||
switch (expr->kind) {
|
||||
case AstNode_Ident:
|
||||
e = check_ident(c, &o, expr, nullptr, nullptr, true);
|
||||
} else if (expr->kind == AstNode_SelectorExpr) {
|
||||
Operand o = {};
|
||||
break;
|
||||
case AstNode_SelectorExpr:
|
||||
e = check_selector(c, &o, expr, nullptr);
|
||||
is_selector = true;
|
||||
} else if (expr->kind == AstNode_Implicit) {
|
||||
break;
|
||||
case AstNode_Implicit:
|
||||
error(us->token, "`using` applied to an implicit value");
|
||||
continue;
|
||||
default:
|
||||
error(us->token, "`using` can only be applied to an entity, got %.*s", LIT(ast_node_strings[expr->kind]));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!check_using_stmt_entity(c, us, expr, is_selector, e)) {
|
||||
@@ -1722,7 +1727,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
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");
|
||||
gbString str = type_to_string(init_type);
|
||||
error(vd->type, "Invalid use of a polymorphic type `%s` in variable declaration", str);
|
||||
gb_string_free(str);
|
||||
init_type = t_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
29
src/ir.cpp
29
src/ir.cpp
@@ -2826,6 +2826,9 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
|
||||
}
|
||||
|
||||
if (are_types_identical(src, dst)) {
|
||||
if (!are_types_identical(src_type, t)) {
|
||||
return ir_emit_transmute(proc, value, t);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -5752,19 +5755,24 @@ void ir_build_constant_value_decl(irProcedure *proc, AstNodeValueDecl *vd) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool polymorphic = is_type_polymorphic(e->type);
|
||||
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;
|
||||
}
|
||||
String entity_name = e->token.string;
|
||||
|
||||
if (e->kind == Entity_TypeName) {
|
||||
bool polymorphic_struct = false;
|
||||
if (e->type != nullptr && e->kind == Entity_TypeName) {
|
||||
Type *bt = base_type(e->type);
|
||||
if (bt->kind == Type_Record) {
|
||||
polymorphic_struct = bt->Record.is_polymorphic;
|
||||
}
|
||||
}
|
||||
|
||||
if (!polymorphic_struct && map_get(&proc->module->min_dep_map, hash_pointer(e)) == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE(bill): Generate a new name
|
||||
// parent_proc.name-guid
|
||||
String ts_name = e->token.string;
|
||||
String ts_name = entity_name;
|
||||
|
||||
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);
|
||||
@@ -5787,6 +5795,9 @@ void ir_build_constant_value_decl(irProcedure *proc, AstNodeValueDecl *vd) {
|
||||
auto procs = *found;
|
||||
for_array(i, procs) {
|
||||
Entity *e = procs[i];
|
||||
if (map_get(&proc->module->min_dep_map, hash_pointer(e)) == nullptr) {
|
||||
continue;
|
||||
}
|
||||
DeclInfo *d = decl_info_of_entity(info, e);
|
||||
ir_build_poly_proc(proc, &d->proc_lit->ProcLit, e);
|
||||
}
|
||||
|
||||
@@ -2215,6 +2215,9 @@ AstNode *convert_stmt_to_body(AstFile *f, AstNode *stmt) {
|
||||
syntax_error(stmt, "Expected a normal statement rather than a block statement");
|
||||
return stmt;
|
||||
}
|
||||
if (stmt->kind == AstNode_EmptyStmt) {
|
||||
syntax_error(stmt, "Expected a non-empty statement");
|
||||
}
|
||||
GB_ASSERT(is_ast_node_stmt(stmt) || is_ast_node_decl(stmt));
|
||||
Token open = ast_node_token(stmt);
|
||||
Token close = ast_node_token(stmt);
|
||||
|
||||
@@ -108,7 +108,7 @@ struct TypeRecord {
|
||||
TYPE_KIND(Generic, struct { \
|
||||
i64 id; \
|
||||
String name; \
|
||||
Type * specific; \
|
||||
Type * specialized; \
|
||||
}) \
|
||||
TYPE_KIND(Pointer, struct { Type *elem; }) \
|
||||
TYPE_KIND(Atomic, struct { Type *elem; }) \
|
||||
@@ -486,11 +486,11 @@ Type *make_type_basic(gbAllocator a, BasicType basic) {
|
||||
return t;
|
||||
}
|
||||
|
||||
Type *make_type_generic(gbAllocator a, i64 id, String name, Type *specific) {
|
||||
Type *make_type_generic(gbAllocator a, i64 id, String name, Type *specialized) {
|
||||
Type *t = alloc_type(a, Type_Generic);
|
||||
t->Generic.id = id;
|
||||
t->Generic.name = name;
|
||||
t->Generic.specific = specific;
|
||||
t->Generic.specialized = specialized;
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -1632,6 +1632,10 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type->kind == Type_Generic && type->Generic.specialized != nullptr) {
|
||||
Type *specialized = type->Generic.specialized;
|
||||
return lookup_field_with_selection(a, specialized, field_name, is_type, sel);
|
||||
}
|
||||
|
||||
} else if (type->Record.kind == Type_Union) {
|
||||
if (field_name == "__tag") {
|
||||
@@ -2291,9 +2295,9 @@ gbString write_type_to_string(gbString str, Type *type) {
|
||||
String name = type->Generic.name;
|
||||
str = gb_string_appendc(str, "$");
|
||||
str = gb_string_append_length(str, name.text, name.len);
|
||||
if (type->Generic.specific != nullptr) {
|
||||
if (type->Generic.specialized != nullptr) {
|
||||
str = gb_string_appendc(str, "/");
|
||||
str = write_type_to_string(str, type->Generic.specific);
|
||||
str = write_type_to_string(str, type->Generic.specialized);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user