Browse Source

Implicit parametric polymorphic procedures

Ginger Bill 8 years ago
parent
commit
69f7382eec
12 changed files with 271 additions and 91 deletions
  1. 4 0
      code/demo.odin
  2. 2 0
      core/opengl.odin
  3. 6 7
      core/os_x.odin
  4. 2 2
      src/build_settings.cpp
  5. 20 6
      src/check_decl.cpp
  6. 157 23
      src/check_expr.cpp
  7. 3 0
      src/check_stmt.cpp
  8. 1 0
      src/checker.cpp
  9. 1 1
      src/main.cpp
  10. 8 2
      src/parser.cpp
  11. 32 27
      src/tokenizer.cpp
  12. 35 23
      src/types.cpp

+ 4 - 0
code/demo.odin

@@ -367,7 +367,10 @@ explicit_parametric_polymorphic_procedures :: proc() {
 }
 }
 
 
 
 
+
+
 main :: proc() {
 main :: proc() {
+/*
 	general_stuff();
 	general_stuff();
 	foreign_blocks();
 	foreign_blocks();
 	default_arguments();
 	default_arguments();
@@ -381,6 +384,7 @@ main :: proc() {
 
 
 	program := "+ + * - /";
 	program := "+ + * - /";
 	accumulator := 0;
 	accumulator := 0;
+*/
 
 
 	for token in program {
 	for token in program {
 		match token {
 		match token {

+ 2 - 0
core/opengl.odin

@@ -8,6 +8,8 @@ import (
 )
 )
 import_load "opengl_constants.odin";
 import_load "opengl_constants.odin";
 
 
+_ := compile_assert(ODIN_OS != "osx");
+
 foreign lib {
 foreign lib {
 	Clear          :: proc(mask: u32)                                #link_name "glClear"         ---;
 	Clear          :: proc(mask: u32)                                #link_name "glClear"         ---;
 	ClearColor     :: proc(r, g, b, a: f32)                          #link_name "glClearColor"    ---;
 	ClearColor     :: proc(r, g, b, a: f32)                          #link_name "glClearColor"    ---;

+ 6 - 7
core/os_x.odin

@@ -8,7 +8,6 @@ import "strings.odin";
 Handle      :: i32;
 Handle      :: i32;
 FileTime    :: u64;
 FileTime    :: u64;
 Errno       :: int;
 Errno       :: int;
-AddressSize :: int;
 
 
 
 
 O_RDONLY   :: 0x00000;
 O_RDONLY   :: 0x00000;
@@ -125,9 +124,9 @@ F_OK :: 0; // Test for file existance
 foreign libc {
 foreign libc {
 	unix_open    :: proc(path: ^u8, mode: int) -> Handle                              #link_name "open"    ---;
 	unix_open    :: proc(path: ^u8, mode: int) -> Handle                              #link_name "open"    ---;
 	unix_close   :: proc(handle: Handle)                                              #link_name "close"   ---;
 	unix_close   :: proc(handle: Handle)                                              #link_name "close"   ---;
-	unix_read    :: proc(handle: Handle, buffer: rawptr, count: int) -> AddressSize   #link_name "read"    ---;
-	unix_write   :: proc(handle: Handle, buffer: rawptr, count: int) -> AddressSize   #link_name "write"   ---;
-	unix_lseek   :: proc(fs: Handle, offset: AddressSize, whence: int) -> AddressSize #link_name "lseek"   ---;
+	unix_read    :: proc(handle: Handle, buffer: rawptr, count: int) -> int   #link_name "read"    ---;
+	unix_write   :: proc(handle: Handle, buffer: rawptr, count: int) -> int   #link_name "write"   ---;
+	unix_lseek   :: proc(fs: Handle, offset: int, whence: int) -> int #link_name "lseek"   ---;
 	unix_gettid  :: proc() -> u64                                                     #link_name "gettid"  ---;
 	unix_gettid  :: proc() -> u64                                                     #link_name "gettid"  ---;
 	unix_stat    :: proc(path: ^u8, stat: ^Stat) -> int                               #link_name "stat"    ---;
 	unix_stat    :: proc(path: ^u8, stat: ^Stat) -> int                               #link_name "stat"    ---;
 	unix_access  :: proc(path: ^u8, mask: int) -> int                                 #link_name "access"  ---;
 	unix_access  :: proc(path: ^u8, mask: int) -> int                                 #link_name "access"  ---;
@@ -168,7 +167,7 @@ close :: proc(fd: Handle) {
 	unix_close(fd);
 	unix_close(fd);
 }
 }
 
 
-write :: proc(fd: Handle, data: []u8) -> (AddressSize, Errno) {
+write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
 	assert(fd != -1);
 	assert(fd != -1);
 
 
 	bytes_written := unix_write(fd, &data[0], len(data));
 	bytes_written := unix_write(fd, &data[0], len(data));
@@ -178,7 +177,7 @@ write :: proc(fd: Handle, data: []u8) -> (AddressSize, Errno) {
 	return bytes_written, 0;
 	return bytes_written, 0;
 }
 }
 
 
-read :: proc(fd: Handle, data: []u8) -> (AddressSize, Errno) {
+read :: proc(fd: Handle, data: []u8) -> (int, Errno) {
 	assert(fd != -1);
 	assert(fd != -1);
 
 
 	bytes_read := unix_read(fd, &data[0], len(data));
 	bytes_read := unix_read(fd, &data[0], len(data));
@@ -188,7 +187,7 @@ read :: proc(fd: Handle, data: []u8) -> (AddressSize, Errno) {
 	return bytes_read, 0;
 	return bytes_read, 0;
 }
 }
 
 
-seek :: proc(fd: Handle, offset: AddressSize, whence: int) -> (AddressSize, Errno) {
+seek :: proc(fd: Handle, offset: int, whence: int) -> (int, Errno) {
 	assert(fd != -1);
 	assert(fd != -1);
 
 
 	final_offset := unix_lseek(fd, offset, whence);
 	final_offset := unix_lseek(fd, offset, whence);

+ 2 - 2
src/build_settings.cpp

@@ -266,12 +266,12 @@ String get_fullpath_core(gbAllocator a, String path) {
 }
 }
 
 
 
 
-
+String const ODIN_VERSION = str_lit("0.6.0-dev");
 
 
 void init_build_context(void) {
 void init_build_context(void) {
 	BuildContext *bc = &build_context;
 	BuildContext *bc = &build_context;
 	bc->ODIN_VENDOR  = str_lit("odin");
 	bc->ODIN_VENDOR  = str_lit("odin");
-	bc->ODIN_VERSION = str_lit("0.6.0-dev");
+	bc->ODIN_VERSION = ODIN_VERSION;
 	bc->ODIN_ROOT    = odin_root_dir();
 	bc->ODIN_ROOT    = odin_root_dir();
 
 
 #if defined(GB_SYSTEM_WINDOWS)
 #if defined(GB_SYSTEM_WINDOWS)

+ 20 - 6
src/check_decl.cpp

@@ -329,7 +329,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
+
+	bool prev_allow_polymorphic_types = c->context.allow_polymorphic_types;
+	c->context.allow_polymorphic_types = true;
 	check_procedure_type(c, proc_type, pl->type);
 	check_procedure_type(c, proc_type, pl->type);
+	c->context.allow_polymorphic_types = prev_allow_polymorphic_types;
 
 
 	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;
@@ -348,11 +357,11 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 			error(e->token, "Procedure type of `main` was expected to be `proc()`, got %s", str);
 			error(e->token, "Procedure type of `main` was expected to be `proc()`, got %s", str);
 			gb_string_free(str);
 			gb_string_free(str);
 		}
 		}
-		if (proc_type->Proc.calling_convention != ProcCC_Odin &&
-		    proc_type->Proc.calling_convention != ProcCC_Contextless) {
+		if (pt->calling_convention != ProcCC_Odin &&
+		    pt->calling_convention != ProcCC_Contextless) {
 			error(e->token, "Procedure `main` cannot have a custom calling convention");
 			error(e->token, "Procedure `main` cannot have a custom calling convention");
 		}
 		}
-		proc_type->Proc.calling_convention = ProcCC_Contextless;
+		pt->calling_convention = ProcCC_Contextless;
 	}
 	}
 
 
 	if (is_inline && is_no_inline) {
 	if (is_inline && is_no_inline) {
@@ -363,7 +372,6 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 		error(pl->type, "A foreign procedure cannot have an `export` tag");
 		error(pl->type, "A foreign procedure cannot have an `export` tag");
 	}
 	}
 
 
-
 	if (pt->is_polymorphic) {
 	if (pt->is_polymorphic) {
 		if (pl->body == NULL) {
 		if (pl->body == NULL) {
 			error(e->token, "Polymorphic procedures must have a body");
 			error(e->token, "Polymorphic procedures must have a body");
@@ -472,9 +480,15 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count
 	}
 	}
 	e->flags |= EntityFlag_Visited;
 	e->flags |= EntityFlag_Visited;
 
 
+	String context_name = str_lit("variable declaration");
+
 	if (type_expr != NULL) {
 	if (type_expr != NULL) {
 		e->type = check_type(c, type_expr);
 		e->type = check_type(c, type_expr);
 	}
 	}
+	if (e->type != NULL && is_type_polymorphic(e->type)) {
+		error(e->token, "Invalid use of a polymorphic type in %.*s", LIT(context_name));
+		e->type = t_invalid;
+	}
 
 
 
 
 	if (e->Variable.is_foreign) {
 	if (e->Variable.is_foreign) {
@@ -514,7 +528,7 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count
 		GB_ASSERT(entities == NULL || entities[0] == e);
 		GB_ASSERT(entities == NULL || entities[0] == e);
 		Operand operand = {};
 		Operand operand = {};
 		check_expr(c, &operand, init_expr);
 		check_expr(c, &operand, init_expr);
-		check_init_variable(c, e, &operand, str_lit("variable declaration"));
+		check_init_variable(c, e, &operand, context_name);
 	}
 	}
 
 
 	if (type_expr != NULL) {
 	if (type_expr != NULL) {
@@ -527,7 +541,7 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count
 	Array<AstNode *> inits;
 	Array<AstNode *> inits;
 	array_init(&inits, c->allocator, 1);
 	array_init(&inits, c->allocator, 1);
 	array_add(&inits, init_expr);
 	array_add(&inits, init_expr);
-	check_init_variables(c, entities, entity_count, inits, str_lit("variable declaration"));
+	check_init_variables(c, entities, entity_count, inits, context_name);
 }
 }
 
 
 void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
 void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {

+ 157 - 23
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) {
 Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_variadic_, bool *success_, Array<Operand> *operands) {
 	if (_params == NULL) {
 	if (_params == NULL) {
 		return NULL;
 		return NULL;
 	}
 	}
 
 
+	bool allow_polymorphic_types = c->context.allow_polymorphic_types;
+
 	bool success = true;
 	bool success = true;
 	ast_node(field_list, FieldList, _params);
 	ast_node(field_list, FieldList, _params);
 	Array<AstNode *> params = field_list->list;
 	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_nil = false;
 		bool default_is_location = false;
 		bool default_is_location = false;
 		bool is_type_param = false;
 		bool is_type_param = false;
+		bool is_type_polymorphic_type = false;
+		bool detemine_type_from_operand = false;
 
 
 
 
 		if (type_expr == NULL) {
 		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) {
 			if (type_expr->kind == AstNode_HelperType) {
 				is_type_param = true;
 				is_type_param = true;
 				if (operands != NULL) {
 				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 {
 				} else {
 					type = make_type_generic(c->allocator, 0, str_lit(""));
 					type = make_type_generic(c->allocator, 0, str_lit(""));
 				}
 				}
 			} else {
 			} 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) {
 			if (default_value != NULL) {
@@ -1218,12 +1310,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
 			type = t_invalid;
 			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->flags&FieldFlag_c_vararg) {
 			if (p->type == NULL ||
 			if (p->type == NULL ||
 			    p->type->kind != AstNode_Ellipsis) {
 			    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) {
 		for_array(j, p->names) {
 			AstNode *name = p->names[j];
 			AstNode *name = p->names[j];
 			if (ast_node_expect(name, AstNode_Ident)) {
 			if (ast_node_expect(name, AstNode_Ident)) {
 				Entity *param = NULL;
 				Entity *param = NULL;
 				if (is_type_param) {
 				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 = make_entity_type_name(c->allocator, scope, name->Ident.token, type);
 					param->TypeName.is_type_alias = true;
 					param->TypeName.is_type_alias = true;
 				} else {
 				} 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,
 					param = make_entity_param(c->allocator, scope, name->Ident.token, type,
 					                          (p->flags&FieldFlag_using) != 0, false);
 					                          (p->flags&FieldFlag_using) != 0, false);
 					param->Variable.default_value = value;
 					param->Variable.default_value = value;
 					param->Variable.default_is_nil = default_is_nil;
 					param->Variable.default_is_nil = default_is_nil;
 					param->Variable.default_is_location = default_is_location;
 					param->Variable.default_is_location = default_is_location;
+
 				}
 				}
 				if (p->flags&FieldFlag_no_alias) {
 				if (p->flags&FieldFlag_no_alias) {
 					param->flags |= EntityFlag_NoAlias;
 					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) {
 		if (e->kind != Entity_Variable) {
 			is_polymorphic = true;
 			is_polymorphic = true;
 			break;
 			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);
 	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_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);
 	case_ast_node(se, SelectorExpr, e);
 		Operand o = {};
 		Operand o = {};
 		check_selector(c, &o, e, NULL);
 		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);
 		gbString str = type_to_string(type);
 		error(e, "Invalid use of a polymorphic type `%s`", str);
 		error(e, "Invalid use of a polymorphic type `%s`", str);
 		gb_string_free(str);
 		gb_string_free(str);
 		type = t_invalid;
 		type = t_invalid;
 	}
 	}
+	#endif
 
 
 	if (is_type_typed(type)) {
 	if (is_type_typed(type)) {
 		add_type_and_value(&c->info, e, Addressing_Type, type, empty_exact_value);
 		add_type_and_value(&c->info, e, Addressing_Type, type, empty_exact_value);

+ 3 - 0
src/check_stmt.cpp

@@ -1717,6 +1717,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 				init_type = check_type(c, vd->type, NULL);
 				init_type = check_type(c, vd->type, NULL);
 				if (init_type == NULL) {
 				if (init_type == NULL) {
 					init_type = t_invalid;
 					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;
 				}
 				}
 			}
 			}
 
 

+ 1 - 0
src/checker.cpp

@@ -261,6 +261,7 @@ struct CheckerContext {
 	DeclInfo * decl;
 	DeclInfo * decl;
 	u32        stmt_state_flags;
 	u32        stmt_state_flags;
 	bool       in_defer; // TODO(bill): Actually handle correctly
 	bool       in_defer; // TODO(bill): Actually handle correctly
+	bool       allow_polymorphic_types;
 	String     proc_name;
 	String     proc_name;
 	Type *     type_hint;
 	Type *     type_hint;
 	DeclInfo * curr_proc_decl;
 	DeclInfo * curr_proc_decl;

+ 1 - 1
src/main.cpp

@@ -375,7 +375,7 @@ int main(int arg_count, char **arg_ptr) {
 		return 1;
 		return 1;
 		#endif
 		#endif
 	} else if (args[1] == "version") {
 	} else if (args[1] == "version") {
-		gb_printf("%.*s version %.*s\n", LIT(args[0]), LIT(build_context.ODIN_VERSION));
+		gb_printf("%.*s version %.*s\n", LIT(args[0]), LIT(ODIN_VERSION));
 		return 0;
 		return 0;
 	} else {
 	} else {
 		usage(args[0]);
 		usage(args[0]);

+ 8 - 2
src/parser.cpp

@@ -632,6 +632,10 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) {
 	case AstNode_Undef:          break;
 	case AstNode_Undef:          break;
 	case AstNode_BasicLit:       break;
 	case AstNode_BasicLit:       break;
 	case AstNode_BasicDirective: break;
 	case AstNode_BasicDirective: break;
+
+	case AstNode_PolyType:
+		n->PolyType.type = clone_ast_node(a, n->PolyType.type);
+		break;
 	case AstNode_Ellipsis:
 	case AstNode_Ellipsis:
 		n->Ellipsis.expr = clone_ast_node(a, n->Ellipsis.expr);
 		n->Ellipsis.expr = clone_ast_node(a, n->Ellipsis.expr);
 		break;
 		break;
@@ -3259,8 +3263,10 @@ u32 check_field_prefixes(AstFile *f, isize name_count, u32 allowed_flags, u32 se
 		syntax_error(f->curr_token, "`#c_vararg` is not allowed within this field list");
 		syntax_error(f->curr_token, "`#c_vararg` is not allowed within this field list");
 		set_flags &= ~FieldFlag_c_vararg;
 		set_flags &= ~FieldFlag_c_vararg;
 	}
 	}
-	if ((allowed_flags&FieldFlag_dollar) == 0 && (set_flags&FieldFlag_dollar)) {
-		syntax_error(f->curr_token, "`$` is only allowed within procedures");
+	// if ((allowed_flags&FieldFlag_dollar) == 0 && (set_flags&FieldFlag_dollar)) {
+	if ((set_flags&FieldFlag_dollar)) {
+		// syntax_error(f->curr_token, "`$` is only allowed within procedures");
+		syntax_error(f->curr_token, "`$` is not yet supported");
 		set_flags &= ~FieldFlag_dollar;
 		set_flags &= ~FieldFlag_dollar;
 	}
 	}
 	return set_flags;
 	return set_flags;

+ 32 - 27
src/tokenizer.cpp

@@ -36,27 +36,27 @@ TOKEN_KIND(Token__OperatorBegin, "_OperatorBegin"), \
 	TOKEN_KIND(Token_CmpAnd, "&&"), \
 	TOKEN_KIND(Token_CmpAnd, "&&"), \
 	TOKEN_KIND(Token_CmpOr,  "||"), \
 	TOKEN_KIND(Token_CmpOr,  "||"), \
 \
 \
-TOKEN_KIND(Token__AssignOpBegin, "_AssignOpBegin"), \
-	TOKEN_KIND(Token_AddEq,      "+="), \
-	TOKEN_KIND(Token_SubEq,      "-="), \
-	TOKEN_KIND(Token_MulEq,      "*="), \
-	TOKEN_KIND(Token_QuoEq,      "/="), \
-	TOKEN_KIND(Token_ModEq,      "%="), \
-	TOKEN_KIND(Token_ModModEq,   "%%="), \
-	TOKEN_KIND(Token_AndEq,      "&="), \
-	TOKEN_KIND(Token_OrEq,       "|="), \
-	TOKEN_KIND(Token_XorEq,      "~="), \
-	TOKEN_KIND(Token_AndNotEq,   "&~="), \
-	TOKEN_KIND(Token_ShlEq,      "<<="), \
-	TOKEN_KIND(Token_ShrEq,      ">>="), \
-	TOKEN_KIND(Token_CmpAndEq,   "&&="), \
-	TOKEN_KIND(Token_CmpOrEq,    "||="), \
-TOKEN_KIND(Token__AssignOpEnd,   "_AssignOpEnd"), \
-	TOKEN_KIND(Token_ArrowRight, "->"), \
-	TOKEN_KIND(Token_ArrowLeft,  "<-"), \
-	TOKEN_KIND(Token_Inc,        "++"), \
-	TOKEN_KIND(Token_Dec,        "--"), \
-	TOKEN_KIND(Token_Undef,      "---"), \
+TOKEN_KIND(Token__AssignOpBegin,      "_AssignOpBegin"), \
+	TOKEN_KIND(Token_AddEq,           "+="), \
+	TOKEN_KIND(Token_SubEq,           "-="), \
+	TOKEN_KIND(Token_MulEq,           "*="), \
+	TOKEN_KIND(Token_QuoEq,           "/="), \
+	TOKEN_KIND(Token_ModEq,           "%="), \
+	TOKEN_KIND(Token_ModModEq,        "%%="), \
+	TOKEN_KIND(Token_AndEq,           "&="), \
+	TOKEN_KIND(Token_OrEq,            "|="), \
+	TOKEN_KIND(Token_XorEq,           "~="), \
+	TOKEN_KIND(Token_AndNotEq,        "&~="), \
+	TOKEN_KIND(Token_ShlEq,           "<<="), \
+	TOKEN_KIND(Token_ShrEq,           ">>="), \
+	TOKEN_KIND(Token_CmpAndEq,        "&&="), \
+	TOKEN_KIND(Token_CmpOrEq,         "||="), \
+TOKEN_KIND(Token__AssignOpEnd,        "_AssignOpEnd"), \
+	TOKEN_KIND(Token_ArrowRight,      "->"), \
+	TOKEN_KIND(Token_ThickArrowRight, "=>"), \
+	TOKEN_KIND(Token_Inc,             "++"), \
+	TOKEN_KIND(Token_Dec,             "--"), \
+	TOKEN_KIND(Token_Undef,           "---"), \
 \
 \
 TOKEN_KIND(Token__ComparisonBegin, "_ComparisonBegin"), \
 TOKEN_KIND(Token__ComparisonBegin, "_ComparisonBegin"), \
 	TOKEN_KIND(Token_CmpEq, "=="), \
 	TOKEN_KIND(Token_CmpEq, "=="), \
@@ -910,7 +910,16 @@ Token tokenizer_get_token(Tokenizer *t) {
 		case '%': token.kind = token_kind_dub_eq(t, '%', Token_Mod, Token_ModEq, Token_ModMod, Token_ModModEq);      break;
 		case '%': token.kind = token_kind_dub_eq(t, '%', Token_Mod, Token_ModEq, Token_ModMod, Token_ModModEq);      break;
 
 
 		case '*': token.kind = token_kind_variant2(t, Token_Mul, Token_MulEq);                                        break;
 		case '*': token.kind = token_kind_variant2(t, Token_Mul, Token_MulEq);                                        break;
-		case '=': token.kind = token_kind_variant2(t, Token_Eq,  Token_CmpEq);                                        break;
+		case '=':
+			token.kind = Token_Eq;
+			if (t->curr_rune == '>') {
+				advance_to_next_rune(t);
+				token.kind = Token_ThickArrowRight;
+			} else if (t->curr_rune == '=') {
+				advance_to_next_rune(t);
+				token.kind = Token_CmpEq;
+			}
+			break;
 		case '~': token.kind = token_kind_variant2(t, Token_Xor, Token_XorEq);                                        break;
 		case '~': token.kind = token_kind_variant2(t, Token_Xor, Token_XorEq);                                        break;
 		case '!': token.kind = token_kind_variant2(t, Token_Not, Token_NotEq);                                        break;
 		case '!': token.kind = token_kind_variant2(t, Token_Not, Token_NotEq);                                        break;
 		case '+': token.kind = token_kind_variant3(t, Token_Add, Token_AddEq, '+', Token_Inc);                        break;
 		case '+': token.kind = token_kind_variant3(t, Token_Add, Token_AddEq, '+', Token_Inc);                        break;
@@ -967,11 +976,7 @@ Token tokenizer_get_token(Tokenizer *t) {
 		} break;
 		} break;
 
 
 		case '<':
 		case '<':
-			if (t->curr_rune == '-') {
-				token.kind = Token_ArrowLeft;
-			} else {
-				token.kind = token_kind_dub_eq(t, '<', Token_Lt, Token_LtEq, Token_Shl, Token_ShlEq);
-			}
+			token.kind = token_kind_dub_eq(t, '<', Token_Lt, Token_LtEq, Token_Shl, Token_ShlEq);
 			break;
 			break;
 		case '>': token.kind = token_kind_dub_eq(t, '>', Token_Gt, Token_GtEq, Token_Shr, Token_ShrEq); break;
 		case '>': token.kind = token_kind_dub_eq(t, '>', Token_Gt, Token_GtEq, Token_Shr, Token_ShrEq); break;
 
 

+ 35 - 23
src/types.cpp

@@ -944,7 +944,6 @@ bool is_type_indexable(Type *t) {
 }
 }
 
 
 bool is_type_polymorphic(Type *t) {
 bool is_type_polymorphic(Type *t) {
-	t = core_type(t);
 	switch (t->kind) {
 	switch (t->kind) {
 	case Type_Generic:
 	case Type_Generic:
 		return true;
 		return true;
@@ -974,29 +973,36 @@ bool is_type_polymorphic(Type *t) {
 		if (t->Proc.is_polymorphic) {
 		if (t->Proc.is_polymorphic) {
 			return true;
 			return true;
 		}
 		}
-		// if (t->Proc.param_count > 0 &&
-		//     is_type_polymorphic(t->Proc.params)) {
-		// 	return true;
-		// }
-		// if (t->Proc.result_count > 0 &&
-		//     is_type_polymorphic(t->Proc.results)) {
-		// 	return true;
-		// }
+		#if 0
+		if (t->Proc.param_count > 0 &&
+		    is_type_polymorphic(t->Proc.params)) {
+			return true;
+		}
+		if (t->Proc.result_count > 0 &&
+		    is_type_polymorphic(t->Proc.results)) {
+			return true;
+		}
+		#endif
 		break;
 		break;
 
 
-	// case Type_Record:
-	// 	GB_ASSERT(t->Record.kind != TypeRecord_Enum);
-	// 	for (isize i = 0; i < t->Record.field_count; i++) {
-	// 	    if (is_type_polymorphic(t->Record.fields[i]->type)) {
-	// 	    	return true;
-	// 	    }
-	// 	}
-	// 	for (isize i = 1; i < t->Record.variant_count; i++) {
-	// 	    if (is_type_polymorphic(t->Record.variants[i]->type)) {
-	// 	    	return true;
-	// 	    }
-	// 	}
-	// 	break;
+	case Type_Record:
+		if (t->Record.kind == TypeRecord_Enum) {
+			if (t->Record.enum_base_type != NULL) {
+				return is_type_polymorphic(t->Record.enum_base_type);
+			}
+			return false;
+		}
+		for (isize i = 0; i < t->Record.field_count; i++) {
+		    if (is_type_polymorphic(t->Record.fields[i]->type)) {
+		    	return true;
+		    }
+		}
+		for (isize i = 1; i < t->Record.variant_count; i++) {
+		    if (is_type_polymorphic(t->Record.variants[i]->type)) {
+		    	return true;
+		    }
+		}
+		break;
 
 
 	case Type_Map:
 	case Type_Map:
 		if (is_type_polymorphic(t->Map.key)) {
 		if (is_type_polymorphic(t->Map.key)) {
@@ -2263,7 +2269,13 @@ gbString write_type_to_string(gbString str, Type *type) {
 		break;
 		break;
 
 
 	case Type_Generic:
 	case Type_Generic:
-		str = gb_string_appendc(str, "type");
+		if (type->Generic.name.len == 0) {
+			str = gb_string_appendc(str, "type");
+		} else {
+			String name = type->Generic.name;
+			str = gb_string_appendc(str, "$");
+			str = gb_string_append_length(str, name.text, name.len);
+		}
 		break;
 		break;
 
 
 	case Type_Pointer:
 	case Type_Pointer: