Browse Source

Polymorphic type specialization for procedures

Ginger Bill 8 years ago
parent
commit
9a8759efef
8 changed files with 307 additions and 89 deletions
  1. 1 1
      LICENSE
  2. 156 8
      code/demo.odin
  3. 10 8
      src/check_decl.cpp
  4. 95 52
      src/check_expr.cpp
  5. 13 6
      src/check_stmt.cpp
  6. 20 9
      src/ir.cpp
  7. 3 0
      src/parser.cpp
  8. 9 5
      src/types.cpp

+ 1 - 1
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
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:
 modification, are permitted provided that the following conditions are met:

+ 156 - 8
code/demo.odin

@@ -1,5 +1,116 @@
 import "fmt.odin";
 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) {
 Vector :: struct(N: int, T: type) {
 	using _: raw_union {
 	using _: raw_union {
 		using e: [N]T;
 		using e: [N]T;
@@ -16,14 +127,51 @@ Vector :: struct(N: int, T: type) {
 
 
 Vector3 :: Vector(3, f32);
 Vector3 :: Vector(3, f32);
 
 
+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() {
 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;
+	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);
 	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);
+
 }
 }

+ 10 - 8
src/check_decl.cpp

@@ -60,7 +60,9 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
 			t = default_type(t);
 			t = default_type(t);
 		}
 		}
 		if (is_type_polymorphic(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;
 			e->type = t_invalid;
 			return nullptr;
 			return nullptr;
 		}
 		}
@@ -400,17 +402,16 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 	check_open_scope(c, pl->type);
 	check_open_scope(c, pl->type);
 	defer (check_close_scope(c));
 	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;
 	auto prev_context = c->context;
 	c->context.allow_polymorphic_types = true;
 	c->context.allow_polymorphic_types = true;
 	check_procedure_type(c, proc_type, pl->type);
 	check_procedure_type(c, proc_type, pl->type);
 	c->context = prev_context;
 	c->context = prev_context;
 
 
+	TypeProc *pt = &proc_type->Proc;
+
 	bool is_foreign         = (pl->tags & ProcTag_foreign)   != 0;
 	bool is_foreign         = (pl->tags & ProcTag_foreign)   != 0;
 	bool is_link_name       = (pl->tags & ProcTag_link_name) != 0;
 	bool is_link_name       = (pl->tags & ProcTag_link_name) != 0;
 	bool is_export          = (pl->tags & ProcTag_export)    != 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;
 	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 (d->scope->is_file && e->token.string == "main") {
 		if (pt->param_count != 0 ||
 		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);
 		e->type = check_type(c, type_expr);
 	}
 	}
 	if (e->type != nullptr && is_type_polymorphic(base_type(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));
+		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;
 		e->type = t_invalid;
 	}
 	}
 
 

+ 95 - 52
src/check_expr.cpp

@@ -156,6 +156,7 @@ bool find_or_generate_polymorphic_procedure(Checker *c, Entity *base_entity, Typ
 	//                                                                           //
 	//                                                                           //
 	///////////////////////////////////////////////////////////////////////////////
 	///////////////////////////////////////////////////////////////////////////////
 
 
+
 	if (base_entity == nullptr) {
 	if (base_entity == nullptr) {
 		return false;
 		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)) {
 	if (!is_type_proc(base_entity->type)) {
 		return false;
 		return false;
 	}
 	}
+	String name = base_entity->token.string;
 
 
 	Type *src = base_type(base_entity->type);
 	Type *src = base_type(base_entity->type);
 	Type *dst = nullptr;
 	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);
 	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) {
 i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) {
 	if (operand->mode == Addressing_Invalid ||
 	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))) {
 	if (are_types_identical(dst, src) && (!is_type_named(dst) || !is_type_named(src))) {
 		return 1;
 		return 1;
 	}
 	}
-
+#endif
 
 
 	if (is_type_bit_field_value(operand->type) && is_type_integer(type)) {
 	if (is_type_bit_field_value(operand->type) && is_type_integer(type)) {
 		Type *bfv = base_type(operand->type);
 		Type *bfv = base_type(operand->type);
@@ -504,6 +510,13 @@ i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) {
 	}
 	}
 #endif
 #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)) {
 	if (is_type_union(dst)) {
 		for (isize i = 0; i < dst->Union.variant_count; i++) {
 		for (isize i = 0; i < dst->Union.variant_count; i++) {
 			Type *vt = dst->Union.variants[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(op_type_str));
 		defer (gb_string_free(expr_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
 			// TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
 			error(operand->expr,
 			error(operand->expr,
 			      "Cannot assign built-in procedure `%s` in %.*s",
 			      "Cannot assign built-in procedure `%s` in %.*s",
 			      expr_str,
 			      expr_str,
 			      LIT(context_name));
 			      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,
 			error(operand->expr,
 			      "Cannot assign `%s` which is a type in %.*s",
 			      "Cannot assign `%s` which is a type in %.*s",
 			      op_type_str,
 			      op_type_str,
 			      LIT(context_name));
 			      LIT(context_name));
-		} else {
+			break;
+		default:
 			// TODO(bill): is this a good enough error message?
 			// TODO(bill): is this a good enough error message?
 			error(operand->expr,
 			error(operand->expr,
 			      "Cannot assign value `%s` of type `%s` to `%s` in %.*s",
 			      "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,
 			      op_type_str,
 			      type_str,
 			      type_str,
 			      LIT(context_name));
 			      LIT(context_name));
+			break;
 		}
 		}
 		operand->mode = Addressing_Invalid;
 		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) {
 					if (poly_operands != nullptr) {
 						Operand operand = (*poly_operands)[entities.count];
 						Operand operand = (*poly_operands)[entities.count];
 						if (is_type_param) {
 						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))) {
 							if (is_type_polymorphic(base_type(operand.type))) {
 								is_polymorphic = true;
 								is_polymorphic = true;
 								can_check_fields = false;
 								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;
 		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;
+
+	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);
+
+		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));
+		}
 
 
-	source = base_type(source);
-	GB_ASSERT(source->kind == Type_Record && source->Record.kind == TypeRecord_Struct);
+		return true;
+	}
 
 
-	return are_types_identical(source->Record.polymorphic_parent, specific);
+	return false;
 }
 }
 
 
 bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool compound, bool modify_type) {
 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;
 	o.type = source;
 	switch (poly->kind) {
 	switch (poly->kind) {
 	case Type_Basic:
 	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);
 		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);
 		if (compound) return are_types_identical(poly, source);
 		return check_is_assignable_to(c, &o, poly);
 		return check_is_assignable_to(c, &o, poly);
+	}
 
 
 	case Type_Generic: {
 	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;
 				return false;
 			}
 			}
 		}
 		}
@@ -1587,7 +1646,7 @@ bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool c
 	}
 	}
 	case Type_Pointer:
 	case Type_Pointer:
 		if (source->kind == 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;
 		return false;
 	case Type_Atomic:
 	case Type_Atomic:
@@ -1640,7 +1699,6 @@ bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool c
 
 
 	case Type_Record:
 	case Type_Record:
 		if (source->kind == Type_Record) {
 		if (source->kind == Type_Record) {
-			// TODO(bill): Polymorphic type assignment
 			// return check_is_assignable_to(c, &o, poly);
 			// return check_is_assignable_to(c, &o, poly);
 		}
 		}
 		return false;
 		return false;
@@ -1718,33 +1776,6 @@ Type *determine_type_from_polymorphic(Checker *c, Type *poly_type, Operand opera
 	return t_invalid;
 	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) {
 Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_variadic_, bool *success_, Array<Operand> *operands) {
 	if (_params == nullptr) {
 	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);
 		Entity *e = scope_lookup_entity(c->context.scope, op_name);
 
 
 		bool is_alias = false;
 		bool is_alias = false;
-		while (e->kind == Entity_Alias) {
+		while (e != nullptr && e->kind == Entity_Alias) {
 			GB_ASSERT(e->Alias.base != nullptr);
 			GB_ASSERT(e->Alias.base != nullptr);
 			e = e->Alias.base;
 			e = e->Alias.base;
 			is_alias = true;
 			is_alias = true;
@@ -6549,6 +6580,10 @@ CallArgumentError check_polymorphic_struct_type(Checker *c, Operand *operand, As
 					Entity *p = tuple->variables[j];
 					Entity *p = tuple->variables[j];
 					Operand o = ordered_operands[j];
 					Operand o = ordered_operands[j];
 					if (p->kind == Entity_TypeName) {
 					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)) {
 						if (!are_types_identical(o.type, p->type)) {
 							ok = false;
 							ok = false;
 						}
 						}
@@ -7619,12 +7654,14 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t
 
 
 		if (!valid) {
 		if (!valid) {
 			gbString str = expr_to_string(o->expr);
 			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) {
 			if (is_const) {
 				error(o->expr, "Cannot index a constant `%s`", str);
 				error(o->expr, "Cannot index a constant `%s`", str);
 			} else {
 			} 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->mode = Addressing_Invalid;
 			o->expr = node;
 			o->expr = node;
 			return kind;
 			return kind;
@@ -8009,6 +8046,12 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 		str = write_expr_to_string(str, ta->type);
 		str = write_expr_to_string(str, ta->type);
 		str = gb_string_appendc(str, ")");
 		str = gb_string_appendc(str, ")");
 	case_end;
 	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);
 	case_ast_node(ie, IndexExpr, node);
 		str = write_expr_to_string(str, ie->expr);
 		str = write_expr_to_string(str, ie->expr);

+ 13 - 6
src/check_stmt.cpp

@@ -1608,16 +1608,21 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 			Entity *e = nullptr;
 			Entity *e = nullptr;
 
 
 			bool is_selector = false;
 			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);
 				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);
 				e = check_selector(c, &o, expr, nullptr);
 				is_selector = true;
 				is_selector = true;
-			} else if (expr->kind == AstNode_Implicit) {
+				break;
+			case AstNode_Implicit:
 				error(us->token, "`using` applied to an implicit value");
 				error(us->token, "`using` applied to an implicit value");
 				continue;
 				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)) {
 			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) {
 			if (init_type == nullptr) {
 				init_type = t_invalid;
 				init_type = t_invalid;
 			} else if (is_type_polymorphic(base_type(init_type))) {
 			} 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;
 				init_type = t_invalid;
 			}
 			}
 		}
 		}

+ 20 - 9
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, dst)) {
+		if (!are_types_identical(src_type, t)) {
+			return ir_emit_transmute(proc, value, t);
+		}
 		return value;
 		return value;
 	}
 	}
 
 
@@ -5752,19 +5755,24 @@ void ir_build_constant_value_decl(irProcedure *proc, AstNodeValueDecl *vd) {
 			continue;
 			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) {
 		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
 			// NOTE(bill): Generate a new name
 			// parent_proc.name-guid
 			// 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;
 			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);
 			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;
 					auto procs = *found;
 					for_array(i, procs) {
 					for_array(i, procs) {
 						Entity *e = procs[i];
 						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);
 						DeclInfo *d = decl_info_of_entity(info, e);
 						ir_build_poly_proc(proc, &d->proc_lit->ProcLit, e);
 						ir_build_poly_proc(proc, &d->proc_lit->ProcLit, e);
 					}
 					}

+ 3 - 0
src/parser.cpp

@@ -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");
 		syntax_error(stmt, "Expected a normal statement rather than a block statement");
 		return stmt;
 		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));
 	GB_ASSERT(is_ast_node_stmt(stmt) || is_ast_node_decl(stmt));
 	Token open = ast_node_token(stmt);
 	Token open = ast_node_token(stmt);
 	Token close = ast_node_token(stmt);
 	Token close = ast_node_token(stmt);

+ 9 - 5
src/types.cpp

@@ -108,7 +108,7 @@ struct TypeRecord {
 	TYPE_KIND(Generic, struct {                           \
 	TYPE_KIND(Generic, struct {                           \
 		i64    id;                                        \
 		i64    id;                                        \
 		String name;                                      \
 		String name;                                      \
-		Type * specific;                                  \
+		Type * specialized;                               \
 	})                                                    \
 	})                                                    \
 	TYPE_KIND(Pointer, struct { Type *elem; })            \
 	TYPE_KIND(Pointer, struct { Type *elem; })            \
 	TYPE_KIND(Atomic,  struct { Type *elem; })            \
 	TYPE_KIND(Atomic,  struct { Type *elem; })            \
@@ -486,11 +486,11 @@ Type *make_type_basic(gbAllocator a, BasicType basic) {
 	return t;
 	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);
 	Type *t = alloc_type(a, Type_Generic);
 	t->Generic.id = id;
 	t->Generic.id = id;
 	t->Generic.name = name;
 	t->Generic.name = name;
-	t->Generic.specific = specific;
+	t->Generic.specialized = specialized;
 	return t;
 	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) {
 	} else if (type->Record.kind == Type_Union) {
 		if (field_name == "__tag") {
 		if (field_name == "__tag") {
@@ -2291,9 +2295,9 @@ gbString write_type_to_string(gbString str, Type *type) {
 			String name = type->Generic.name;
 			String name = type->Generic.name;
 			str = gb_string_appendc(str, "$");
 			str = gb_string_appendc(str, "$");
 			str = gb_string_append_length(str, name.text, name.len);
 			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 = gb_string_appendc(str, "/");
-				str = write_type_to_string(str, type->Generic.specific);
+				str = write_type_to_string(str, type->Generic.specialized);
 			}
 			}
 		}
 		}
 		break;
 		break;