Browse Source

Typesafe variadic procedures

Ginger Bill 9 years ago
parent
commit
11205f968a
9 changed files with 293 additions and 108 deletions
  1. 19 8
      examples/demo.odin
  2. 2 0
      examples/win32.odin
  3. 2 0
      src/checker/checker.cpp
  4. 85 17
      src/checker/expr.cpp
  5. 1 1
      src/checker/stmt.cpp
  6. 20 5
      src/checker/type.cpp
  7. 1 1
      src/codegen/codegen.cpp
  8. 61 5
      src/codegen/ssa.cpp
  9. 102 71
      src/parser.cpp

+ 19 - 8
examples/demo.odin

@@ -3,17 +3,28 @@
 #load "game.odin"
 
 main :: proc() {
-	print_int(min(1, 2)); nl()
-	print_int(max(1, 2)); nl()
-	print_int(abs(-1337)); nl()
 
-	a, b, c := 1, 2, -1337
+	print_ints :: proc(args: ..int) {
+		for i := 0; i < len(args); i++ {
+			print_int(args[i])
+			nl()
+		}
+	}
+	// print_ints()
+	// print_ints(1)
+	print_ints(1, 2, 3, 4, 5)
+
+	// print_int(min(1, 2)); nl()
+	// print_int(max(1, 2)); nl()
+	// print_int(abs(-1337)); nl()
+
+	// a, b, c := 1, 2, -1337
 
-	print_int(min(a, b)); nl()
-	print_int(max(a, b)); nl()
-	print_int(abs(c) as int); nl()
+	// print_int(min(a, b)); nl()
+	// print_int(max(a, b)); nl()
+	// print_int(abs(c) as int); nl()
 
-	nl()
+	// nl()
 /*
 	Vec3   :: type struct { x, y, z: f32 }
 	Entity :: type struct {

+ 2 - 0
examples/win32.odin

@@ -110,6 +110,8 @@ GetQueryPerformanceFrequency :: proc() -> i64 {
 	return r
 }
 
+GetCommandLineA :: proc() -> ^u8 #foreign
+
 
 
 // File Stuff

+ 2 - 0
src/checker/checker.cpp

@@ -132,6 +132,7 @@ enum BuiltinProcId {
 	BuiltinProc_align_of_val,
 	BuiltinProc_offset_of,
 	BuiltinProc_offset_of_val,
+	BuiltinProc_type_of_val,
 	BuiltinProc_assert,
 
 	BuiltinProc_len,
@@ -170,6 +171,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
 	{STR_LIT("align_of_val"),     1, false, Expr_Expr},
 	{STR_LIT("offset_of"),        2, false, Expr_Expr},
 	{STR_LIT("offset_of_val"),    1, false, Expr_Expr},
+	{STR_LIT("type_of_val"),      1, false, Expr_Expr},
 	{STR_LIT("assert"),           1, false, Expr_Stmt},
 
 	{STR_LIT("len"),              1, false, Expr_Expr},

+ 85 - 17
src/checker/expr.cpp

@@ -459,10 +459,12 @@ void check_enum_type(Checker *c, Type *enum_type, AstNode *node) {
 	enum_type->Record.other_field_count = et->field_count;
 }
 
-Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize field_count) {
+Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize field_count, b32 *is_variadic_) {
 	if (field_list == NULL || field_count == 0)
 		return NULL;
 
+	b32 is_variadic = false;
+
 	Type *tuple = make_type_tuple(c->allocator);
 
 	Entity **variables = gb_alloc_array(c->allocator, Entity *, field_count);
@@ -471,6 +473,15 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize fiel
 		ast_node(f, Field, field);
 		AstNode *type_expr = f->type;
 		if (type_expr) {
+			if (type_expr->kind == AstNode_Ellipsis) {
+				type_expr = type_expr->Ellipsis.expr;
+				if (field->next == NULL) {
+					is_variadic = true;
+				} else {
+					error(&c->error_collector, ast_node_token(field), "Invalid AST: Invalid variadic parameter");
+				}
+			}
+
 			Type *type = check_type(c, type_expr);
 			for (AstNode *name = f->name_list; name != NULL; name = name->next) {
 				if (name->kind == AstNode_Ident) {
@@ -478,14 +489,24 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize fiel
 					add_entity(c, scope, name, param);
 					variables[variable_index++] = param;
 				} else {
-					error(&c->error_collector, ast_node_token(name), "Invalid parameter (invalid AST)");
+					error(&c->error_collector, ast_node_token(name), "Invalid AST: Invalid parameter");
 				}
 			}
 		}
 	}
+
+	if (is_variadic && field_count > 0) {
+		// NOTE(bill): Change last variadic parameter to be a slice
+		// Custom Calling convention for variadic parameters
+		Entity *end = variables[field_count-1];
+		end->type = make_type_slice(c->allocator, end->type);
+	}
+
 	tuple->Tuple.variables = variables;
 	tuple->Tuple.variable_count = field_count;
 
+	if (is_variadic_) *is_variadic_ = is_variadic;
+
 	return tuple;
 }
 
@@ -520,7 +541,8 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
 
 	// gb_printf("%td -> %td\n", param_count, result_count);
 
-	Type *params  = check_get_params(c, c->context.scope, pt->param_list,   param_count);
+	b32 variadic = false;
+	Type *params  = check_get_params(c, c->context.scope, pt->param_list,   param_count, &variadic);
 	Type *results = check_get_results(c, c->context.scope, pt->result_list, result_count);
 
 	type->Proc.scope        = c->context.scope;
@@ -528,6 +550,7 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
 	type->Proc.param_count  = pt->param_count;
 	type->Proc.results      = results;
 	type->Proc.result_count = pt->result_count;
+	type->Proc.variadic     = variadic;
 }
 
 
@@ -772,10 +795,19 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_c
 		goto end;
 	case_end;
 
-	default:
+	default: {
+		if (e->kind == AstNode_CallExpr) {
+			Operand o = {};
+			check_expr_or_type(c, &o, e);
+			if (o.mode == Addressing_Type) {
+				type = o.type;
+				goto end;
+			}
+		}
+
 		err_str = expr_to_string(e);
 		error(&c->error_collector, ast_node_token(e), "`%s` is not a type", err_str);
-		break;
+	} break;
 	}
 
 	type = t_invalid;
@@ -1894,7 +1926,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 
 	case BuiltinProc_size_of_val:
 		// size_of_val :: proc(val: Type) -> int
-		check_assignment(c, operand, NULL, make_string("argument of `size_of`"));
+		check_assignment(c, operand, NULL, make_string("argument of `size_of_val`"));
 		if (operand->mode == Addressing_Invalid)
 			return false;
 
@@ -1917,7 +1949,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 
 	case BuiltinProc_align_of_val:
 		// align_of_val :: proc(val: Type) -> int
-		check_assignment(c, operand, NULL, make_string("argument of `align_of`"));
+		check_assignment(c, operand, NULL, make_string("argument of `align_of_val`"));
 		if (operand->mode == Addressing_Invalid)
 			return false;
 
@@ -1996,6 +2028,14 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		operand->type  = t_int;
 	} break;
 
+	case BuiltinProc_type_of_val:
+		// type_of_val :: proc(val: Type) -> type(Type)
+		check_assignment(c, operand, NULL, make_string("argument of `type_of_val`"));
+		if (operand->mode == Addressing_Invalid)
+			return false;
+		operand->mode = Addressing_Type;
+		break;
+
 	case BuiltinProc_assert:
 		// assert :: proc(cond: bool)
 
@@ -2519,14 +2559,20 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
 	isize error_code = 0;
 	isize param_index = 0;
 	isize param_count = 0;
+	b32 variadic = proc_type->Proc.variadic;
 
-	if (proc_type->Proc.params)
+	if (proc_type->Proc.params) {
 		param_count = proc_type->Proc.params->Tuple.variable_count;
+	}
 
- 	if (ce->arg_list_count == 0 && param_count == 0)
-		return;
+	if (ce->arg_list_count == 0) {
+		if (variadic && param_count-1 == 0)
+			return;
+		if (param_count == 0)
+			return;
+	}
 
-	if (ce->arg_list_count > param_count) {
+	if (ce->arg_list_count > param_count && !variadic) {
 		error_code = +1;
 	} else {
 		Entity **sig_params = proc_type->Proc.params->Tuple.variables;
@@ -2537,19 +2583,40 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
 				continue;
 			if (operand->type->kind != Type_Tuple) {
 				check_not_tuple(c, operand);
-				check_assignment(c, operand, sig_params[param_index]->type, make_string("argument"), true);
+				isize index = param_index;
+				b32 end_variadic = false;
+				if (variadic && param_index >= param_count-1) {
+					index = param_count-1;
+					end_variadic = true;
+				}
+				Type *arg_type = sig_params[index]->type;
+				if (end_variadic && is_type_slice(arg_type)) {
+					arg_type = get_base_type(arg_type)->Slice.elem;
+				}
+				check_assignment(c, operand, arg_type, make_string("argument"), true);
 				param_index++;
 			} else {
 				auto *tuple = &operand->type->Tuple;
 				isize i = 0;
 				for (;
-				     i < tuple->variable_count && param_index < param_count;
-				     i++, param_index++) {
+				     i < tuple->variable_count && (param_index < param_count && !variadic);
+				     i++) {
 					Entity *e = tuple->variables[i];
 					operand->type = e->type;
 					operand->mode = Addressing_Value;
 					check_not_tuple(c, operand);
-					check_assignment(c, operand, sig_params[param_index]->type, make_string("argument"), true);
+					isize index = param_index;
+					b32 end_variadic = false;
+					if (variadic && param_index >= param_count-1) {
+						index = param_count-1;
+						end_variadic = true;
+					}
+					Type *arg_type = sig_params[index]->type;
+					if (end_variadic && is_type_slice(arg_type)) {
+						arg_type = get_base_type(arg_type)->Slice.elem;
+					}
+					check_assignment(c, operand, arg_type, make_string("argument"), true);
+					param_index++;
 				}
 
 				if (i < tuple->variable_count && param_index == param_count) {
@@ -2558,12 +2625,13 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
 				}
 			}
 
-			if (param_index >= param_count)
+			if (!variadic && param_index >= param_count)
 				break;
 		}
 
 
-		if (param_index < param_count) {
+		if ((!variadic && param_index < param_count) ||
+		    (variadic  && param_index < param_count-1)) {
 			error_code = -1;
 		} else if (call_arg != NULL && call_arg->next != NULL) {
 			error_code = +1;

+ 1 - 1
src/checker/stmt.cpp

@@ -421,7 +421,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
 void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later) {
 	GB_ASSERT(e->type == NULL);
 
-	Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0);
+	Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0, false);
 	e->type = proc_type;
 	ast_node(pd, ProcDecl, d->proc_decl);
 	check_open_scope(c, pd->type);

+ 20 - 5
src/checker/type.cpp

@@ -149,6 +149,7 @@ struct Type {
 			Type * results; // Type_Tuple
 			isize  param_count;
 			isize  result_count;
+			b32    variadic;
 		} Proc;
 	};
 };
@@ -240,13 +241,27 @@ Type *make_type_tuple(gbAllocator a) {
 	return t;
 }
 
-Type *make_type_proc(gbAllocator a, Scope *scope, Type *params, isize param_count, Type *results, isize result_count) {
+Type *make_type_proc(gbAllocator a, Scope *scope, Type *params, isize param_count, Type *results, isize result_count, b32 variadic) {
 	Type *t = alloc_type(a, Type_Proc);
-	t->Proc.scope = scope;
-	t->Proc.params = params;
-	t->Proc.param_count = param_count;
-	t->Proc.results = results;
+
+	if (variadic) {
+		if (param_count == 0) {
+			GB_PANIC("variadic procedure must have at least one parameter");
+		}
+		GB_ASSERT(params != NULL && params->kind == Type_Tuple);
+		Entity *e = params->Tuple.variables[param_count-1];
+		if (get_base_type(e->type)->kind != Type_Slice) {
+			// NOTE(bill): For custom calling convention
+			GB_PANIC("variadic parameter must be of type slice");
+		}
+	}
+
+	t->Proc.scope        = scope;
+	t->Proc.params       = params;
+	t->Proc.param_count  = param_count;
+	t->Proc.results      = results;
 	t->Proc.result_count = result_count;
+	t->Proc.variadic     = variadic;
 	return t;
 }
 

+ 1 - 1
src/codegen/codegen.cpp

@@ -131,7 +131,7 @@ void ssa_gen_code(ssaGen *s) {
 		String name = make_string(SSA_STARTUP_RUNTIME_PROC_NAME);
 		Type *proc_type = make_type_proc(a, gb_alloc_item(a, Scope),
 		                                 NULL, 0,
-		                                 NULL, 0);
+		                                 NULL, 0, false);
 		AstNode *body = gb_alloc_item(a, AstNode);
 		ssaValue *p = ssa_make_value_procedure(a, m, proc_type, NULL, body, name);
 		Token token = {};

+ 61 - 5
src/codegen/ssa.cpp

@@ -2159,10 +2159,23 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 		auto *type = &proc_type_->Proc;
 
 		isize arg_index = 0;
-		isize arg_count = type->param_count;
+
+		isize arg_count = 0;
+		for (AstNode *a = ce->arg_list; a != NULL; a = a->next) {
+			Type *at = get_base_type(type_of_expr(proc->module->info, a));
+			if (at->kind == Type_Tuple) {
+				arg_count += at->Tuple.variable_count;
+			} else {
+				arg_count++;
+			}
+		}
 		ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, arg_count);
+		b32 variadic = proc_type_->Proc.variadic;
 
-		for (AstNode *arg = ce->arg_list; arg != NULL; arg = arg->next) {
+		AstNode *arg = ce->arg_list;
+		for (;
+		     arg != NULL;
+		     arg = arg->next) {
 			ssaValue *a = ssa_build_expr(proc, arg);
 			Type *at = ssa_type(a);
 			if (at->kind == Type_Tuple) {
@@ -2176,11 +2189,54 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 			}
 		}
 
-		auto *pt = &proc_type_->Proc.params->Tuple;
-		for (isize i = 0; i < arg_count; i++) {
-			args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type, true);
+		auto *pt = &type->params->Tuple;
+
+		if (variadic) {
+			isize i = 0;
+			for (; i < type->param_count-1; i++) {
+				args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type, true);
+			}
+			Type *variadic_type = pt->variables[i]->type;
+			GB_ASSERT(is_type_slice(variadic_type));
+			variadic_type = get_base_type(variadic_type)->Slice.elem;
+			for (; i < arg_count; i++) {
+				args[i] = ssa_emit_conv(proc, args[i], variadic_type, true);
+			}
+		} else {
+			for (isize i = 0; i < arg_count; i++) {
+				args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type, true);
+			}
 		}
 
+		if (variadic) {
+			gbAllocator allocator = proc->module->allocator;
+			Type *slice_type = pt->variables[type->param_count-1]->type;
+			Type *elem_type  = get_base_type(slice_type)->Slice.elem;
+			Type *elem_ptr_type = make_type_pointer(allocator, elem_type);
+			ssaValue *slice = ssa_add_local_generated(proc, slice_type);
+			isize slice_len = arg_count+1 - type->param_count;
+
+			if (slice_len > 0) {
+				ssaValue *base_array = ssa_add_local_generated(proc, make_type_array(allocator, elem_type, slice_len));
+
+				for (isize i = type->param_count-1, j = 0; i < arg_count; i++, j++) {
+					ssaValue *addr = ssa_emit_struct_gep(proc, base_array, j, elem_type);
+					ssa_emit_store(proc, addr, args[i]);
+				}
+
+				ssaValue *base_elem = ssa_emit_struct_gep(proc, base_array, v_zero32, elem_type);
+				ssa_emit_store(proc, ssa_emit_struct_gep(proc, slice, v_zero32, elem_ptr_type), base_elem);
+				ssaValue *len = ssa_make_value_constant(allocator, t_int, make_exact_value_integer(slice_len));
+				ssa_emit_store(proc, ssa_emit_struct_gep(proc, slice, v_one32, t_int), len);
+				ssa_emit_store(proc, ssa_emit_struct_gep(proc, slice, v_two32, t_int), len);
+			}
+
+
+			arg_count = type->param_count;
+			args[arg_count-1] = ssa_emit_load(proc, slice);
+		}
+
+
 		return ssa_emit_call(proc, value, args, arg_count);
 	case_end;
 

+ 102 - 71
src/parser.cpp

@@ -77,6 +77,10 @@ enum CallExprKind {
 	AST_NODE_KIND(Invalid,  "invalid node",  struct{}) \
 	AST_NODE_KIND(BasicLit, "basic literal", Token) \
 	AST_NODE_KIND(Ident,    "identifier",    Token) \
+	AST_NODE_KIND(Ellipsis, "ellipsis", struct { \
+		Token token; \
+		AstNode *expr; \
+	}) \
 	AST_NODE_KIND(ProcLit, "procedure literal", struct { \
 		AstNode *type; \
 		AstNode *body; \
@@ -110,7 +114,6 @@ AST_NODE_KIND(_ExprBegin,  "",  struct{}) \
 		b32 triple_indexed; \
 	}) \
 	AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \
-	AST_NODE_KIND(Ellipsis,   "ellipsis",    struct { Token token; }) \
 AST_NODE_KIND(_ExprEnd,       "", struct{}) \
 AST_NODE_KIND(_StmtBegin,     "", struct{}) \
 	AST_NODE_KIND(BadStmt,    "bad statement",                 struct { Token begin, end; }) \
@@ -542,11 +545,6 @@ gb_inline AstNode *make_deref_expr(AstFile *f, AstNode *expr, Token op) {
 }
 
 
-gb_inline AstNode *make_ellipsis(AstFile *f, Token token) {
-	AstNode *result = make_node(f, AstNode_Ellipsis);
-	result->Ellipsis.token = token;
-	return result;
-}
 gb_inline AstNode *make_basic_lit(AstFile *f, Token basic_lit) {
 	AstNode *result = make_node(f, AstNode_BasicLit);
 	result->BasicLit = basic_lit;
@@ -559,6 +557,14 @@ gb_inline AstNode *make_ident(AstFile *f, Token token) {
 	return result;
 }
 
+gb_inline AstNode *make_ellipsis(AstFile *f, Token token, AstNode *expr) {
+	AstNode *result = make_node(f, AstNode_Ellipsis);
+	result->Ellipsis.token = token;
+	result->Ellipsis.expr = expr;
+	return result;
+}
+
+
 gb_inline AstNode *make_proc_lit(AstFile *f, AstNode *type, AstNode *body, u64 tags) {
 	AstNode *result = make_node(f, AstNode_ProcLit);
 	result->ProcLit.type = type;
@@ -1253,6 +1259,37 @@ b32 is_literal_type(AstNode *node) {
 	return false;
 }
 
+AstNode *parse_call_expr(AstFile *f, AstNode *operand) {
+	AstNode *arg_list = NULL;
+	AstNode *arg_list_curr = NULL;
+	isize arg_list_count = 0;
+	Token open_paren, close_paren;
+
+	f->expr_level++;
+	open_paren = expect_token(f, Token_OpenParen);
+
+	while (f->cursor[0].kind != Token_CloseParen &&
+	       f->cursor[0].kind != Token_EOF) {
+		if (f->cursor[0].kind == Token_Comma)
+			ast_file_err(f, f->cursor[0], "Expected an expression not a ,");
+
+		DLIST_APPEND(arg_list, arg_list_curr, parse_expr(f, false));
+		arg_list_count++;
+
+		if (f->cursor[0].kind != Token_Comma) {
+			if (f->cursor[0].kind == Token_CloseParen)
+				break;
+		}
+
+		next_token(f);
+	}
+
+	f->expr_level--;
+	close_paren = expect_token(f, Token_CloseParen);
+
+	return make_call_expr(f, operand, arg_list, arg_list_count, open_paren, close_paren);
+}
+
 AstNode *parse_atom_expr(AstFile *f, b32 lhs) {
 	AstNode *operand = parse_operand(f, lhs);
 
@@ -1273,34 +1310,7 @@ AstNode *parse_atom_expr(AstFile *f, b32 lhs) {
 			if (lhs) {
 				// TODO(bill): Handle this shit! Is this even allowed in this language?!
 			}
-			AstNode *arg_list = NULL;
-			AstNode *arg_list_curr = NULL;
-			isize arg_list_count = 0;
-			Token open_paren, close_paren;
-
-			f->expr_level++;
-			open_paren = expect_token(f, Token_OpenParen);
-
-			while (f->cursor[0].kind != Token_CloseParen &&
-			       f->cursor[0].kind != Token_EOF) {
-				if (f->cursor[0].kind == Token_Comma)
-					ast_file_err(f, f->cursor[0], "Expected an expression not a ,");
-
-				DLIST_APPEND(arg_list, arg_list_curr, parse_expr(f, false));
-				arg_list_count++;
-
-				if (f->cursor[0].kind != Token_Comma) {
-					if (f->cursor[0].kind == Token_CloseParen)
-						break;
-				}
-
-				next_token(f);
-			}
-
-			f->expr_level--;
-			close_paren = expect_token(f, Token_CloseParen);
-
-			operand = make_call_expr(f, operand, arg_list, arg_list_count, open_paren, close_paren);
+			operand = parse_call_expr(f, operand);
 		} break;
 
 		case Token_Period: {
@@ -1617,64 +1627,81 @@ AstNode *parse_type(AstFile *f) {
 	return type;
 }
 
-AstNode *parse_field_decl(AstFile *f, b32 allow_using) {
-	b32 is_using = false;
+
+Token parse_procedure_signature(AstFile *f,
+                                AstNode **param_list, isize *param_count,
+                                AstNode **result_list, isize *result_count);
+
+AstNode *parse_proc_type(AstFile *f) {
+	AstNode *params = NULL;
+	AstNode *results = NULL;
+	isize param_count = 0;
+	isize result_count = 0;
+
+	Token proc_token = parse_procedure_signature(f, &params, &param_count, &results, &result_count);
+
+	return make_proc_type(f, proc_token, params, param_count, results, result_count);
+}
+
+AstNode *parse_field_decl(AstFile *f) {
 	AstNode *name_list = NULL;
 	isize name_count = 0;
-	if (allow_using) {
-		if (allow_token(f, Token_using)) {
-			is_using = true;
-		}
+	b32 is_using = false;
+	if (allow_token(f, Token_using)) {
+		is_using = true;
 	}
 
 	name_list = parse_lhs_expr_list(f, &name_count);
-	if (name_count == 0)
+	if (name_count == 0) {
 		ast_file_err(f, f->cursor[0], "Empty field declaration");
+	}
 
 	if (name_count > 1 && is_using) {
 		ast_file_err(f, f->cursor[0], "Cannot apply `using` to more than one of the same type");
+		is_using = false;
 	}
 
 
 	expect_token(f, Token_Colon);
 
-	AstNode *type = parse_type_attempt(f);
-	if (type == NULL)
+	AstNode *type = NULL;
+	if (f->cursor[0].kind == Token_Ellipsis) {
+		Token ellipsis = f->cursor[0];
+		next_token(f);
+		type = parse_type_attempt(f);
+		if (type == NULL) {
+			ast_file_err(f, f->cursor[0], "variadic parameter is missing a type after `..`");
+			type = make_bad_expr(f, ellipsis, f->cursor[0]);
+		} else {
+			if (name_count > 1) {
+				ast_file_err(f, f->cursor[0], "mutliple variadic parameters, only 1 is allowed");
+				type = make_bad_expr(f, ellipsis, f->cursor[0]);
+			} else {
+				type = make_ellipsis(f, ellipsis, type);
+			}
+		}
+	} else {
+		type = parse_type_attempt(f);
+	}
+
+	if (type == NULL) {
 		ast_file_err(f, f->cursor[0], "Expected a type for this field declaration");
+	}
 
 	AstNode *field = make_field(f, name_list, name_count, type, is_using);
 	return field;
 }
 
-Token parse_procedure_signature(AstFile *f,
-                                AstNode **param_list, isize *param_count,
-                                AstNode **result_list, isize *result_count);
-
-AstNode *parse_proc_type(AstFile *f) {
-	AstNode *params = NULL;
-	AstNode *results = NULL;
-	isize param_count = 0;
-	isize result_count = 0;
-
-	Token proc_token = parse_procedure_signature(f, &params, &param_count, &results, &result_count);
-
-	return make_proc_type(f, proc_token, params, param_count, results, result_count);
-}
-
-
-AstNode *parse_parameter_list(AstFile *f, isize *param_count_, TokenKind separator, b32 allow_using) {
+AstNode *parse_parameter_list(AstFile *f, isize *param_count_) {
 	AstNode *param_list = NULL;
 	AstNode *param_list_curr = NULL;
 	isize param_count = 0;
 	while (f->cursor[0].kind == Token_Identifier ||
-	       (allow_using && f->cursor[0].kind == Token_using)) {
-		if (!allow_using && allow_token(f, Token_using)) {
-			ast_file_err(f, f->cursor[-1], "`using` is only allowed within structures (at the moment)");
-		}
-		AstNode *field = parse_field_decl(f, allow_using);
+	       f->cursor[0].kind == Token_using) {
+		AstNode *field = parse_field_decl(f);
 		DLIST_APPEND(param_list, param_list_curr, field);
 		param_count += field->Field.name_count;
-		if (f->cursor[0].kind != separator)
+		if (f->cursor[0].kind != Token_Comma)
 			break;
 		next_token(f);
 	}
@@ -1746,14 +1773,18 @@ AstNode *parse_struct_params(AstFile *f, isize *decl_count_) {
 AstNode *parse_identifier_or_type(AstFile *f) {
 	switch (f->cursor[0].kind) {
 	case Token_Identifier: {
-		AstNode *ident = parse_identifier(f);
+		AstNode *e = parse_identifier(f);
 		while (f->cursor[0].kind == Token_Period) {
 			Token token = f->cursor[0];
 			next_token(f);
 			AstNode *sel = parse_identifier(f);
-			ident = make_selector_expr(f, token, ident, sel);
+			e = make_selector_expr(f, token, e, sel);
+		}
+		if (f->cursor[0].kind == Token_OpenParen) {
+			// HACK NOTE(bill): For type_of_val(expr)
+			e = parse_call_expr(f, e);
 		}
-		return ident;
+		return e;
 	}
 
 	case Token_Pointer:
@@ -1765,7 +1796,7 @@ AstNode *parse_identifier_or_type(AstFile *f) {
 		AstNode *count_expr = NULL;
 
 		if (f->cursor[0].kind == Token_Ellipsis) {
-			count_expr = make_ellipsis(f, f->cursor[0]);
+			count_expr = make_ellipsis(f, f->cursor[0], NULL);
 			next_token(f);
 		} else if (f->cursor[0].kind != Token_CloseBracket) {
 			count_expr = parse_expr(f, false);
@@ -1922,7 +1953,7 @@ Token parse_procedure_signature(AstFile *f,
                                AstNode **result_list, isize *result_count) {
 	Token proc_token = expect_token(f, Token_proc);
 	expect_token(f, Token_OpenParen);
-	*param_list = parse_parameter_list(f, param_count, Token_Comma, true);
+	*param_list = parse_parameter_list(f, param_count);
 	expect_token(f, Token_CloseParen);
 	*result_list = parse_results(f, result_count);
 	return proc_token;