Browse Source

#const value procedure parameters; $N for polymorphic array lengths

gingerBill 7 years ago
parent
commit
66ee2cb6ed
9 changed files with 298 additions and 130 deletions
  1. 38 38
      core/math.odin
  2. 77 18
      src/check_expr.cpp
  3. 93 32
      src/check_type.cpp
  4. 9 9
      src/checker.cpp
  5. 11 0
      src/entity.cpp
  6. 13 13
      src/ir.cpp
  7. 22 6
      src/parser.cpp
  8. 5 6
      src/tokenizer.cpp
  9. 30 8
      src/types.cpp

+ 38 - 38
core/math.odin

@@ -16,9 +16,9 @@ EPSILON      :: 1.19209290e-7;
 τ :: TAU;
 π :: PI;
 
-Vec2 :: [vector 2]f32;
-Vec3 :: [vector 3]f32;
-Vec4 :: [vector 4]f32;
+Vec2 :: [2]f32;
+Vec3 :: [3]f32;
+Vec4 :: [4]f32;
 
 // Column major
 Mat2 :: [2][2]f32;
@@ -122,38 +122,38 @@ to_degrees :: proc(radians: f32) -> f32 do return radians * 360 / TAU;
 
 
 
-dot :: proc(a, b: $T/[vector 2]$E) -> E { c := a*b; return c.x + c.y; }
-dot :: proc(a, b: $T/[vector 3]$E) -> E { c := a*b; return c.x + c.y + c.z; }
-dot :: proc(a, b: $T/[vector 4]$E) -> E { c := a*b; return c.x + c.y + c.z + c.w; }
+dot :: proc(a, b: $T/[2]$E) -> E { c := a*b; return c[0] + c[1]; }
+dot :: proc(a, b: $T/[3]$E) -> E { c := a*b; return c[0] + c[1] + c[2]; }
+dot :: proc(a, b: $T/[4]$E) -> E { c := a*b; return c[0] + c[1] + c[2] + c[3]; }
 
-cross :: proc(x, y: $T/[vector 3]$E) -> T {
+cross :: proc(x, y: $T/[3]$E) -> T {
 	a := swizzle(x, 1, 2, 0) * swizzle(y, 2, 0, 1);
 	b := swizzle(x, 2, 0, 1) * swizzle(y, 1, 2, 0);
 	return T(a - b);
 }
 
 
-mag :: proc(v: $T/[vector 2]$E) -> E do return sqrt(dot(v, v));
-mag :: proc(v: $T/[vector 3]$E) -> E do return sqrt(dot(v, v));
-mag :: proc(v: $T/[vector 4]$E) -> E do return sqrt(dot(v, v));
+mag :: proc(v: $T/[2]$E) -> E do return sqrt(dot(v, v));
+mag :: proc(v: $T/[3]$E) -> E do return sqrt(dot(v, v));
+mag :: proc(v: $T/[4]$E) -> E do return sqrt(dot(v, v));
 
-norm :: proc(v: $T/[vector 2]$E) -> T do return v / mag(v);
-norm :: proc(v: $T/[vector 3]$E) -> T do return v / mag(v);
-norm :: proc(v: $T/[vector 4]$E) -> T do return v / mag(v);
+norm :: proc(v: $T/[2]$E) -> T do return v / mag(v);
+norm :: proc(v: $T/[3]$E) -> T do return v / mag(v);
+norm :: proc(v: $T/[4]$E) -> T do return v / mag(v);
 
-norm0 :: proc(v: $T/[vector 2]$E) -> T {
+norm0 :: proc(v: $T/[2]$E) -> T {
 	m := mag(v);
 	if m == 0 do return 0;
 	return v/m;
 }
 
-norm0 :: proc(v: $T/[vector 3]$E) -> T {
+norm0 :: proc(v: $T/[3]$E) -> T {
 	m := mag(v);
 	if m == 0 do return 0;
 	return v/m;
 }
 
-norm0 :: proc(v: $T/[vector 4]$E) -> T {
+norm0 :: proc(v: $T/[4]$E) -> T {
 	m := mag(v);
 	if m == 0 do return 0;
 	return v/m;
@@ -194,10 +194,10 @@ mul :: proc(a, b: Mat4) -> Mat4 {
 
 mul :: proc(m: Mat4, v: Vec4) -> Vec4 {
 	return Vec4{
-		m[0][0]*v.x + m[1][0]*v.y + m[2][0]*v.z + m[3][0]*v.w,
-		m[0][1]*v.x + m[1][1]*v.y + m[2][1]*v.z + m[3][1]*v.w,
-		m[0][2]*v.x + m[1][2]*v.y + m[2][2]*v.z + m[3][2]*v.w,
-		m[0][3]*v.x + m[1][3]*v.y + m[2][3]*v.z + m[3][3]*v.w,
+		m[0][0]*v[0] + m[1][0]*v[1] + m[2][0]*v[2] + m[3][0]*v[3],
+		m[0][1]*v[0] + m[1][1]*v[1] + m[2][1]*v[2] + m[3][1]*v[3],
+		m[0][2]*v[0] + m[1][2]*v[1] + m[2][2]*v[2] + m[3][2]*v[3],
+		m[0][3]*v[0] + m[1][3]*v[1] + m[2][3]*v[2] + m[3][3]*v[3],
 	};
 }
 
@@ -273,9 +273,9 @@ inverse :: proc(m: Mat4) -> Mat4 {
 
 mat4_translate :: proc(v: Vec3) -> Mat4 {
 	m := mat4_identity();
-	m[3][0] = v.x;
-	m[3][1] = v.y;
-	m[3][2] = v.z;
+	m[3][0] = v[0];
+	m[3][1] = v[1];
+	m[3][2] = v[2];
 	m[3][3] = 1;
 	return m;
 }
@@ -289,28 +289,28 @@ mat4_rotate :: proc(v: Vec3, angle_radians: f32) -> Mat4 {
 
 	rot := mat4_identity();
 
-	rot[0][0] = c + t.x*a.x;
-	rot[0][1] = 0 + t.x*a.y + s*a.z;
-	rot[0][2] = 0 + t.x*a.z - s*a.y;
+	rot[0][0] = c + t[0]*a[0];
+	rot[0][1] = 0 + t[0]*a[1] + s*a[2];
+	rot[0][2] = 0 + t[0]*a[2] - s*a[1];
 	rot[0][3] = 0;
 
-	rot[1][0] = 0 + t.y*a.x - s*a.z;
-	rot[1][1] = c + t.y*a.y;
-	rot[1][2] = 0 + t.y*a.z + s*a.x;
+	rot[1][0] = 0 + t[1]*a[0] - s*a[2];
+	rot[1][1] = c + t[1]*a[1];
+	rot[1][2] = 0 + t[1]*a[2] + s*a[0];
 	rot[1][3] = 0;
 
-	rot[2][0] = 0 + t.z*a.x + s*a.y;
-	rot[2][1] = 0 + t.z*a.y - s*a.x;
-	rot[2][2] = c + t.z*a.z;
+	rot[2][0] = 0 + t[2]*a[0] + s*a[1];
+	rot[2][1] = 0 + t[2]*a[1] - s*a[0];
+	rot[2][2] = c + t[2]*a[2];
 	rot[2][3] = 0;
 
 	return rot;
 }
 
 scale :: proc(m: Mat4, v: Vec3) -> Mat4 {
-	m[0][0] *= v.x;
-	m[1][1] *= v.y;
-	m[2][2] *= v.z;
+	m[0][0] *= v[0];
+	m[1][1] *= v[1];
+	m[2][2] *= v[2];
 	return m;
 }
 
@@ -328,9 +328,9 @@ look_at :: proc(eye, centre, up: Vec3) -> Mat4 {
 	u := cross(s, f);
 
 	return Mat4{
-		{+s.x, +u.x, -f.x, 0},
-		{+s.y, +u.y, -f.y, 0},
-		{+s.z, +u.z, -f.z, 0},
+		{+s[0], +u[0], -f[0], 0},
+		{+s[1], +u[1], -f[1], 0},
+		{+s[2], +u[2], -f[2], 0},
 		{-dot(s, eye), -dot(u, eye), dot(f, eye), 1},
 	};
 }

+ 77 - 18
src/check_expr.cpp

@@ -755,8 +755,6 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
 	}
 }
 
-
-
 bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool compound, bool modify_type) {
 	Operand o = {Addressing_Value};
 	o.type = source;
@@ -792,9 +790,73 @@ bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool c
 		}
 		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, modify_type);
+		if (source->kind == Type_Array) {
+			if (poly->Array.generic_type && modify_type) {
+				Type *gt = poly->Array.generic_type;
+				GB_ASSERT(gt->kind == Type_Generic);
+				Entity *e = scope_lookup_entity(gt->Generic.scope, gt->Generic.name);
+				GB_ASSERT(e != nullptr);
+				if (e->kind == Entity_TypeName) {
+					poly->Array.generic_type = nullptr;
+					poly->Array.count = source->Array.count;
+
+					e->kind = Entity_Constant;
+					e->Constant.value = exact_value_i64(source->Array.count);
+					e->type = t_untyped_integer;
+				} else if (e->kind == Entity_Constant) {
+					poly->Array.generic_type = nullptr;
+					if (e->Constant.value.kind != ExactValue_Integer) {
+						return false;
+					}
+					i64 count = i128_to_i64(e->Constant.value.value_integer);
+					if (count != source->Array.count) {
+						return false;
+					}
+					poly->Array.count = source->Array.count;
+				} else {
+					return false;
+				}
+
+				return is_polymorphic_type_assignable(c, poly->Array.elem, source->Array.elem, true, modify_type);
+			}
+			if (poly->Array.count == source->Array.count) {
+				return is_polymorphic_type_assignable(c, poly->Array.elem, source->Array.elem, true, modify_type);
+			}
+		}
+		return false;
+	case Type_Vector:
+		if (source->kind == Type_Vector) {
+			if (poly->Vector.generic_type && modify_type) {
+				Type *gt = poly->Vector.generic_type;
+				GB_ASSERT(gt->kind == Type_Generic);
+				Entity *e = scope_lookup_entity(gt->Generic.scope, gt->Generic.name);
+				GB_ASSERT(e != nullptr);
+				if (e->kind == Entity_TypeName) {
+					poly->Vector.generic_type = nullptr;
+					poly->Vector.count = source->Vector.count;
+
+					e->kind = Entity_Constant;
+					e->Constant.value = exact_value_i64(source->Vector.count);
+					e->type = t_untyped_integer;
+				} else if (e->kind == Entity_Constant) {
+					poly->Vector.generic_type = nullptr;
+					if (e->Constant.value.kind != ExactValue_Integer) {
+						return false;
+					}
+					i64 count = i128_to_i64(e->Constant.value.value_integer);
+					if (count != source->Vector.count) {
+						return false;
+					}
+					poly->Vector.count = source->Vector.count;
+				} else {
+					return false;
+				}
+
+				return is_polymorphic_type_assignable(c, poly->Vector.elem, source->Vector.elem, true, modify_type);
+			}
+			if (poly->Vector.count == source->Vector.count) {
+				return is_polymorphic_type_assignable(c, poly->Vector.elem, source->Vector.elem, true, modify_type);
+			}
 		}
 		return false;
 	case Type_DynamicArray:
@@ -802,12 +864,6 @@ bool is_polymorphic_type_assignable(Checker *c, Type *poly, Type *source, bool c
 			return is_polymorphic_type_assignable(c, poly->DynamicArray.elem, source->DynamicArray.elem, true, modify_type);
 		}
 		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, modify_type);
-		}
-		return false;
 	case Type_Slice:
 		if (source->kind == Type_Slice) {
 			return is_polymorphic_type_assignable(c, poly->Slice.elem, source->Slice.elem, true, modify_type);
@@ -4109,12 +4165,13 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 				break;
 			}
 
-			GB_ASSERT(e->kind == Entity_Variable);
-			if (e->Variable.default_value.kind != ExactValue_Invalid ||
-			    e->Variable.default_is_nil ||
-			    e->Variable.default_is_location) {
-				param_count_excluding_defaults--;
-				continue;
+			if (e->kind == Entity_Variable) {
+				if (e->Variable.default_value.kind != ExactValue_Invalid ||
+				    e->Variable.default_is_nil ||
+				    e->Variable.default_is_location) {
+					param_count_excluding_defaults--;
+					continue;
+				}
 			}
 			break;
 		}
@@ -4374,6 +4431,8 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
 				if (e->kind == Entity_TypeName) {
 					error(call, "Type parameter `%.*s` is missing in procedure call",
 					      LIT(e->token.string));
+				} else if (e->kind == Entity_Constant && e->Constant.value.kind != ExactValue_Invalid) {
+					// Ignore
 				} else {
 					gbString str = type_to_string(e->type);
 					error(call, "Parameter `%.*s` of type `%s` is missing in procedure call",
@@ -5339,7 +5398,7 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t
 		if (cl->type != nullptr) {
 			type = nullptr;
 
-			// [..]Type
+			// [...]Type
 			if (cl->type->kind == AstNode_ArrayType && cl->type->ArrayType.count != nullptr) {
 				AstNode *count = cl->type->ArrayType.count;
 				if (count->kind == AstNode_UnaryExpr &&

+ 93 - 32
src/check_type.cpp

@@ -686,7 +686,7 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node, Array<Opera
 							specialization = nullptr;
 						}
 					}
-					type = make_type_generic(c->allocator, 0, str_lit(""), specialization);
+					type = make_type_generic(c->allocator, c->context.scope, 0, str_lit(""), specialization);
 				} else {
 					type = check_type(c, type_expr);
 					if (is_type_polymorphic(type)) {
@@ -1270,6 +1270,9 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
 		bool detemine_type_from_operand = false;
 		Type *specialization = nullptr;
 
+		bool is_using = (p->flags&FieldFlag_using) != 0;
+		bool is_constant_value = (p->flags&FieldFlag_const) != 0;
+
 
 		if (type_expr == nullptr) {
 			if (default_value->kind == AstNode_BasicDirective &&
@@ -1338,7 +1341,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
 					detemine_type_from_operand = true;
 					type = t_invalid;
 				} else {
-					type = make_type_generic(c->allocator, 0, str_lit(""), specialization);
+					type = make_type_generic(c->allocator, c->context.scope, 0, str_lit(""), specialization);
 				}
 			} else {
 				bool prev = c->context.allow_polymorphic_types;
@@ -1357,6 +1360,8 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
 			if (default_value != nullptr) {
 				if (type_expr->kind == AstNode_TypeType) {
 					error(default_value, "A type parameter may not have a default value");
+				} else if (is_constant_value) {
+					error(default_value, "A constant parameter may not have a default value");
 				} else {
 					Operand o = {};
 					if (default_value->kind == AstNode_BasicDirective &&
@@ -1431,6 +1436,16 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
 			}
 		}
 
+		if (is_constant_value) {
+			if (is_type_param) {
+				error(p->type, "`$` is not needed for a `type` parameter");
+			}
+			if (p->flags&FieldFlag_no_alias) {
+				error(p->type, "`#no_alias` can only be applied to variable fields of pointer type");
+				p->flags &= ~FieldFlag_no_alias; // Remove the flag
+			}
+
+		}
 
 		for_array(j, p->names) {
 			AstNode *name = p->names[j];
@@ -1475,16 +1490,16 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
 				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 &&
-				    operands->count > variables.count) {
-					Operand op = (*operands)[variables.count];
-					type = determine_type_from_polymorphic(c, type, op);
-					if (type == t_invalid) {
-						success = false;
-					} else if (!c->context.no_polymorphic_errors) {
-						// NOTE(bill): The type should be determined now and thus, no need to determine the type any more
-						is_type_polymorphic_type = false;
-						// is_type_polymorphic_type = is_type_polymorphic(base_type(type));
+				if (operands != nullptr && variables.count < operands->count) {
+					if (is_type_polymorphic_type) {
+						Operand op = (*operands)[variables.count];
+						type = determine_type_from_polymorphic(c, type, op);
+						if (type == t_invalid) {
+							success = false;
+						} else if (!c->context.no_polymorphic_errors) {
+							// NOTE(bill): The type should be determined now and thus, no need to determine the type any more
+							is_type_polymorphic_type = false;
+						}
 					}
 				}
 
@@ -1495,11 +1510,33 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
 					}
 				}
 
-				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 (is_constant_value) {
+					if (!is_type_constant_type(type)) {
+						gbString str = type_to_string(type);
+						error(params[i], "Invalid constant type, %s", str);
+						gb_string_free(str);
+					}
+
+					bool poly_const = true;
+					if (operands != nullptr) {
+						poly_const = false;
+						if (variables.count < operands->count) {
+							Operand op = (*operands)[variables.count];
+							if (op.mode != Addressing_Constant) {
+								error(op.expr, "Expected a constant parameter value");
+							} else {
+								value = op.value;
+							}
+						}
+					}
+
+					param = make_entity_const_param(c->allocator, scope, name->Ident.token, type, value, poly_const);
+				} else {
+					param = make_entity_param(c->allocator, scope, name->Ident.token, type, is_using, 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) {
@@ -1952,27 +1989,36 @@ bool check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node, Array
 }
 
 
-i64 check_array_count(Checker *c, AstNode *e) {
+i64 check_array_count(Checker *c, Operand *o, AstNode *e) {
 	if (e == nullptr) {
 		return 0;
 	}
-	Operand o = {};
 	if (e->kind == AstNode_UnaryExpr &&
 	    e->UnaryExpr.op.kind == Token_Ellipsis) {
 		return -1;
 	}
 
-	check_expr(c, &o, e);
-	if (o.mode != Addressing_Constant) {
-		if (o.mode != Addressing_Invalid) {
+	check_expr_or_type(c, o, e);
+	if (o->mode == Addressing_Type && o->type->kind == Type_Generic) {
+		if (c->context.allow_polymorphic_types) {
+			if (o->type->Generic.specialized) {
+				o->type->Generic.specialized = nullptr;
+				error(o->expr, "Polymorphic array length cannot have a specialization");
+			}
+			return 0;
+		}
+	}
+	if (o->mode != Addressing_Constant) {
+		if (o->mode != Addressing_Invalid) {
+			o->mode = Addressing_Invalid;
 			error(e, "Array count must be a constant");
 		}
 		return 0;
 	}
-	Type *type = base_type(o.type);
+	Type *type = base_type(o->type);
 	if (is_type_untyped(type) || is_type_integer(type)) {
-		if (o.value.kind == ExactValue_Integer) {
-			i64 count = i128_to_i64(o.value.value_integer);
+		if (o->value.kind == ExactValue_Integer) {
+			i64 count = i128_to_i64(o->value.value_integer);
 			if (count >= 0) {
 				return count;
 			}
@@ -2162,7 +2208,7 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type)
 				specific = nullptr;
 			}
 		}
-		Type *t = make_type_generic(c->allocator, 0, token.string, specific);
+		Type *t = make_type_generic(c->allocator, c->context.scope, 0, token.string, specific);
 		if (c->context.allow_polymorphic_types) {
 			Scope *ps = c->context.polymorphic_scope;
 			Scope *s = c->context.scope;
@@ -2176,7 +2222,7 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type)
 			add_entity(c, ps, ident, e);
 			add_entity(c, s, ident, e);
 		} else {
-			error(ident, "Invalid use of a polymorphic type `$%.*s`", LIT(token.string));
+			error(ident, "Invalid use of a polymorphic parameter `$%.*s`", LIT(token.string));
 			*type = t_invalid;
 			return false;
 		}
@@ -2233,13 +2279,18 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type)
 
 	case_ast_node(at, ArrayType, e);
 		if (at->count != nullptr) {
-			Type *elem = check_type(c, at->elem, nullptr);
-			i64 count = check_array_count(c, at->count);
+			Operand o = {};
+			i64 count = check_array_count(c, &o, at->count);
+			Type *generic_type = nullptr;
+			if (o.mode == Addressing_Type && o.type->kind == Type_Generic) {
+				generic_type = o.type;
+			}
 			if (count < 0) {
 				error(at->count, "... can only be used in conjuction with compound literals");
 				count = 0;
 			}
-			*type = make_type_array(c->allocator, elem, count);
+			Type *elem = check_type(c, at->elem, nullptr);
+			*type = make_type_array(c->allocator, elem, count, generic_type);
 		} else {
 			Type *elem = check_type(c, at->elem);
 			*type = make_type_slice(c->allocator, elem);
@@ -2256,15 +2307,25 @@ bool check_type_internal(Checker *c, AstNode *e, Type **type, Type *named_type)
 
 
 	case_ast_node(vt, VectorType, e);
+
+		Operand o = {};
+		i64 count = check_array_count(c, &o, vt->count);
+		Type *generic_type = nullptr;
+		if (o.mode == Addressing_Type && o.type->kind == Type_Generic) {
+			generic_type = o.type;
+		}
+		if (count < 0) {
+			count = 0;
+		}
+
 		Type *elem = check_type(c, vt->elem);
 		Type *be = base_type(elem);
-		i64 count = check_array_count(c, vt->count);
 		if (is_type_vector(be) || (!is_type_boolean(be) && !is_type_numeric(be) && be->kind != Type_Generic)) {
 			gbString err_str = type_to_string(elem);
 			error(vt->elem, "Vector element type must be numerical or a boolean, got `%s`", err_str);
 			gb_string_free(err_str);
 		}
-		*type = make_type_vector(c->allocator, elem, count);
+		*type = make_type_vector(c->allocator, elem, count, generic_type);
 		return true;
 	case_end;
 

+ 9 - 9
src/checker.cpp

@@ -514,15 +514,15 @@ void    scope_lookup_parent_entity (Scope *s, String name, Scope **scope_, Entit
 Entity *scope_insert_entity        (Scope *s, Entity *entity);
 
 
-ExprInfo *check_get_expr_info(CheckerInfo *i, AstNode *expr);
-void check_set_expr_info(CheckerInfo *i, AstNode *expr, ExprInfo info);
-void check_remove_expr_info(CheckerInfo *i, AstNode *expr);
-void add_untyped(CheckerInfo *i, AstNode *expression, bool lhs, AddressingMode mode, Type *basic_type, ExactValue value);
-void add_type_and_value(CheckerInfo *i, AstNode *expression, AddressingMode mode, Type *type, ExactValue value);
-void add_entity_use(Checker *c, AstNode *identifier, Entity *entity);
-void add_implicit_entity(Checker *c, AstNode *node, Entity *e);
-void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclInfo *d);
-void add_implicit_entity(Checker *c, AstNode *node, Entity *e);
+ExprInfo *check_get_expr_info     (CheckerInfo *i, AstNode *expr);
+void      check_set_expr_info     (CheckerInfo *i, AstNode *expr, ExprInfo info);
+void      check_remove_expr_info  (CheckerInfo *i, AstNode *expr);
+void      add_untyped             (CheckerInfo *i, AstNode *expression, bool lhs, AddressingMode mode, Type *basic_type, ExactValue value);
+void      add_type_and_value      (CheckerInfo *i, AstNode *expression, AddressingMode mode, Type *type, ExactValue value);
+void      add_entity_use          (Checker *c, AstNode *identifier, Entity *entity);
+void      add_implicit_entity     (Checker *c, AstNode *node, Entity *e);
+void      add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclInfo *d);
+void      add_implicit_entity     (Checker *c, AstNode *node, Entity *e);
 
 void check_add_import_decl(Checker *c, AstNodeImportDecl *id);
 void check_add_export_decl(Checker *c, AstNodeExportDecl *ed);

+ 11 - 0
src/entity.cpp

@@ -43,6 +43,7 @@ enum EntityFlag {
 	EntityFlag_Value         = 1<<9,
 	EntityFlag_Sret          = 1<<10,
 	EntityFlag_BitFieldValue = 1<<11,
+	EntityFlag_PolyConst     = 1<<12,
 
 	EntityFlag_CVarArg       = 1<<20,
 };
@@ -207,6 +208,16 @@ Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type,
 	return entity;
 }
 
+
+Entity *make_entity_const_param(gbAllocator a, Scope *scope, Token token, Type *type, ExactValue value, bool poly_const) {
+	Entity *entity = make_entity_constant(a, scope, token, type, value);
+	entity->flags |= EntityFlag_Used;
+	if (poly_const) entity->flags |= EntityFlag_PolyConst;
+	entity->flags |= EntityFlag_Param;
+	return entity;
+}
+
+
 Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool is_using, i32 field_src_index) {
 	Entity *entity = make_entity_variable(a, scope, token, type, false);
 	entity->Variable.field_src_index = field_src_index;

+ 13 - 13
src/ir.cpp

@@ -5012,6 +5012,8 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 				Entity *e = pt->variables[i];
 				if (e->kind == Entity_TypeName) {
 					args[i] = ir_value_nil(proc->module->allocator, e->type);
+				} else if (e->kind == Entity_Constant) {
+					continue;
 				} else {
 					GB_ASSERT(e->kind == Entity_Variable);
 					if (args[i] == nullptr) {
@@ -5029,8 +5031,6 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 			return ir_emit_call(proc, value, args, param_count);
 		}
 
-		isize arg_index = 0;
-
 		isize arg_count = 0;
 		for_array(i, ce->args) {
 			AstNode *a = ce->args[i];
@@ -5042,8 +5042,8 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 			}
 		}
 
-
-		irValue **args = gb_alloc_array(proc->module->allocator, irValue *, gb_max(type->param_count, arg_count));
+		Array<irValue *> args = {};
+		array_init(&args, proc->module->allocator, gb_max(type->param_count, arg_count));
 		bool variadic = type->variadic;
 		bool vari_expand = ce->ellipsis.pos.line != 0;
 		bool is_c_vararg = type->c_vararg;
@@ -5052,7 +5052,7 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 			AstNode *arg = ce->args[i];
 			TypeAndValue arg_tv = type_and_value_of_expr(proc->module->info, arg);
 			if (arg_tv.mode == Addressing_Type) {
-				args[arg_index++] = ir_value_nil(proc->module->allocator, arg_tv.type);
+				array_add(&args, ir_value_nil(proc->module->allocator, arg_tv.type));
 			} else {
 				irValue *a = ir_build_expr(proc, arg);
 				Type *at = ir_type(a);
@@ -5060,10 +5060,10 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 					for_array(i, at->Tuple.variables) {
 						Entity *e = at->Tuple.variables[i];
 						irValue *v = ir_emit_struct_ev(proc, a, cast(i32)i);
-						args[arg_index++] = v;
+						array_add(&args, v);
 					}
 				} else {
-					args[arg_index++] = a;
+					array_add(&args, a);
 				}
 			}
 		}
@@ -5081,15 +5081,15 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 			if (variadic) {
 				end--;
 			}
-			while (arg_index < end) {
-				Entity *e = pt->variables[arg_index];
+			while (args.count < end) {
+				Entity *e = pt->variables[args.count];
 				GB_ASSERT(e->kind == Entity_Variable);
 				if (e->Variable.default_value.kind != ExactValue_Invalid) {
-					args[arg_index++] = ir_value_constant(proc->module->allocator, e->type, e->Variable.default_value);
+					array_add(&args, ir_value_constant(proc->module->allocator, e->type, e->Variable.default_value));
 				} else if (e->Variable.default_is_location) {
-					args[arg_index++] = ir_emit_source_code_location(proc, procedure, pos);
+					array_add(&args, ir_emit_source_code_location(proc, procedure, pos));
 				} else {
-					args[arg_index++] = ir_value_nil(proc->module->allocator, e->type);
+					array_add(&args, ir_value_nil(proc->module->allocator, e->type));
 				}
 			}
 		}
@@ -5172,7 +5172,7 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
 			args[arg_count-1] = ir_emit_load(proc, slice);
 		}
 
-		return ir_emit_call(proc, value, args, final_count);
+		return ir_emit_call(proc, value, args.data, final_count);
 	case_end;
 
 	case_ast_node(se, SliceExpr, expr);

+ 22 - 6
src/parser.cpp

@@ -126,8 +126,9 @@ enum FieldFlag {
 	FieldFlag_using     = 1<<1,
 	FieldFlag_no_alias  = 1<<2,
 	FieldFlag_c_vararg  = 1<<3,
+	FieldFlag_const     = 1<<4,
 
-	FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg,
+	FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_const,
 	FieldFlag_Struct    = FieldFlag_using,
 };
 
@@ -3314,6 +3315,10 @@ AstNode *parse_proc_type(AstFile *f, Token proc_token) {
 			is_generic = true;
 			break;
 		}
+		if (f->flags&FieldFlag_const) {
+			is_generic = true;
+			break;
+		}
 	}
 
 
@@ -3357,6 +3362,7 @@ enum FieldPrefixKind {
 	FieldPrefix_Using,
 	FieldPrefix_NoAlias,
 	FieldPrefix_CVarArg,
+	FieldPrefix_Const,
 };
 
 FieldPrefixKind is_token_field_prefix(AstFile *f) {
@@ -3367,15 +3373,17 @@ FieldPrefixKind is_token_field_prefix(AstFile *f) {
 	case Token_using:
 		return FieldPrefix_Using;
 
+
 	case Token_Hash: {
 		advance_token(f);
 		switch (f->curr_token.kind) {
 		case Token_Ident:
 			if (f->curr_token.string == "no_alias") {
 				return FieldPrefix_NoAlias;
-			}
-			if (f->curr_token.string == "c_vararg") {
+			} else if (f->curr_token.string == "c_vararg") {
 				return FieldPrefix_CVarArg;
+			} else if (f->curr_token.string == "const") {
+				return FieldPrefix_Const;
 			}
 			break;
 		}
@@ -3389,6 +3397,7 @@ u32 parse_field_prefixes(AstFile *f) {
 	i32 using_count    = 0;
 	i32 no_alias_count = 0;
 	i32 c_vararg_count = 0;
+	i32 const_count    = 0;
 
 	for (;;) {
 		FieldPrefixKind kind = is_token_field_prefix(f);
@@ -3396,20 +3405,23 @@ u32 parse_field_prefixes(AstFile *f) {
 			break;
 		}
 		switch (kind) {
-		case FieldPrefix_Using:     using_count    += 1; advance_token(f); break;
-		case FieldPrefix_NoAlias:   no_alias_count += 1; advance_token(f); break;
-		case FieldPrefix_CVarArg:   c_vararg_count += 1; advance_token(f); break;
+		case FieldPrefix_Using:   using_count    += 1; advance_token(f); break;
+		case FieldPrefix_NoAlias: no_alias_count += 1; advance_token(f); break;
+		case FieldPrefix_CVarArg: c_vararg_count += 1; advance_token(f); break;
+		case FieldPrefix_Const:   const_count += 1; advance_token(f); break;
 		}
 	}
 	if (using_count     > 1) syntax_error(f->curr_token, "Multiple `using` in this field list");
 	if (no_alias_count  > 1) syntax_error(f->curr_token, "Multiple `#no_alias` in this field list");
 	if (c_vararg_count  > 1) syntax_error(f->curr_token, "Multiple `#c_vararg` in this field list");
+	if (const_count     > 1) syntax_error(f->curr_token, "Multiple `$` in this field list");
 
 
 	u32 field_flags = 0;
 	if (using_count     > 0) field_flags |= FieldFlag_using;
 	if (no_alias_count  > 0) field_flags |= FieldFlag_no_alias;
 	if (c_vararg_count  > 0) field_flags |= FieldFlag_c_vararg;
+	if (const_count  > 0)    field_flags |= FieldFlag_const;
 	return field_flags;
 }
 
@@ -3431,6 +3443,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");
 		set_flags &= ~FieldFlag_c_vararg;
 	}
+	if ((allowed_flags&FieldFlag_const) == 0 && (set_flags&FieldFlag_const)) {
+		syntax_error(f->curr_token, "`$` is not allowed within this field list");
+		set_flags &= ~FieldFlag_const;
+	}
 	return set_flags;
 }
 

+ 5 - 6
src/tokenizer.cpp

@@ -155,14 +155,13 @@ TokenPos token_pos(String file, isize line, isize column) {
 }
 
 i32 token_pos_cmp(TokenPos const &a, TokenPos const &b) {
-	if (a.line == b.line) {
-		if (a.column == b.column) {
-			isize min_len = gb_min(a.file.len, b.file.len);
-			return gb_memcompare(a.file.text, b.file.text, min_len);
-		}
+	if (a.line != b.line) {
+		return (a.line < b.line) ? -1 : +1;
+	}
+	if (a.column != b.column) {
 		return (a.column < b.column) ? -1 : +1;
 	}
-	return (a.line < b.line) ? -1 : +1;
+	return string_compare(a.file, b.file);
 }
 
 bool operator==(TokenPos const &a, TokenPos const &b) { return token_pos_cmp(a, b) == 0; }

+ 30 - 8
src/types.cpp

@@ -98,11 +98,20 @@ struct TypeStruct {
 		i64    id;                                        \
 		String name;                                      \
 		Type * specialized;                               \
+		Scope *scope;                                     \
 	})                                                    \
 	TYPE_KIND(Pointer, struct { Type *elem; })            \
-	TYPE_KIND(Array,   struct { Type *elem; i64 count; }) \
+	TYPE_KIND(Array,   struct {                           \
+		Type *elem;                                       \
+		i64   count;                                      \
+		Type *generic_type;                               \
+	})                                                    \
 	TYPE_KIND(DynamicArray, struct { Type *elem; })       \
-	TYPE_KIND(Vector,  struct { Type *elem; i64 count; }) \
+	TYPE_KIND(Vector,  struct {                           \
+		Type *elem;                                       \
+		i64 count;                                        \
+		Type *generic_type;                               \
+	})                                                    \
 	TYPE_KIND(Slice,   struct { Type *elem; })            \
 	TYPE_KIND(Struct,  TypeStruct)                        \
 	TYPE_KIND(Enum, struct {                              \
@@ -466,11 +475,12 @@ Type *make_type_basic(gbAllocator a, BasicType basic) {
 	return t;
 }
 
-Type *make_type_generic(gbAllocator a, i64 id, String name, Type *specialized) {
+Type *make_type_generic(gbAllocator a, Scope *scope, i64 id, String name, Type *specialized) {
 	Type *t = alloc_type(a, Type_Generic);
 	t->Generic.id = id;
 	t->Generic.name = name;
 	t->Generic.specialized = specialized;
+	t->Generic.scope = scope;
 	return t;
 }
 
@@ -480,10 +490,11 @@ Type *make_type_pointer(gbAllocator a, Type *elem) {
 	return t;
 }
 
-Type *make_type_array(gbAllocator a, Type *elem, i64 count) {
+Type *make_type_array(gbAllocator a, Type *elem, i64 count, Type *generic_type = nullptr) {
 	Type *t = alloc_type(a, Type_Array);
 	t->Array.elem = elem;
 	t->Array.count = count;
+	t->Array.generic_type = generic_type;
 	return t;
 }
 
@@ -493,10 +504,11 @@ Type *make_type_dynamic_array(gbAllocator a, Type *elem) {
 	return t;
 }
 
-Type *make_type_vector(gbAllocator a, Type *elem, i64 count) {
+Type *make_type_vector(gbAllocator a, Type *elem, i64 count, Type *generic_type = nullptr) {
 	Type *t = alloc_type(a, Type_Vector);
 	t->Vector.elem = elem;
 	t->Vector.count = count;
+	t->Vector.generic_type = generic_type;
 	return t;
 }
 
@@ -951,11 +963,14 @@ bool is_type_polymorphic(Type *t) {
 	case Type_Pointer:
 		return is_type_polymorphic(t->Pointer.elem);
 	case Type_Array:
+		if (t->Array.generic_type != nullptr) {
+			return true;
+		}
 		return is_type_polymorphic(t->Array.elem);
-	case Type_DynamicArray:
-		return is_type_polymorphic(t->DynamicArray.elem);
 	case Type_Vector:
 		return is_type_polymorphic(t->Vector.elem);
+	case Type_DynamicArray:
+		return is_type_polymorphic(t->DynamicArray.elem);
 	case Type_Slice:
 		return is_type_polymorphic(t->Slice.elem);
 
@@ -2415,12 +2430,19 @@ gbString write_type_to_string(gbString str, Type *type) {
 
 	case Type_Tuple:
 		if (type->Tuple.variables.count > 0) {
+			isize comma_index = 0;
 			for_array(i, type->Tuple.variables) {
 				Entity *var = type->Tuple.variables[i];
 				if (var != nullptr) {
-					if (i > 0) {
+					if (var->kind == Entity_Constant) {
+						// Ignore
+						continue;
+					}
+
+					if (comma_index++ > 0) {
 						str = gb_string_appendc(str, ", ");
 					}
+
 					if (var->kind == Entity_Variable) {
 						if (var->flags&EntityFlag_CVarArg) {
 							str = gb_string_appendc(str, "#c_vararg ");