Browse Source

Rudimentary support for parametric polymorphic types

Ginger Bill 8 years ago
parent
commit
1c5ddd65b4
10 changed files with 1114 additions and 640 deletions
  1. 22 423
      code/demo.odin
  2. 430 0
      code/demo_backup.odin
  3. 1 1
      src/check_decl.cpp
  4. 461 64
      src/check_expr.cpp
  5. 126 125
      src/check_stmt.cpp
  6. 3 0
      src/checker.cpp
  7. 18 2
      src/ir.cpp
  8. 1 1
      src/ir_print.cpp
  9. 37 23
      src/parser.cpp
  10. 15 1
      src/types.cpp

+ 22 - 423
code/demo.odin

@@ -1,430 +1,29 @@
-import (
-	"fmt.odin";
-	"atomics.odin";
-	"bits.odin";
-	"decimal.odin";
-	"hash.odin";
-	"math.odin";
-	"mem.odin";
-	"opengl.odin";
-	"os.odin";
-	"raw.odin";
-	"strconv.odin";
-	"strings.odin";
-	"sync.odin";
-	"sort.odin";
-	"types.odin";
-	"utf8.odin";
-	"utf16.odin";
-/*
-*/
-)
-
-
-general_stuff :: proc() {
-	// Complex numbers
-	a := 3 + 4i;
-	b: complex64 = 3 + 4i;
-	c: complex128 = 3 + 4i;
-	d := complex(2, 3);
-
-	e := a / conj(a);
-	fmt.println("(3+4i)/(3-4i) =", e);
-	fmt.println(real(e), "+", imag(e), "i");
-
-
-	// C-style variadic procedures
-	foreign __llvm_core {
-		// The variadic part allows for extra type checking too which C does not provide
-		c_printf :: proc(fmt: ^u8, #c_vararg args: ...any) -> i32 #link_name "printf" ---;
-	}
-	str := "%d\n\x00";
-	// c_printf(&str[0], i32(789456123));
-
-
-	Foo :: struct {
-		x: int;
-		y: f32;
-		z: string;
-	}
-	foo := Foo{123, 0.513, "A string"};
-	x, y, z := expand_to_tuple(foo);
-	fmt.println(x, y, z);
-	compile_assert(type_of(x) == int);
-	compile_assert(type_of(y) == f32);
-	compile_assert(type_of(z) == string);
-
-
-	// By default, all variables are zeroed
-	// This can be overridden with the "uninitialized value"
-	// This is similar to `nil` but applied to everything
-	undef_int: int = ---;
-
-
-	// Context system is now implemented using Implicit Parameter Passing (IPP)
-	// The previous implementation was Thread Local Storage (TLS)
-	// IPP has the advantage that it works on systems without TLS and that you can
-	// link the context to the stack frame and thus look at previous contexts
-	//
-	// It does mean that a pointer is implicitly passed procedures with the default
-	// Odin calling convention (#cc_odin)
-	// This can be overridden with something like #cc_contextless or #cc_c if performance
-	// is worried about
-
-}
-
-foreign_blocks :: proc() {
-	// See sys/windows.odin
-}
-
-default_arguments :: proc() {
-	hello :: proc(a: int = 9, b: int = 9) do fmt.printf("a is %d; b is %d\n", a, b);
-	fmt.println("\nTesting default arguments:");
-	hello(1, 2);
-	hello(1);
-	hello();
-}
-
-named_arguments :: proc() {
-	Colour :: enum {
-		Red,
-		Orange,
-		Yellow,
-		Green,
-		Blue,
-		Octarine,
-	};
-	using Colour;
-
-	make_character :: proc(name, catch_phrase: string, favourite_colour, least_favourite_colour: Colour) {
-		fmt.println();
-		fmt.printf("My name is %v and I like %v.  %v\n", name, favourite_colour, catch_phrase);
-	}
-
-	make_character("Frank", "¡Ay, caramba!", Blue, Green);
-
-
-	// As the procedures have more and more parameters, it is very easy
-	// to get many of the arguments in the wrong order especialy if the
-	// types are the same
-	make_character("¡Ay, caramba!", "Frank", Green, Blue);
-
-	// Named arguments help to disambiguate this problem
-	make_character(catch_phrase = "¡Ay, caramba!", name = "Frank",
-	               least_favourite_colour = Green, favourite_colour = Blue);
-
-
-	// The named arguments can be specifed in any order.
-	make_character(favourite_colour = Octarine, catch_phrase = "U wot m8!",
-	               least_favourite_colour = Green, name = "Dennis");
-
-
-	// NOTE: You cannot mix named arguments with normal values
-	/*
-	make_character("Dennis",
-	               favourite_colour = Octarine, catch_phrase = "U wot m8!",
-	               least_favourite_colour = Green);
-	*/
-
-
-	// Named arguments can also aid with default arguments
-	numerous_things :: proc(s: string, a := 1, b := 2, c := 3.14,
-	                        d := "The Best String!", e := false, f := 10.3/3.1, g := false) {
-		g_str := g ? "true" : "false";
-		fmt.printf("How many?! %s: %v\n", s, g_str);
-	}
-
-	numerous_things("First");
-	numerous_things(s = "Second", g = true);
-
-
-	// Default values can be placed anywhere, not just at the end like in other languages
-	weird :: proc(pre: string, mid: int = 0, post: string) {
-		fmt.println(pre, mid, post);
-	}
-
-	weird("How many things", 42, "huh?");
-	weird(pre = "Prefix", post = "Pat");
-
-}
-
-
-default_return_values :: proc() {
-	foo :: proc(x: int) -> (first: string = "Hellope", second := "world!") {
-		match x {
-		case 0: return;
-		case 1: return "Goodbye";
-		case 2: return "Goodbye", "cruel world...";
-		case 3: return second = "cruel world...", first = "Goodbye";
+import "fmt.odin";
+
+Vector :: struct(N: int, T: type) {
+	using _: raw_union {
+		using e: [N]T;
+		when 0 < N && N <= 4 {
+			using v: struct {
+				when N >= 1 do x: T;
+				when N >= 2 do y: T;
+				when N >= 3 do z: T;
+				when N >= 4 do w: T;
+			};
 		}
 		}
-
-		return second = "my old friend.";
-	}
-
-	fmt.printf("%s %s\n", foo(0));
-	fmt.printf("%s %s\n", foo(1));
-	fmt.printf("%s %s\n", foo(2));
-	fmt.printf("%s %s\n", foo(3));
-	fmt.printf("%s %s\n", foo(4));
-	fmt.println();
-
-
-	// A more "real" example
-	Error :: enum {
-		None,
-		WhyTheNumberThree,
-		TenIsTooBig,
 	};
 	};
-
-	Entity :: struct {
-		name: string;
-		id:   u32;
-	}
-
-	some_thing :: proc(input: int) -> (result: ^Entity = nil, err := Error.None) {
-		match {
-		case input == 3:  return err = Error.WhyTheNumberThree;
-		case input >= 10: return err = Error.TenIsTooBig;
-		}
-
-		e := new(Entity);
-		e.id = u32(input);
-
-		return result = e;
-	}
-}
-
-call_location :: proc() {
-	amazing :: proc(n: int, using loc := #caller_location) {
-		fmt.printf("%s(%d:%d) just asked to do something amazing.\n",
-				   fully_pathed_filename, line, column);
-		fmt.printf("Normal -> %d\n", n);
-		fmt.printf("Amazing -> %d\n", n+1);
-		fmt.println();
-	}
-
-	loc := #location(main);
-	fmt.println("`main` is located at", loc);
-
-	fmt.println("This line is located at", #location());
-	fmt.println();
-
-	amazing(3);
-	amazing(4, #location(call_location));
-
-	// See _preload.odin for the implementations of `assert` and `panic`
-
 }
 }
 
 
-
-explicit_parametric_polymorphic_procedures :: proc() {
-	// This is how `new` is actually implemented, see _preload.odin
-	alloc_type :: proc(T: type) -> ^T do return cast(^T)alloc(size_of(T), align_of(T));
-
-	int_ptr := alloc_type(int);
-	defer free(int_ptr);
-	int_ptr^ = 137;
-	fmt.println(int_ptr, int_ptr^);
-
-	// Named arguments work too!
-	another_ptr := alloc_type(T = f32);
-	defer free(another_ptr);
-
-
-	add :: proc(T: type, args: ...T) -> T {
-		res: T;
-		for arg in args do res += arg;
-		return res;
-	}
-
-	fmt.println("add =", add(int, 1, 2, 3, 4, 5, 6));
-
-	swap :: proc(T: type, a, b: ^T) {
-		tmp := a^;
-		a^ = b^;
-		b^ = tmp;
-	}
-
-	a, b: int = 3, 4;
-	fmt.println("Pre-swap:", a, b);
-	swap(int, &a, &b);
-	fmt.println("Post-swap:", a, b);
-	a, b = b, a; // Or use this syntax for this silly example case
-
-
-	Vector2 :: struct {x, y: f32;};
-	{
-		// A more complicated example using subtyping
-		// Something like this could be used in a game
-
-		Entity :: struct {
-			using position: Vector2;
-			flags:          u64;
-			id:             u64;
-			derived:        any;
-		}
-
-		Rock :: struct {
-			using entity: Entity;
-			heavy: bool;
-		}
-		Door :: struct {
-			using entity: Entity;
-			open:         bool;
-		}
-		Monster :: struct {
-			using entity: Entity;
-			is_robot:     bool;
-			is_zombie:    bool;
-		}
-
-		new_entity :: proc(T: type, x, y: f32) -> ^T {
-			result := new(T);
-			result.derived = result^;
-			result.x = x;
-			result.y = y;
-
-			return result;
-		}
-
-		entities: [dynamic]^Entity;
-
-		rock := new_entity(Rock, 3, 5);
-
-		// Named arguments work too!
-		door := new_entity(T = Door, x = 3, y = 6);
-
-		// And named arguments can be any order
-		monster := new_entity(
-			y = 1,
-			x = 2,
-			T = Monster,
-		);
-
-		append(&entities, rock, door, monster);
-
-		fmt.println("Subtyping");
-		for entity in entities {
-			match e in entity.derived {
-			case Rock:    fmt.println("Rock",    e.x, e.y);
-			case Door:    fmt.println("Door",    e.x, e.y);
-			case Monster: fmt.println("Monster", e.x, e.y);
-			}
-		}
-	}
-	{
-		Entity :: struct {
-			using position: Vector2;
-			flags:          u64;
-			id:             u64;
-			variant: union { Rock, Door, Monster };
-		}
-
-		Rock :: struct {
-			using entity: ^Entity;
-			heavy: bool;
-		}
-		Door :: struct {
-			using entity: ^Entity;
-			open:         bool;
-		}
-		Monster :: struct {
-			using entity: ^Entity;
-			is_robot:     bool;
-			is_zombie:    bool;
-		}
-
-		new_entity :: proc(T: type, x, y: f32) -> ^T {
-			result := new(Entity);
-			result.variant = T{entity = result};
-			result.x = x;
-			result.y = y;
-
-			return cast(^T)&result.variant;
-		}
-
-		entities: [dynamic]^Entity;
-
-		rock := new_entity(Rock, 3, 5);
-
-		// Named arguments work too!
-		door := new_entity(T = Door, x = 3, y = 6);
-
-		// And named arguments can be any order
-		monster := new_entity(
-			y = 1,
-			x = 2,
-			T = Monster,
-		);
-
-		append(&entities, rock, door, monster);
-
-		fmt.println("Union");
-		for entity in entities {
-			match e in entity.variant {
-			case Rock:    fmt.println("Rock",    e.x, e.y);
-			case Door:    fmt.println("Door",    e.x, e.y);
-			case Monster: fmt.println("Monster", e.x, e.y);
-			}
-		}
-	}
-}
-
-
-implicit_polymorphic_assignment :: proc() {
-	yep :: proc(p: proc(x: int)) {
-		p(123);
-	}
-
-	frank :: proc(x: $T)    do fmt.println("frank ->", x);
-	tim   :: proc(x, y: $T) do fmt.println("tim ->", x, y);
-	yep(frank);
-	// yep(tim);
-}
-
-
-
+Vector3 :: Vector(3, f32);
 
 
 main :: proc() {
 main :: proc() {
-/*
-	foo :: proc(x: i64,  y: f32) do fmt.println("#1", x, y);
-	foo :: proc(x: type, y: f32) do fmt.println("#2", type_info(x), y);
-	foo :: proc(x: type)         do fmt.println("#3", type_info(x));
-
-	f :: foo;
-
-	f(y = 3785.1546, x = 123);
-	f(x = int, y = 897.513);
-	f(x = f32);
-
-	general_stuff();
-	foreign_blocks();
-	default_arguments();
-	named_arguments();
-	default_return_values();
-	call_location();
-	explicit_parametric_polymorphic_procedures();
-	implicit_polymorphic_assignment();
-
-
-	// Command line argument(s)!
-	// -opt=0,1,2,3
-*/
-/*
-	program := "+ + * - /";
-	accumulator := 0;
-
-	for token in program {
-		match token {
-		case '+': accumulator += 1;
-		case '-': accumulator -= 1;
-		case '*': accumulator *= 2;
-		case '/': accumulator /= 2;
-		case: // Ignore everything else
-		}
-	}
-
-	fmt.printf("The program \"%s\" calculates the value %d\n",
-			   program, accumulator);
-*/
+	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);
 }
 }

+ 430 - 0
code/demo_backup.odin

@@ -0,0 +1,430 @@
+import (
+	"fmt.odin";
+	"atomics.odin";
+	"bits.odin";
+	"decimal.odin";
+	"hash.odin";
+	"math.odin";
+	"mem.odin";
+	"opengl.odin";
+	"os.odin";
+	"raw.odin";
+	"strconv.odin";
+	"strings.odin";
+	"sync.odin";
+	"sort.odin";
+	"types.odin";
+	"utf8.odin";
+	"utf16.odin";
+/*
+*/
+)
+
+
+general_stuff :: proc() {
+	// Complex numbers
+	a := 3 + 4i;
+	b: complex64 = 3 + 4i;
+	c: complex128 = 3 + 4i;
+	d := complex(2, 3);
+
+	e := a / conj(a);
+	fmt.println("(3+4i)/(3-4i) =", e);
+	fmt.println(real(e), "+", imag(e), "i");
+
+
+	// C-style variadic procedures
+	foreign __llvm_core {
+		// The variadic part allows for extra type checking too which C does not provide
+		c_printf :: proc(fmt: ^u8, #c_vararg args: ...any) -> i32 #link_name "printf" ---;
+	}
+	str := "%d\n\x00";
+	// c_printf(&str[0], i32(789456123));
+
+
+	Foo :: struct {
+		x: int;
+		y: f32;
+		z: string;
+	}
+	foo := Foo{123, 0.513, "A string"};
+	x, y, z := expand_to_tuple(foo);
+	fmt.println(x, y, z);
+	compile_assert(type_of(x) == int);
+	compile_assert(type_of(y) == f32);
+	compile_assert(type_of(z) == string);
+
+
+	// By default, all variables are zeroed
+	// This can be overridden with the "uninitialized value"
+	// This is similar to `nil` but applied to everything
+	undef_int: int = ---;
+
+
+	// Context system is now implemented using Implicit Parameter Passing (IPP)
+	// The previous implementation was Thread Local Storage (TLS)
+	// IPP has the advantage that it works on systems without TLS and that you can
+	// link the context to the stack frame and thus look at previous contexts
+	//
+	// It does mean that a pointer is implicitly passed procedures with the default
+	// Odin calling convention (#cc_odin)
+	// This can be overridden with something like #cc_contextless or #cc_c if performance
+	// is worried about
+
+}
+
+foreign_blocks :: proc() {
+	// See sys/windows.odin
+}
+
+default_arguments :: proc() {
+	hello :: proc(a: int = 9, b: int = 9) do fmt.printf("a is %d; b is %d\n", a, b);
+	fmt.println("\nTesting default arguments:");
+	hello(1, 2);
+	hello(1);
+	hello();
+}
+
+named_arguments :: proc() {
+	Colour :: enum {
+		Red,
+		Orange,
+		Yellow,
+		Green,
+		Blue,
+		Octarine,
+	};
+	using Colour;
+
+	make_character :: proc(name, catch_phrase: string, favourite_colour, least_favourite_colour: Colour) {
+		fmt.println();
+		fmt.printf("My name is %v and I like %v.  %v\n", name, favourite_colour, catch_phrase);
+	}
+
+	make_character("Frank", "¡Ay, caramba!", Blue, Green);
+
+
+	// As the procedures have more and more parameters, it is very easy
+	// to get many of the arguments in the wrong order especialy if the
+	// types are the same
+	make_character("¡Ay, caramba!", "Frank", Green, Blue);
+
+	// Named arguments help to disambiguate this problem
+	make_character(catch_phrase = "¡Ay, caramba!", name = "Frank",
+	               least_favourite_colour = Green, favourite_colour = Blue);
+
+
+	// The named arguments can be specifed in any order.
+	make_character(favourite_colour = Octarine, catch_phrase = "U wot m8!",
+	               least_favourite_colour = Green, name = "Dennis");
+
+
+	// NOTE: You cannot mix named arguments with normal values
+	/*
+	make_character("Dennis",
+	               favourite_colour = Octarine, catch_phrase = "U wot m8!",
+	               least_favourite_colour = Green);
+	*/
+
+
+	// Named arguments can also aid with default arguments
+	numerous_things :: proc(s: string, a := 1, b := 2, c := 3.14,
+	                        d := "The Best String!", e := false, f := 10.3/3.1, g := false) {
+		g_str := g ? "true" : "false";
+		fmt.printf("How many?! %s: %v\n", s, g_str);
+	}
+
+	numerous_things("First");
+	numerous_things(s = "Second", g = true);
+
+
+	// Default values can be placed anywhere, not just at the end like in other languages
+	weird :: proc(pre: string, mid: int = 0, post: string) {
+		fmt.println(pre, mid, post);
+	}
+
+	weird("How many things", 42, "huh?");
+	weird(pre = "Prefix", post = "Pat");
+
+}
+
+
+default_return_values :: proc() {
+	foo :: proc(x: int) -> (first: string = "Hellope", second := "world!") {
+		match x {
+		case 0: return;
+		case 1: return "Goodbye";
+		case 2: return "Goodbye", "cruel world...";
+		case 3: return second = "cruel world...", first = "Goodbye";
+		}
+
+		return second = "my old friend.";
+	}
+
+	fmt.printf("%s %s\n", foo(0));
+	fmt.printf("%s %s\n", foo(1));
+	fmt.printf("%s %s\n", foo(2));
+	fmt.printf("%s %s\n", foo(3));
+	fmt.printf("%s %s\n", foo(4));
+	fmt.println();
+
+
+	// A more "real" example
+	Error :: enum {
+		None,
+		WhyTheNumberThree,
+		TenIsTooBig,
+	};
+
+	Entity :: struct {
+		name: string;
+		id:   u32;
+	}
+
+	some_thing :: proc(input: int) -> (result: ^Entity = nil, err := Error.None) {
+		match {
+		case input == 3:  return err = Error.WhyTheNumberThree;
+		case input >= 10: return err = Error.TenIsTooBig;
+		}
+
+		e := new(Entity);
+		e.id = u32(input);
+
+		return result = e;
+	}
+}
+
+call_location :: proc() {
+	amazing :: proc(n: int, using loc := #caller_location) {
+		fmt.printf("%s(%d:%d) just asked to do something amazing.\n",
+				   fully_pathed_filename, line, column);
+		fmt.printf("Normal -> %d\n", n);
+		fmt.printf("Amazing -> %d\n", n+1);
+		fmt.println();
+	}
+
+	loc := #location(main);
+	fmt.println("`main` is located at", loc);
+
+	fmt.println("This line is located at", #location());
+	fmt.println();
+
+	amazing(3);
+	amazing(4, #location(call_location));
+
+	// See _preload.odin for the implementations of `assert` and `panic`
+
+}
+
+
+explicit_parametric_polymorphic_procedures :: proc() {
+	// This is how `new` is actually implemented, see _preload.odin
+	alloc_type :: proc(T: type) -> ^T do return cast(^T)alloc(size_of(T), align_of(T));
+
+	int_ptr := alloc_type(int);
+	defer free(int_ptr);
+	int_ptr^ = 137;
+	fmt.println(int_ptr, int_ptr^);
+
+	// Named arguments work too!
+	another_ptr := alloc_type(T = f32);
+	defer free(another_ptr);
+
+
+	add :: proc(T: type, args: ...T) -> T {
+		res: T;
+		for arg in args do res += arg;
+		return res;
+	}
+
+	fmt.println("add =", add(int, 1, 2, 3, 4, 5, 6));
+
+	swap :: proc(T: type, a, b: ^T) {
+		tmp := a^;
+		a^ = b^;
+		b^ = tmp;
+	}
+
+	a, b: int = 3, 4;
+	fmt.println("Pre-swap:", a, b);
+	swap(int, &a, &b);
+	fmt.println("Post-swap:", a, b);
+	a, b = b, a; // Or use this syntax for this silly example case
+
+
+	Vector2 :: struct {x, y: f32;};
+	{
+		// A more complicated example using subtyping
+		// Something like this could be used in a game
+
+		Entity :: struct {
+			using position: Vector2;
+			flags:          u64;
+			id:             u64;
+			derived:        any;
+		}
+
+		Rock :: struct {
+			using entity: Entity;
+			heavy: bool;
+		}
+		Door :: struct {
+			using entity: Entity;
+			open:         bool;
+		}
+		Monster :: struct {
+			using entity: Entity;
+			is_robot:     bool;
+			is_zombie:    bool;
+		}
+
+		new_entity :: proc(T: type, x, y: f32) -> ^T {
+			result := new(T);
+			result.derived = result^;
+			result.x = x;
+			result.y = y;
+
+			return result;
+		}
+
+		entities: [dynamic]^Entity;
+
+		rock := new_entity(Rock, 3, 5);
+
+		// Named arguments work too!
+		door := new_entity(T = Door, x = 3, y = 6);
+
+		// And named arguments can be any order
+		monster := new_entity(
+			y = 1,
+			x = 2,
+			T = Monster,
+		);
+
+		append(&entities, rock, door, monster);
+
+		fmt.println("Subtyping");
+		for entity in entities {
+			match e in entity.derived {
+			case Rock:    fmt.println("Rock",    e.x, e.y);
+			case Door:    fmt.println("Door",    e.x, e.y);
+			case Monster: fmt.println("Monster", e.x, e.y);
+			}
+		}
+	}
+	{
+		Entity :: struct {
+			using position: Vector2;
+			flags:          u64;
+			id:             u64;
+			variant: union { Rock, Door, Monster };
+		}
+
+		Rock :: struct {
+			using entity: ^Entity;
+			heavy: bool;
+		}
+		Door :: struct {
+			using entity: ^Entity;
+			open:         bool;
+		}
+		Monster :: struct {
+			using entity: ^Entity;
+			is_robot:     bool;
+			is_zombie:    bool;
+		}
+
+		new_entity :: proc(T: type, x, y: f32) -> ^T {
+			result := new(Entity);
+			result.variant = T{entity = result};
+			result.x = x;
+			result.y = y;
+
+			return cast(^T)&result.variant;
+		}
+
+		entities: [dynamic]^Entity;
+
+		rock := new_entity(Rock, 3, 5);
+
+		// Named arguments work too!
+		door := new_entity(T = Door, x = 3, y = 6);
+
+		// And named arguments can be any order
+		monster := new_entity(
+			y = 1,
+			x = 2,
+			T = Monster,
+		);
+
+		append(&entities, rock, door, monster);
+
+		fmt.println("Union");
+		for entity in entities {
+			match e in entity.variant {
+			case Rock:    fmt.println("Rock",    e.x, e.y);
+			case Door:    fmt.println("Door",    e.x, e.y);
+			case Monster: fmt.println("Monster", e.x, e.y);
+			}
+		}
+	}
+}
+
+
+implicit_polymorphic_assignment :: proc() {
+	yep :: proc(p: proc(x: int)) {
+		p(123);
+	}
+
+	frank :: proc(x: $T)    do fmt.println("frank ->", x);
+	tim   :: proc(x, y: $T) do fmt.println("tim ->", x, y);
+	yep(frank);
+	// yep(tim);
+}
+
+
+
+
+main :: proc() {
+/*
+	foo :: proc(x: i64,  y: f32) do fmt.println("#1", x, y);
+	foo :: proc(x: type, y: f32) do fmt.println("#2", type_info(x), y);
+	foo :: proc(x: type)         do fmt.println("#3", type_info(x));
+
+	f :: foo;
+
+	f(y = 3785.1546, x = 123);
+	f(x = int, y = 897.513);
+	f(x = f32);
+
+	general_stuff();
+	foreign_blocks();
+	default_arguments();
+	named_arguments();
+	default_return_values();
+	call_location();
+	explicit_parametric_polymorphic_procedures();
+	implicit_polymorphic_assignment();
+
+
+	// Command line argument(s)!
+	// -opt=0,1,2,3
+*/
+/*
+	program := "+ + * - /";
+	accumulator := 0;
+
+	for token in program {
+		match token {
+		case '+': accumulator += 1;
+		case '-': accumulator -= 1;
+		case '*': accumulator *= 2;
+		case '/': accumulator /= 2;
+		case: // Ignore everything else
+		}
+	}
+
+	fmt.printf("The program \"%s\" calculates the value %d\n",
+			   program, accumulator);
+*/
+}

+ 1 - 1
src/check_decl.cpp

@@ -558,7 +558,7 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count
 	if (type_expr != nullptr) {
 	if (type_expr != nullptr) {
 		e->type = check_type(c, type_expr);
 		e->type = check_type(c, type_expr);
 	}
 	}
-	if (e->type != nullptr && is_type_polymorphic(e->type)) {
+	if (e->type != nullptr && is_type_polymorphic(base_type(e->type))) {
 		error(e->token, "Invalid use of a polymorphic type in %.*s", LIT(context_name));
 		error(e->token, "Invalid use of a polymorphic type in %.*s", LIT(context_name));
 		e->type = t_invalid;
 		e->type = t_invalid;
 	}
 	}

+ 461 - 64
src/check_expr.cpp

@@ -12,6 +12,7 @@ enum CallArgumentError {
 	CallArgumentError_ParameterNotFound,
 	CallArgumentError_ParameterNotFound,
 	CallArgumentError_ParameterMissing,
 	CallArgumentError_ParameterMissing,
 	CallArgumentError_DuplicateParameter,
 	CallArgumentError_DuplicateParameter,
+	CallArgumentError_NoneConstantParameter,
 };
 };
 
 
 enum CallArgumentErrorMode {
 enum CallArgumentErrorMode {
@@ -972,7 +973,7 @@ Entity *make_names_field_for_record(Checker *c, Scope *scope) {
 	return e;
 	return e;
 }
 }
 
 
-void check_struct_type(Checker *c, Type *struct_type, AstNode *node) {
+void check_struct_type(Checker *c, Type *struct_type, AstNode *node, Array<Operand> *poly_operands) {
 	GB_ASSERT(is_type_struct(struct_type));
 	GB_ASSERT(is_type_struct(struct_type));
 	ast_node(st, StructType, node);
 	ast_node(st, StructType, node);
 
 
@@ -984,11 +985,132 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) {
 			min_field_count += f->names.count;
 			min_field_count += f->names.count;
 		case_end;
 		case_end;
 		}
 		}
-
 	}
 	}
 	struct_type->Record.names = make_names_field_for_record(c, c->context.scope);
 	struct_type->Record.names = make_names_field_for_record(c, c->context.scope);
 
 
-	auto fields = check_fields(c, node, st->fields, min_field_count, str_lit("struct"));
+	Type *polymorphic_params = nullptr;
+	bool is_polymorphic = false;
+	if (st->polymorphic_params != nullptr) {
+		ast_node(field_list, FieldList, st->polymorphic_params);
+		Array<AstNode *> params = field_list->list;
+		if (params.count != 0) {
+			isize variable_count = 0;
+			for_array(i, params) {
+				AstNode *field = params[i];
+				if (ast_node_expect(field, AstNode_Field)) {
+					ast_node(f, Field, field);
+					variable_count += gb_max(f->names.count, 1);
+				}
+			}
+
+			Array<Entity *> entities = {};
+			array_init(&entities, c->allocator, variable_count);
+
+			for_array(i, params) {
+				AstNode *param = params[i];
+				if (param->kind != AstNode_Field) {
+					continue;
+				}
+				ast_node(p, Field, param);
+				AstNode *type_expr = p->type;
+				Type *type = nullptr;
+				bool is_type_param = false;
+				bool is_type_polymorphic_type = false;
+				if (type_expr == nullptr) {
+					error(param, "Expected a type for this parameter");
+					continue;
+				}
+				if (type_expr->kind == AstNode_Ellipsis) {
+					type_expr = type_expr->Ellipsis.expr;
+					error(param, "A polymorphic parameter cannot be variadic");
+				}
+				if (type_expr->kind == AstNode_TypeType) {
+					is_type_param = true;
+					type = make_type_generic(c->allocator, 0, str_lit(""));
+				} else {
+					type = check_type(c, type_expr);
+					if (is_type_polymorphic(type)) {
+						is_type_polymorphic_type = true;
+					}
+				}
+
+				if (type == nullptr) {
+					error(params[i], "Invalid parameter type");
+					type = t_invalid;
+				}
+				if (is_type_untyped(type)) {
+					if (is_type_untyped_undef(type)) {
+						error(params[i], "Cannot determine parameter type from ---");
+					} else {
+						error(params[i], "Cannot determine parameter type from a nil");
+					}
+					type = t_invalid;
+				}
+
+				if (is_type_polymorphic_type) {
+					gbString str = type_to_string(type);
+					error(params[i], "Parameter types cannot be polymorphic, got %s", str);
+					gb_string_free(str);
+					type = t_invalid;
+				}
+
+				if (!is_type_param && !is_type_constant_type(type)) {
+					gbString str = type_to_string(type);
+					error(params[i], "A parameter must be a valid constant type, got %s", str);
+					gb_string_free(str);
+				}
+
+				Scope *scope = c->context.scope;
+				for_array(j, p->names) {
+					AstNode *name = p->names[j];
+					if (!ast_node_expect(name, AstNode_Ident)) {
+						continue;
+					}
+					Entity *e = nullptr;
+
+					Token token = name->Ident.token;
+
+					if (poly_operands != nullptr) {
+						Operand operand = (*poly_operands)[entities.count];
+						if (is_type_param) {
+							GB_ASSERT(operand.mode == Addressing_Type);
+							e = make_entity_type_name(c->allocator, scope, token, operand.type);
+							e->TypeName.is_type_alias = true;
+						} else {
+							GB_ASSERT(operand.mode == Addressing_Constant);
+							e = make_entity_constant(c->allocator, scope, token, operand.type, operand.value);
+						}
+					} else {
+						if (is_type_param) {
+							e = make_entity_type_name(c->allocator, scope, token, type);
+							e->TypeName.is_type_alias = true;
+						} else {
+							e = make_entity_constant(c->allocator, scope, token, type, empty_exact_value);
+						}
+					}
+
+					add_entity(c, scope, name, e);
+					array_add(&entities, e);
+				}
+			}
+
+			if (entities.count > 0) {
+				Type *tuple = make_type_tuple(c->allocator);
+				tuple->Tuple.variables = entities.data;
+				tuple->Tuple.variable_count = entities.count;
+
+				polymorphic_params = tuple;
+			}
+		}
+	}
+
+	is_polymorphic = polymorphic_params != nullptr && poly_operands == nullptr;
+
+	Array<Entity *> fields = {};
+
+	if (!is_polymorphic) {
+		fields = check_fields(c, node, st->fields, min_field_count, str_lit("struct"));
+	}
 
 
 	struct_type->Record.scope               = c->context.scope;
 	struct_type->Record.scope               = c->context.scope;
 	struct_type->Record.is_packed           = st->is_packed;
 	struct_type->Record.is_packed           = st->is_packed;
@@ -996,6 +1118,9 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) {
 	struct_type->Record.fields              = fields.data;
 	struct_type->Record.fields              = fields.data;
 	struct_type->Record.fields_in_src_order = fields.data;
 	struct_type->Record.fields_in_src_order = fields.data;
 	struct_type->Record.field_count         = fields.count;
 	struct_type->Record.field_count         = fields.count;
+	struct_type->Record.polymorphic_params  = polymorphic_params;
+	struct_type->Record.is_polymorphic      = is_polymorphic;
+
 
 
 	type_set_offsets(c->allocator, struct_type);
 	type_set_offsets(c->allocator, struct_type);
 
 
@@ -1703,53 +1828,55 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
 
 
 		for_array(j, p->names) {
 		for_array(j, p->names) {
 			AstNode *name = p->names[j];
 			AstNode *name = p->names[j];
-			if (ast_node_expect(name, AstNode_Ident)) {
-				Entity *param = nullptr;
-				if (is_type_param) {
-					if (operands != nullptr) {
-						Operand o = (*operands)[variable_index];
-						if (o.mode == Addressing_Type) {
-							type = o.type;
-						} else {
-							if (!c->context.no_polymorphic_errors) {
-								error(o.expr, "Expected a type to assign to the type parameter");
-							}
-							success = false;
-							type = t_invalid;
-						}
-					}
-					param = make_entity_type_name(c->allocator, scope, name->Ident.token, type);
-					param->TypeName.is_type_alias = true;
-				} else {
-					if (operands != nullptr && is_type_polymorphic_type) {
-						Operand op = (*operands)[variable_index];
-						type = determine_type_from_polymorphic(c, type, op);
-						if (type == t_invalid) {
-							success = false;
-						}
-					}
+			if (!ast_node_expect(name, AstNode_Ident)) {
+				continue;
+			}
 
 
-					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
+			Entity *param = nullptr;
+			if (is_type_param) {
+				if (operands != nullptr) {
+					Operand o = (*operands)[variable_index];
+					if (o.mode == Addressing_Type) {
+						type = o.type;
+					} else {
+						if (!c->context.no_polymorphic_errors) {
+							error(o.expr, "Expected a type to assign to the type parameter");
 						}
 						}
+						success = false;
+						type = t_invalid;
+					}
+				}
+				param = make_entity_type_name(c->allocator, scope, name->Ident.token, type);
+				param->TypeName.is_type_alias = true;
+			} else {
+				if (operands != nullptr && is_type_polymorphic_type) {
+					Operand op = (*operands)[variable_index];
+					type = determine_type_from_polymorphic(c, type, op);
+					if (type == t_invalid) {
+						success = false;
 					}
 					}
-
-					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) {
 				if (p->flags&FieldFlag_no_alias) {
-					param->flags |= EntityFlag_NoAlias;
+					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
+					}
 				}
 				}
 
 
-				add_entity(c, scope, name, param);
-				variables[variable_index++] = param;
+				param = make_entity_param(c->allocator, scope, name->Ident.token, type,
+				                          (p->flags&FieldFlag_using) != 0, false);
+				param->Variable.default_value = value;
+				param->Variable.default_is_nil = default_is_nil;
+				param->Variable.default_is_location = default_is_location;
+
+			}
+			if (p->flags&FieldFlag_no_alias) {
+				param->flags |= EntityFlag_NoAlias;
 			}
 			}
+
+			add_entity(c, scope, name, param);
+			variables[variable_index++] = param;
 		}
 		}
 	}
 	}
 
 
@@ -2358,7 +2485,7 @@ i64 check_array_or_map_count(Checker *c, AstNode *e, bool is_map) {
 				if (count >= 0) {
 				if (count >= 0) {
 					return count;
 					return count;
 				}
 				}
-				error(e, "Invalid array count");
+				error(e, "Invalid negative array count %lld", cast(long long)count);
 			}
 			}
 			return 0;
 			return 0;
 		}
 		}
@@ -2663,7 +2790,7 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type)
 		*type = make_type_struct(c->allocator);
 		*type = make_type_struct(c->allocator);
 		set_base_type(named_type, *type);
 		set_base_type(named_type, *type);
 		check_open_scope(c, e);
 		check_open_scope(c, e);
-		check_struct_type(c, *type, e);
+		check_struct_type(c, *type, e, nullptr);
 		check_close_scope(c);
 		check_close_scope(c);
 		(*type)->Record.node = e;
 		(*type)->Record.node = e;
 		return true;
 		return true;
@@ -6114,8 +6241,263 @@ Entity *find_using_index_expr(Type *t) {
 	return nullptr;
 	return nullptr;
 }
 }
 
 
+isize lookup_polymorphic_struct_parameter(TypeRecord *st, String parameter_name) {
+	if (!st->is_polymorphic) return -1;
+
+	TypeTuple *params = &st->polymorphic_params->Tuple;
+	isize param_count = params->variable_count;
+	for (isize i = 0; i < param_count; i++) {
+		Entity *e = params->variables[i];
+		String name = e->token.string;
+		if (is_blank_ident(name)) {
+			continue;
+		}
+		if (name == parameter_name) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+CallArgumentError check_polymorphic_struct_type(Checker *c, Operand *operand, AstNode *call) {
+	ast_node(ce, CallExpr, call);
+
+	Type *original_type = operand->type;
+	Type *struct_type = base_type(operand->type);
+	GB_ASSERT(is_type_struct(struct_type));
+	TypeRecord *st = &struct_type->Record;
+	GB_ASSERT(st->is_polymorphic);
+
+	bool show_error = true;
+
+	Array<Operand> operands = {};
+	defer (array_free(&operands));
+
+	bool named_fields = false;
+
+	if (is_call_expr_field_value(ce)) {
+		named_fields = true;
+		array_init_count(&operands, heap_allocator(), ce->args.count);
+		for_array(i, ce->args) {
+			AstNode *arg = ce->args[i];
+			ast_node(fv, FieldValue, arg);
+			check_expr_or_type(c, &operands[i], fv->value);
+		}
+
+		bool vari_expand = (ce->ellipsis.pos.line != 0);
+		if (vari_expand) {
+			error(ce->ellipsis, "Invalid use of `..` in a polymorphic type call`");
+		}
+
+	} else {
+		array_init(&operands, heap_allocator(), 2*ce->args.count);
+		check_unpack_arguments(c, -1, &operands, ce->args, false);
+	}
+
+	CallArgumentError err = CallArgumentError_None;
+
+	TypeTuple *tuple = &st->polymorphic_params->Tuple;
+	isize param_count = tuple->variable_count;
+
+	Array<Operand> ordered_operands = operands;
+	if (named_fields) {
+		bool *visited = gb_alloc_array(c->allocator, bool, param_count);
+
+		array_init_count(&ordered_operands, c->tmp_allocator, param_count);
+
+		for_array(i, ce->args) {
+			AstNode *arg = ce->args[i];
+			ast_node(fv, FieldValue, arg);
+			if (fv->field->kind != AstNode_Ident) {
+				if (show_error) {
+					gbString expr_str = expr_to_string(fv->field);
+					error(arg, "Invalid parameter name `%s` in polymorphic type call", expr_str);
+					gb_string_free(expr_str);
+				}
+				err = CallArgumentError_InvalidFieldValue;
+				continue;
+			}
+			String name = fv->field->Ident.token.string;
+			isize index = lookup_polymorphic_struct_parameter(st, name);
+			if (index < 0) {
+				if (show_error) {
+					error(arg, "No parameter named `%.*s` for this polymorphic type", LIT(name));
+				}
+				err = CallArgumentError_ParameterNotFound;
+				continue;
+			}
+			if (visited[index]) {
+				if (show_error) {
+					error(arg, "Duplicate parameter `%.*s` in polymorphic type", LIT(name));
+				}
+				err = CallArgumentError_DuplicateParameter;
+				continue;
+			}
+
+			visited[index] = true;
+			ordered_operands[index] = operands[i];
+		}
+
+		for (isize i = 0; i < param_count; i++) {
+			if (!visited[i]) {
+				Entity *e = tuple->variables[i];
+				if (is_blank_ident(e->token)) {
+					continue;
+				}
+
+				if (show_error) {
+					if (e->kind == Entity_TypeName) {
+						error(call, "Type parameter `%.*s` is missing in polymorphic type call",
+						      LIT(e->token.string));
+					} else {
+						gbString str = type_to_string(e->type);
+						error(call, "Parameter `%.*s` of type `%s` is missing in polymorphic type call",
+						      LIT(e->token.string), str);
+						gb_string_free(str);
+					}
+				}
+				err = CallArgumentError_ParameterMissing;
+			}
+		}
+	}
+
+	if (err != 0) {
+		operand->mode = Addressing_Invalid;
+		return err;
+	}
+
+	i64 score = 0;
+	for (isize i = 0; i < param_count; i++) {
+		Operand *o = &ordered_operands[i];
+		if (o->mode == Addressing_Invalid) {
+			continue;
+		}
+		Entity *e = tuple->variables[i];
+
+		if (e->kind == Entity_TypeName) {
+			if (o->mode != Addressing_Type) {
+				if (show_error) {
+					error(o->expr, "Expected a type for the argument `%.*s`", LIT(e->token.string));
+				}
+				err = CallArgumentError_WrongTypes;
+			}
+			if (are_types_identical(e->type, o->type)) {
+				score += assign_score_function(1);
+			} else {
+				score += assign_score_function(10);
+			}
+		} else {
+			i64 s = 0;
+			if (!check_is_assignable_to_with_score(c, o, e->type, &s)) {
+				if (show_error) {
+					check_assignment(c, o, e->type, str_lit("polymorphic type argument"));
+				}
+				err = CallArgumentError_WrongTypes;
+			}
+			o->type = e->type;
+			if (o->mode != Addressing_Constant) {
+				if (show_error) {
+					error(o->expr, "Expected a constant value for this polymorphic type argument");
+				}
+				err = CallArgumentError_NoneConstantParameter;
+			}
+			score += s;
+		}
+	}
+
+	if (param_count < ordered_operands.count) {
+		error(call, "Too many polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count);
+		err = CallArgumentError_TooManyArguments;
+	} else if (param_count > ordered_operands.count) {
+		error(call, "Too few polymorphic type arguments, expected %td, got %td", param_count, ordered_operands.count);
+		err = CallArgumentError_TooFewArguments;
+	}
+
+	if (err == 0) {
+		// TODO(bill): Check for previous types
+		gbAllocator a = c->allocator;
+
+		auto *found_gen_types = map_get(&c->info.gen_types, hash_pointer(original_type));
+
+		if (found_gen_types != nullptr) {
+			for_array(i, *found_gen_types) {
+				Entity *e = (*found_gen_types)[i];
+				Type *t = base_type(e->type);
+				TypeTuple *tuple = &t->Record.polymorphic_params->Tuple;
+				bool ok = true;
+				GB_ASSERT(param_count == tuple->variable_count);
+				for (isize j = 0; j < param_count; j++) {
+					Entity *p = tuple->variables[j];
+					Operand o = ordered_operands[j];
+					if (p->kind == Entity_TypeName) {
+						if (!are_types_identical(o.type, p->type)) {
+							ok = false;
+						}
+					} else if (p->kind == Entity_Constant) {
+						if (!are_types_identical(o.type, p->type)) {
+							ok = false;
+						}
+						if (!compare_exact_values(Token_CmpEq, o.value, p->Constant.value)) {
+							ok = false;
+						}
+					} else {
+						GB_PANIC("Unknown entity kind");
+					}
+				}
+				if (ok) {
+					operand->mode = Addressing_Type;
+					operand->type = e->type;
+					return err;
+				}
+			}
+		}
+
+		String generated_name = make_string_c(expr_to_string(call));
+
+		Type *named_type = make_type_named(a, generated_name, nullptr, nullptr);
+		Type *struct_type = make_type_struct(a);
+		AstNode *node = clone_ast_node(a, st->node);
+		set_base_type(named_type, struct_type);
+		check_open_scope(c, node);
+		check_struct_type(c, struct_type, node, &ordered_operands);
+		check_close_scope(c);
+		struct_type->Record.node = node;
+
+		Entity *e = nullptr;
+
+		{
+			Token token = ast_node_token(node);
+			token.kind = Token_String;
+			token.string = generated_name;
+
+			AstNode *node = gb_alloc_item(a, AstNode);
+			node->kind = AstNode_Ident;
+			node->Ident.token = token;
+
+			e = make_entity_type_name(a, st->scope->parent, token, named_type);
+			add_entity(c, st->scope->parent, node, e);
+			add_entity_use(c, node, e);
+		}
+
+		named_type->Named.type_name = e;
+
+		if (found_gen_types) {
+			array_add(found_gen_types, e);
+		} else {
+			Array<Entity *> array = {};
+			array_init(&array, heap_allocator());
+			array_add(&array, e);
+			map_set(&c->info.gen_types, hash_pointer(original_type), array);
+		}
+
+		operand->mode = Addressing_Type;
+		operand->type = named_type;
+	}
+	return err;
+}
+
+
 ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
 ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
-	GB_ASSERT(call->kind == AstNode_CallExpr);
 	ast_node(ce, CallExpr, call);
 	ast_node(ce, CallExpr, call);
 	if (ce->proc != nullptr &&
 	if (ce->proc != nullptr &&
 	    ce->proc->kind == AstNode_BasicDirective) {
 	    ce->proc->kind == AstNode_BasicDirective) {
@@ -6170,28 +6552,43 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
 
 
 	if (operand->mode == Addressing_Type) {
 	if (operand->mode == Addressing_Type) {
 		Type *t = operand->type;
 		Type *t = operand->type;
-		gbString str = type_to_string(t);
-		defer (gb_string_free(str));
-
-		operand->mode = Addressing_Invalid;
-		isize arg_count = ce->args.count;
-		switch (arg_count) {
-		case 0:  error(call, "Missing argument in conversion to `%s`", str);   break;
-		default: error(call, "Too many arguments in conversion to `%s`", str); break;
-		case 1: {
-			AstNode *arg = ce->args[0];
-			if (arg->kind == AstNode_FieldValue) {
-				error(call, "`field = value` cannot be used in a type conversion");
-				arg = arg->FieldValue.value;
-				// NOTE(bill): Carry on the cast regardless
+		if (is_type_polymorphic_struct(t)) {
+			auto err = check_polymorphic_struct_type(c, operand, call);
+			if (err == 0) {
+				AstNode *ident = operand->expr;
+				while (ident->kind == AstNode_SelectorExpr) {
+					AstNode *s = ident->SelectorExpr.selector;
+					ident = s;
+				}
+				add_entity_use(c, ident, entity_of_ident(&c->info, ident));
+				add_type_and_value(&c->info, call, Addressing_Type, operand->type, empty_exact_value);
+			} else {
+				operand->mode = Addressing_Invalid;
+				operand->type = t_invalid;
 			}
 			}
-			check_expr(c, operand, arg);
-			if (operand->mode != Addressing_Invalid) {
-				check_cast(c, operand, t);
+		} else {
+			gbString str = type_to_string(t);
+			defer (gb_string_free(str));
+
+			operand->mode = Addressing_Invalid;
+			isize arg_count = ce->args.count;
+			switch (arg_count) {
+			case 0:  error(call, "Missing argument in conversion to `%s`", str);   break;
+			default: error(call, "Too many arguments in conversion to `%s`", str); break;
+			case 1: {
+				AstNode *arg = ce->args[0];
+				if (arg->kind == AstNode_FieldValue) {
+					error(call, "`field = value` cannot be used in a type conversion");
+					arg = arg->FieldValue.value;
+					// NOTE(bill): Carry on the cast regardless
+				}
+				check_expr(c, operand, arg);
+				if (operand->mode != Addressing_Invalid) {
+					check_cast(c, operand, t);
+				}
+			} break;
 			}
 			}
-		} break;
 		}
 		}
-
 		return Expr_Expr;
 		return Expr_Expr;
 	}
 	}
 
 

+ 126 - 125
src/check_stmt.cpp

@@ -1666,150 +1666,151 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 	case_end;
 	case_end;
 
 
 	case_ast_node(vd, ValueDecl, node);
 	case_ast_node(vd, ValueDecl, node);
-		if (vd->is_mutable) {
-			Entity **entities = gb_alloc_array(c->allocator, Entity *, vd->names.count);
-			isize entity_count = 0;
+		if (!vd->is_mutable) {
+			break;
+		}
+		Entity **entities = gb_alloc_array(c->allocator, Entity *, vd->names.count);
+		isize entity_count = 0;
 
 
-			if (vd->flags & VarDeclFlag_thread_local) {
-				vd->flags &= ~VarDeclFlag_thread_local;
-				error(node, "`thread_local` may only be applied to a variable declaration");
-			}
+		if (vd->flags & VarDeclFlag_thread_local) {
+			vd->flags &= ~VarDeclFlag_thread_local;
+			error(node, "`thread_local` may only be applied to a variable declaration");
+		}
 
 
-			for_array(i, vd->names) {
-				AstNode *name = vd->names[i];
-				Entity *entity = nullptr;
-				if (name->kind != AstNode_Ident) {
-					error(name, "A variable declaration must be an identifier");
-				} else {
-					Token token = name->Ident.token;
-					String str = token.string;
-					Entity *found = nullptr;
-					// NOTE(bill): Ignore assignments to `_`
-					if (!is_blank_ident(str)) {
-						found = current_scope_lookup_entity(c->context.scope, str);
-					}
-					if (found == nullptr) {
-						entity = make_entity_variable(c->allocator, c->context.scope, token, nullptr, false);
-						entity->identifier = name;
-
-						AstNode *fl = c->context.curr_foreign_library;
-						if (fl != nullptr) {
-							GB_ASSERT(fl->kind == AstNode_Ident);
-							entity->Variable.is_foreign = true;
-							entity->Variable.foreign_library_ident = fl;
-						}
-					} else {
-						TokenPos pos = found->token.pos;
-						error(token,
-						      "Redeclaration of `%.*s` in this scope\n"
-						      "\tat %.*s(%td:%td)",
-						      LIT(str), LIT(pos.file), pos.line, pos.column);
-						entity = found;
-					}
-				}
-				if (entity == nullptr) {
-					entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
+		for_array(i, vd->names) {
+			AstNode *name = vd->names[i];
+			Entity *entity = nullptr;
+			if (name->kind != AstNode_Ident) {
+				error(name, "A variable declaration must be an identifier");
+			} else {
+				Token token = name->Ident.token;
+				String str = token.string;
+				Entity *found = nullptr;
+				// NOTE(bill): Ignore assignments to `_`
+				if (!is_blank_ident(str)) {
+					found = current_scope_lookup_entity(c->context.scope, str);
 				}
 				}
-				entity->parent_proc_decl = c->context.curr_proc_decl;
-				entities[entity_count++] = entity;
-			}
-
-			Type *init_type = nullptr;
-			if (vd->type) {
-				init_type = check_type(c, vd->type, nullptr);
-				if (init_type == nullptr) {
-					init_type = t_invalid;
-				} else if (is_type_polymorphic(init_type)) {
-					error(vd->type, "Invalid use of a polymorphic type in variable declaration");
-					init_type = t_invalid;
+				if (found == nullptr) {
+					entity = make_entity_variable(c->allocator, c->context.scope, token, nullptr, false);
+					entity->identifier = name;
+
+					AstNode *fl = c->context.curr_foreign_library;
+					if (fl != nullptr) {
+						GB_ASSERT(fl->kind == AstNode_Ident);
+						entity->Variable.is_foreign = true;
+						entity->Variable.foreign_library_ident = fl;
+					}
+				} else {
+					TokenPos pos = found->token.pos;
+					error(token,
+					      "Redeclaration of `%.*s` in this scope\n"
+					      "\tat %.*s(%td:%td)",
+					      LIT(str), LIT(pos.file), pos.line, pos.column);
+					entity = found;
 				}
 				}
 			}
 			}
+			if (entity == nullptr) {
+				entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
+			}
+			entity->parent_proc_decl = c->context.curr_proc_decl;
+			entities[entity_count++] = entity;
+		}
 
 
-			for (isize i = 0; i < entity_count; i++) {
-				Entity *e = entities[i];
-				GB_ASSERT(e != nullptr);
-				if (e->flags & EntityFlag_Visited) {
-					e->type = t_invalid;
-					continue;
-				}
-				e->flags |= EntityFlag_Visited;
+		Type *init_type = nullptr;
+		if (vd->type != nullptr) {
+			init_type = check_type(c, vd->type, nullptr);
+			if (init_type == nullptr) {
+				init_type = t_invalid;
+			} else if (is_type_polymorphic(base_type(init_type))) {
+				error(vd->type, "Invalid use of a polymorphic type in variable declaration");
+				init_type = t_invalid;
+			}
+		}
 
 
-				if (e->type == nullptr) {
-					e->type = init_type;
-				}
+		for (isize i = 0; i < entity_count; i++) {
+			Entity *e = entities[i];
+			GB_ASSERT(e != nullptr);
+			if (e->flags & EntityFlag_Visited) {
+				e->type = t_invalid;
+				continue;
 			}
 			}
+			e->flags |= EntityFlag_Visited;
 
 
-			check_arity_match(c, vd);
-			check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration"));
+			if (e->type == nullptr) {
+				e->type = init_type;
+			}
+		}
 
 
-			for (isize i = 0; i < entity_count; i++) {
-				Entity *e = entities[i];
-				if (e->Variable.is_foreign) {
-					if (vd->values.count > 0) {
-						error(e->token, "A foreign variable declaration cannot have a default value");
-					}
-					init_entity_foreign_library(c, e);
+		check_arity_match(c, vd);
+		check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration"));
 
 
-					String name = e->token.string;
-					auto *fp = &c->info.foreigns;
-					HashKey key = hash_string(name);
-					Entity **found = map_get(fp, key);
-					if (found) {
-						Entity *f = *found;
-						TokenPos pos = f->token.pos;
-						Type *this_type = base_type(e->type);
-						Type *other_type = base_type(f->type);
-						if (!are_types_identical(this_type, other_type)) {
-							error(e->token,
-							      "Foreign entity `%.*s` previously declared elsewhere with a different type\n"
-							      "\tat %.*s(%td:%td)",
-							      LIT(name), LIT(pos.file), pos.line, pos.column);
-						}
-					} else {
-						map_set(fp, key, e);
+		for (isize i = 0; i < entity_count; i++) {
+			Entity *e = entities[i];
+			if (e->Variable.is_foreign) {
+				if (vd->values.count > 0) {
+					error(e->token, "A foreign variable declaration cannot have a default value");
+				}
+				init_entity_foreign_library(c, e);
+
+				String name = e->token.string;
+				auto *fp = &c->info.foreigns;
+				HashKey key = hash_string(name);
+				Entity **found = map_get(fp, key);
+				if (found) {
+					Entity *f = *found;
+					TokenPos pos = f->token.pos;
+					Type *this_type = base_type(e->type);
+					Type *other_type = base_type(f->type);
+					if (!are_types_identical(this_type, other_type)) {
+						error(e->token,
+						      "Foreign entity `%.*s` previously declared elsewhere with a different type\n"
+						      "\tat %.*s(%td:%td)",
+						      LIT(name), LIT(pos.file), pos.line, pos.column);
 					}
 					}
+				} else {
+					map_set(fp, key, e);
 				}
 				}
-				add_entity(c, c->context.scope, e->identifier, e);
 			}
 			}
+			add_entity(c, c->context.scope, e->identifier, e);
+		}
 
 
-			if ((vd->flags & VarDeclFlag_using) != 0) {
-				Token token = ast_node_token(node);
-				if (vd->type != nullptr && entity_count > 1) {
-					error(token, "`using` can only be applied to one variable of the same type");
-					// TODO(bill): Should a `continue` happen here?
-				}
+		if ((vd->flags & VarDeclFlag_using) != 0) {
+			Token token = ast_node_token(node);
+			if (vd->type != nullptr && entity_count > 1) {
+				error(token, "`using` can only be applied to one variable of the same type");
+				// TODO(bill): Should a `continue` happen here?
+			}
 
 
-				for (isize entity_index = 0; entity_index < entity_count; entity_index++) {
-					Entity *e = entities[entity_index];
-					if (e == nullptr) {
-						continue;
-					}
-					if (e->kind != Entity_Variable) {
-						continue;
-					}
-					bool is_immutable = e->Variable.is_immutable;
-					String name = e->token.string;
-					Type *t = base_type(type_deref(e->type));
-
-					if (is_type_struct(t) || is_type_raw_union(t)) {
-						Scope *scope = scope_of_node(&c->info, t->Record.node);
-						for_array(i, scope->elements.entries) {
-							Entity *f = scope->elements.entries[i].value;
-							if (f->kind == Entity_Variable) {
-								Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
-								uvar->Variable.is_immutable = is_immutable;
-								Entity *prev = scope_insert_entity(c->context.scope, uvar);
-								if (prev != nullptr) {
-									error(token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
-									return;
-								}
+			for (isize entity_index = 0; entity_index < entity_count; entity_index++) {
+				Entity *e = entities[entity_index];
+				if (e == nullptr) {
+					continue;
+				}
+				if (e->kind != Entity_Variable) {
+					continue;
+				}
+				bool is_immutable = e->Variable.is_immutable;
+				String name = e->token.string;
+				Type *t = base_type(type_deref(e->type));
+
+				if (is_type_struct(t) || is_type_raw_union(t)) {
+					Scope *scope = scope_of_node(&c->info, t->Record.node);
+					for_array(i, scope->elements.entries) {
+						Entity *f = scope->elements.entries[i].value;
+						if (f->kind == Entity_Variable) {
+							Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
+							uvar->Variable.is_immutable = is_immutable;
+							Entity *prev = scope_insert_entity(c->context.scope, uvar);
+							if (prev != nullptr) {
+								error(token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
+								return;
 							}
 							}
 						}
 						}
-					} else {
-						// NOTE(bill): skip the rest to remove extra errors
-						error(token, "`using` can only be applied to variables of type struct or raw_union");
-						return;
 					}
 					}
+				} else {
+					// NOTE(bill): skip the rest to remove extra errors
+					error(token, "`using` can only be applied to variables of type struct or raw_union");
+					return;
 				}
 				}
 			}
 			}
 		}
 		}

+ 3 - 0
src/checker.cpp

@@ -301,6 +301,7 @@ struct CheckerInfo {
 	Map<ExprInfo>         untyped;         // Key: AstNode * | Expression -> ExprInfo
 	Map<ExprInfo>         untyped;         // Key: AstNode * | Expression -> ExprInfo
 	Map<Entity *>         implicits;       // Key: AstNode *
 	Map<Entity *>         implicits;       // Key: AstNode *
 	Map<Array<Entity *> > gen_procs;       // Key: AstNode * | Identifier -> Entity
 	Map<Array<Entity *> > gen_procs;       // Key: AstNode * | Identifier -> Entity
+	Map<Array<Entity *> > gen_types;       // Key: Type *
 	Map<DeclInfo *>       entities;        // Key: Entity *
 	Map<DeclInfo *>       entities;        // Key: Entity *
 	Map<Entity *>         foreigns;        // Key: String
 	Map<Entity *>         foreigns;        // Key: String
 	Map<AstFile *>        files;           // Key: String (full path)
 	Map<AstFile *>        files;           // Key: String (full path)
@@ -742,6 +743,7 @@ void init_checker_info(CheckerInfo *i) {
 	map_init(&i->foreigns,      a);
 	map_init(&i->foreigns,      a);
 	map_init(&i->implicits,     a);
 	map_init(&i->implicits,     a);
 	map_init(&i->gen_procs,     a);
 	map_init(&i->gen_procs,     a);
+	map_init(&i->gen_types,     a);
 	map_init(&i->type_info_map, a);
 	map_init(&i->type_info_map, a);
 	map_init(&i->files,         a);
 	map_init(&i->files,         a);
 	i->type_info_count = 0;
 	i->type_info_count = 0;
@@ -758,6 +760,7 @@ void destroy_checker_info(CheckerInfo *i) {
 	map_destroy(&i->foreigns);
 	map_destroy(&i->foreigns);
 	map_destroy(&i->implicits);
 	map_destroy(&i->implicits);
 	map_destroy(&i->gen_procs);
 	map_destroy(&i->gen_procs);
+	map_destroy(&i->gen_types);
 	map_destroy(&i->type_info_map);
 	map_destroy(&i->type_info_map);
 	map_destroy(&i->files);
 	map_destroy(&i->files);
 }
 }

+ 18 - 2
src/ir.cpp

@@ -3670,6 +3670,18 @@ void ir_pop_target_list(irProcedure *proc) {
 void ir_gen_global_type_name(irModule *m, Entity *e, String name) {
 void ir_gen_global_type_name(irModule *m, Entity *e, String name) {
 	if (e->type == nullptr) return;
 	if (e->type == nullptr) return;
 
 
+	if (is_type_polymorphic(base_type(e->type))) {
+		auto found = map_get(&m->info->gen_types, hash_pointer(e->type));
+		if (found == nullptr) {
+			return;
+		}
+		for_array(i, *found) {
+			Entity *e = (*found)[i];
+			ir_mangle_add_sub_type_name(m, e, name);
+		}
+		return;
+	}
+
 	irValue *t = ir_value_type_name(m->allocator, name, e->type);
 	irValue *t = ir_value_type_name(m->allocator, name, e->type);
 	ir_module_add_value(m, e, t);
 	ir_module_add_value(m, e, t);
 	map_set(&m->members, hash_string(name), t);
 	map_set(&m->members, hash_string(name), t);
@@ -5731,8 +5743,10 @@ void ir_build_constant_value_decl(irProcedure *proc, AstNodeValueDecl *vd) {
 		}
 		}
 
 
 		bool polymorphic = is_type_polymorphic(e->type);
 		bool polymorphic = is_type_polymorphic(e->type);
-
-		if (!polymorphic && map_get(&proc->module->min_dep_map, hash_pointer(e)) == nullptr) {
+		if (polymorphic && !is_type_struct(e->type)) {
+			continue;
+		}
+		if (map_get(&proc->module->min_dep_map, hash_pointer(e)) == nullptr) {
 			// NOTE(bill): Nothing depends upon it so doesn't need to be built
 			// NOTE(bill): Nothing depends upon it so doesn't need to be built
 			continue;
 			continue;
 		}
 		}
@@ -5741,6 +5755,7 @@ void ir_build_constant_value_decl(irProcedure *proc, AstNodeValueDecl *vd) {
 			// 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 = e->token.string;
+
 			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);
 			i32 guid = cast(i32)proc->module->members.entries.count;
 			i32 guid = cast(i32)proc->module->members.entries.count;
@@ -5751,6 +5766,7 @@ void ir_build_constant_value_decl(irProcedure *proc, AstNodeValueDecl *vd) {
 			                                           name, e->type);
 			                                           name, e->type);
 			map_set(&proc->module->entity_names, hash_entity(e), name);
 			map_set(&proc->module->entity_names, hash_entity(e), name);
 			ir_gen_global_type_name(proc->module, e, name);
 			ir_gen_global_type_name(proc->module, e, name);
+
 		} else if (e->kind == Entity_Procedure) {
 		} else if (e->kind == Entity_Procedure) {
 			CheckerInfo *info = proc->module->info;
 			CheckerInfo *info = proc->module->info;
 			DeclInfo *decl = decl_info_of_entity(info, e);
 			DeclInfo *decl = decl_info_of_entity(info, e);

+ 1 - 1
src/ir_print.cpp

@@ -340,7 +340,7 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
 	case Type_Named:
 	case Type_Named:
 		if (is_type_struct(t) || is_type_union(t)) {
 		if (is_type_struct(t) || is_type_union(t)) {
 			String *name = map_get(&m->entity_names, hash_pointer(t->Named.type_name));
 			String *name = map_get(&m->entity_names, hash_pointer(t->Named.type_name));
-			GB_ASSERT_MSG(name != nullptr, "%.*s", LIT(t->Named.name));
+			GB_ASSERT_MSG(name != nullptr, "%.*s %p", LIT(t->Named.name), t->Named.type_name);
 			ir_print_encoded_local(f, *name);
 			ir_print_encoded_local(f, *name);
 		} else {
 		} else {
 			ir_print_type(f, m, base_type(t));
 			ir_print_type(f, m, base_type(t));

+ 37 - 23
src/parser.cpp

@@ -111,6 +111,7 @@ enum StmtStateFlag {
 };
 };
 
 
 enum FieldFlag {
 enum FieldFlag {
+	FieldFlag_NONE      = 0,
 	FieldFlag_ellipsis  = 1<<0,
 	FieldFlag_ellipsis  = 1<<0,
 	FieldFlag_using     = 1<<1,
 	FieldFlag_using     = 1<<1,
 	FieldFlag_no_alias  = 1<<2,
 	FieldFlag_no_alias  = 1<<2,
@@ -418,12 +419,13 @@ AST_NODE_KIND(_TypeBegin, "", i32) \
 		AstNode *elem; \
 		AstNode *elem; \
 	}) \
 	}) \
 	AST_NODE_KIND(StructType, "struct type", struct { \
 	AST_NODE_KIND(StructType, "struct type", struct { \
-		Token token; \
-		Array<AstNode *> fields; \
-		isize field_count; \
-		bool is_packed; \
-		bool is_ordered; \
-		AstNode *align; \
+		Token            token;               \
+		Array<AstNode *> fields;              \
+		isize            field_count;         \
+		AstNode *        polymorphic_params;  \
+		bool             is_packed;           \
+		bool             is_ordered;          \
+		AstNode *        align;               \
 	}) \
 	}) \
 	AST_NODE_KIND(UnionType, "union type", struct { \
 	AST_NODE_KIND(UnionType, "union type", struct { \
 		Token            token;       \
 		Token            token;       \
@@ -866,6 +868,7 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) {
 		break;
 		break;
 	case AstNode_StructType:
 	case AstNode_StructType:
 		n->StructType.fields = clone_ast_node_array(a, n->StructType.fields);
 		n->StructType.fields = clone_ast_node_array(a, n->StructType.fields);
+		n->StructType.polymorphic_params = clone_ast_node(a, n->StructType.polymorphic_params);
 		n->StructType.align  = clone_ast_node(a, n->StructType.align);
 		n->StructType.align  = clone_ast_node(a, n->StructType.align);
 		break;
 		break;
 	case AstNode_UnionType:
 	case AstNode_UnionType:
@@ -1459,14 +1462,15 @@ AstNode *ast_vector_type(AstFile *f, Token token, AstNode *count, AstNode *elem)
 }
 }
 
 
 AstNode *ast_struct_type(AstFile *f, Token token, Array<AstNode *> fields, isize field_count,
 AstNode *ast_struct_type(AstFile *f, Token token, Array<AstNode *> fields, isize field_count,
-                         bool is_packed, bool is_ordered, AstNode *align) {
+                         AstNode *polymorphic_params, bool is_packed, bool is_ordered, AstNode *align) {
 	AstNode *result = make_ast_node(f, AstNode_StructType);
 	AstNode *result = make_ast_node(f, AstNode_StructType);
-	result->StructType.token       = token;
-	result->StructType.fields      = fields;
-	result->StructType.field_count = field_count;
-	result->StructType.is_packed   = is_packed;
-	result->StructType.is_ordered  = is_ordered;
-	result->StructType.align       = align;
+	result->StructType.token              = token;
+	result->StructType.fields             = fields;
+	result->StructType.field_count        = field_count;
+	result->StructType.polymorphic_params = polymorphic_params;
+	result->StructType.is_packed          = is_packed;
+	result->StructType.is_ordered         = is_ordered;
+	result->StructType.align              = align;
 	return result;
 	return result;
 }
 }
 
 
@@ -2182,6 +2186,8 @@ AstNode *        parse_simple_stmt      (AstFile *f, StmtAllowFlag flags);
 AstNode *        parse_type             (AstFile *f);
 AstNode *        parse_type             (AstFile *f);
 AstNode *        parse_call_expr        (AstFile *f, AstNode *operand);
 AstNode *        parse_call_expr        (AstFile *f, AstNode *operand);
 AstNode *        parse_record_field_list(AstFile *f, isize *name_count_);
 AstNode *        parse_record_field_list(AstFile *f, isize *name_count_);
+AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters, bool allow_type_token);
+
 
 
 AstNode *convert_stmt_to_expr(AstFile *f, AstNode *statement, String kind) {
 AstNode *convert_stmt_to_expr(AstFile *f, AstNode *statement, String kind) {
 	if (statement == nullptr) {
 	if (statement == nullptr) {
@@ -2205,7 +2211,7 @@ AstNode *convert_stmt_to_body(AstFile *f, AstNode *stmt) {
 		syntax_error(stmt, "Expected a normal statement rather than a block statement");
 		syntax_error(stmt, "Expected a normal statement rather than a block statement");
 		return stmt;
 		return stmt;
 	}
 	}
-	GB_ASSERT(is_ast_node_stmt(stmt));
+	GB_ASSERT(is_ast_node_stmt(stmt) || is_ast_node_decl(stmt));
 	Token open = ast_node_token(stmt);
 	Token open = ast_node_token(stmt);
 	Token close = ast_node_token(stmt);
 	Token close = ast_node_token(stmt);
 	Array<AstNode *> stmts = make_ast_node_array(f, 1);
 	Array<AstNode *> stmts = make_ast_node_array(f, 1);
@@ -2430,10 +2436,21 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
 
 
 	case Token_struct: {
 	case Token_struct: {
 		Token token = expect_token(f, Token_struct);
 		Token token = expect_token(f, Token_struct);
+		AstNode *polymorphic_params = nullptr;
 		bool is_packed = false;
 		bool is_packed = false;
 		bool is_ordered = false;
 		bool is_ordered = false;
 		AstNode *align = nullptr;
 		AstNode *align = nullptr;
 
 
+		if (allow_token(f, Token_OpenParen)) {
+			isize param_count = 0;
+			polymorphic_params = parse_field_list(f, &param_count, 0, Token_CloseParen, false, true);
+			if (param_count == 0) {
+				syntax_error(polymorphic_params, "Expected at least 1 polymorphic parametric");
+				polymorphic_params = nullptr;
+			}
+			expect_token_after(f, Token_CloseParen, "parameter list");
+		}
+
 		isize prev_level = f->expr_level;
 		isize prev_level = f->expr_level;
 		f->expr_level = -1;
 		f->expr_level = -1;
 
 
@@ -2477,7 +2494,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
 			decls = fields->FieldList.list;
 			decls = fields->FieldList.list;
 		}
 		}
 
 
-		return ast_struct_type(f, token, decls, name_count, is_packed, is_ordered, align);
+		return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_ordered, align);
 	} break;
 	} break;
 
 
 	case Token_union: {
 	case Token_union: {
@@ -3394,7 +3411,6 @@ AstNode *parse_block_stmt(AstFile *f, b32 is_when) {
 	return parse_body(f);
 	return parse_body(f);
 }
 }
 
 
-AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters);
 
 
 
 
 AstNode *parse_results(AstFile *f) {
 AstNode *parse_results(AstFile *f) {
@@ -3414,7 +3430,7 @@ AstNode *parse_results(AstFile *f) {
 
 
 	AstNode *list = nullptr;
 	AstNode *list = nullptr;
 	expect_token(f, Token_OpenParen);
 	expect_token(f, Token_OpenParen);
-	list = parse_field_list(f, nullptr, 0, Token_CloseParen, true);
+	list = parse_field_list(f, nullptr, 0, Token_CloseParen, true, false);
 	expect_token_after(f, Token_CloseParen, "parameter list");
 	expect_token_after(f, Token_CloseParen, "parameter list");
 	return list;
 	return list;
 }
 }
@@ -3424,7 +3440,7 @@ AstNode *parse_proc_type(AstFile *f, Token proc_token, String *link_name_) {
 	AstNode *results = nullptr;
 	AstNode *results = nullptr;
 
 
 	expect_token(f, Token_OpenParen);
 	expect_token(f, Token_OpenParen);
-	params = parse_field_list(f, nullptr, FieldFlag_Signature, Token_CloseParen, true);
+	params = parse_field_list(f, nullptr, FieldFlag_Signature, Token_CloseParen, true, true);
 	expect_token_after(f, Token_CloseParen, "parameter list");
 	expect_token_after(f, Token_CloseParen, "parameter list");
 	results = parse_results(f);
 	results = parse_results(f);
 
 
@@ -3669,7 +3685,7 @@ AstNode *parse_record_field_list(AstFile *f, isize *name_count_) {
 	return ast_field_list(f, start_token, decls);
 	return ast_field_list(f, start_token, decls);
 }
 }
 
 
-AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters) {
+AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters, bool allow_type_token) {
 	TokenKind separator = Token_Comma;
 	TokenKind separator = Token_Comma;
 	Token start_token = f->curr_token;
 	Token start_token = f->curr_token;
 
 
@@ -3682,7 +3698,6 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
 
 
 	isize total_name_count = 0;
 	isize total_name_count = 0;
 	bool allow_ellipsis = allowed_flags&FieldFlag_ellipsis;
 	bool allow_ellipsis = allowed_flags&FieldFlag_ellipsis;
-	bool allow_type_token = allow_default_parameters;
 
 
 	while (f->curr_token.kind != follow &&
 	while (f->curr_token.kind != follow &&
 	       f->curr_token.kind != Token_Colon &&
 	       f->curr_token.kind != Token_Colon &&
@@ -3691,10 +3706,9 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
 		AstNode *param = parse_var_type(f, allow_ellipsis, allow_type_token);
 		AstNode *param = parse_var_type(f, allow_ellipsis, allow_type_token);
 		AstNodeAndFlags naf = {param, flags};
 		AstNodeAndFlags naf = {param, flags};
 		array_add(&list, naf);
 		array_add(&list, naf);
-		if (f->curr_token.kind != Token_Comma) {
+		if (!allow_token(f, Token_Comma)) {
 			break;
 			break;
 		}
 		}
-		advance_token(f);
 	}
 	}
 
 
 	if (f->curr_token.kind == Token_Colon) {
 	if (f->curr_token.kind == Token_Colon) {
@@ -3750,7 +3764,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
 			AstNode *default_value = nullptr;
 			AstNode *default_value = nullptr;
 			expect_token_after(f, Token_Colon, "field list");
 			expect_token_after(f, Token_Colon, "field list");
 			if (f->curr_token.kind != Token_Eq) {
 			if (f->curr_token.kind != Token_Eq) {
-				type = parse_var_type(f, allow_ellipsis, allow_default_parameters);
+				type = parse_var_type(f, allow_ellipsis, allow_type_token);
 			}
 			}
 			if (allow_token(f, Token_Eq)) {
 			if (allow_token(f, Token_Eq)) {
 				// TODO(bill): Should this be true==lhs or false==rhs?
 				// TODO(bill): Should this be true==lhs or false==rhs?

+ 15 - 1
src/types.cpp

@@ -94,6 +94,8 @@ struct TypeRecord {
 	bool     are_offsets_being_processed;
 	bool     are_offsets_being_processed;
 	bool     is_packed;
 	bool     is_packed;
 	bool     is_ordered;
 	bool     is_ordered;
+	bool     is_polymorphic;
+	Type *   polymorphic_params; // Type_Tuple
 
 
 	i64      custom_align; // NOTE(bill): Only used in structs at the moment
 	i64      custom_align; // NOTE(bill): Only used in structs at the moment
 	Entity * names;
 	Entity * names;
@@ -937,6 +939,15 @@ bool is_type_indexable(Type *t) {
 	return is_type_array(t) || is_type_slice(t) || is_type_vector(t) || is_type_string(t);
 	return is_type_array(t) || is_type_slice(t) || is_type_vector(t) || is_type_string(t);
 }
 }
 
 
+bool is_type_polymorphic_struct(Type *t) {
+	t = base_type(t);
+	if (t->kind == Type_Record &&
+	    t->Record.kind == TypeRecord_Struct) {
+		return t->Record.is_polymorphic;
+	}
+	return false;
+}
+
 bool is_type_polymorphic(Type *t) {
 bool is_type_polymorphic(Type *t) {
 	switch (t->kind) {
 	switch (t->kind) {
 	case Type_Generic:
 	case Type_Generic:
@@ -967,7 +978,7 @@ bool is_type_polymorphic(Type *t) {
 		if (t->Proc.is_polymorphic) {
 		if (t->Proc.is_polymorphic) {
 			return true;
 			return true;
 		}
 		}
-		#if 0
+		#if 1
 		if (t->Proc.param_count > 0 &&
 		if (t->Proc.param_count > 0 &&
 		    is_type_polymorphic(t->Proc.params)) {
 		    is_type_polymorphic(t->Proc.params)) {
 			return true;
 			return true;
@@ -995,6 +1006,9 @@ bool is_type_polymorphic(Type *t) {
 		}
 		}
 		break;
 		break;
 	case Type_Record:
 	case Type_Record:
+		if (t->Record.is_polymorphic) {
+			return true;
+		}
 		for (isize i = 0; i < t->Record.field_count; i++) {
 		for (isize i = 0; i < t->Record.field_count; i++) {
 		    if (is_type_polymorphic(t->Record.fields[i]->type)) {
 		    if (is_type_polymorphic(t->Record.fields[i]->type)) {
 		    	return true;
 		    	return true;