Browse Source

Remove AstNode linked lists and replace with arrays

Ginger Bill 9 years ago
parent
commit
50aeea1c2e
7 changed files with 595 additions and 593 deletions
  1. 16 14
      src/checker/checker.cpp
  2. 160 133
      src/checker/expr.cpp
  3. 78 73
      src/checker/stmt.cpp
  4. 103 117
      src/codegen/ssa.cpp
  5. 3 2
      src/main.cpp
  6. 192 236
      src/parser.cpp
  7. 43 18
      src/printer.cpp

+ 16 - 14
src/checker/checker.cpp

@@ -741,7 +741,8 @@ void check_parsed_files(Checker *c) {
 	gb_for_array(i, c->parser->files) {
 		AstFile *f = &c->parser->files[i];
 		add_curr_ast_file(c, f);
-		for (AstNode *decl = f->decls; decl != NULL; decl = decl->next) {
+		gb_for_array(decl_index, f->decls) {
+			AstNode *decl = f->decls[decl_index];
 			if (!is_ast_node_decl(decl))
 				continue;
 
@@ -752,9 +753,9 @@ void check_parsed_files(Checker *c) {
 			case_ast_node(vd, VarDecl, decl);
 				switch (vd->kind) {
 				case Declaration_Immutable: {
-					for (AstNode *name = vd->name_list, *value = vd->value_list;
-					     name != NULL && value != NULL;
-					     name = name->next, value = value->next) {
+					gb_for_array(i, vd->values) {
+						AstNode *name = vd->names[i];
+						AstNode *value = vd->values[i];
 						ExactValue v = {ExactValue_Invalid};
 						Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v);
 						DeclInfo *di = make_declaration_info(c->allocator, c->global_scope);
@@ -763,8 +764,8 @@ void check_parsed_files(Checker *c) {
 						add_file_entity(c, name, e, di);
 					}
 
-					isize lhs_count = vd->name_count;
-					isize rhs_count = vd->value_count;
+					isize lhs_count = gb_array_count(vd->names);
+					isize rhs_count = gb_array_count(vd->values);
 
 					if (rhs_count == 0 && vd->type == NULL) {
 						error(&c->error_collector, ast_node_token(decl), "Missing type or initial expression");
@@ -774,20 +775,24 @@ void check_parsed_files(Checker *c) {
 				} break;
 
 				case Declaration_Mutable: {
-					isize entity_count = vd->name_count;
+					isize entity_count = gb_array_count(vd->names);
 					isize entity_index = 0;
 					Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
 					DeclInfo *di = NULL;
-					if (vd->value_count > 0) {
+					if (gb_array_count(vd->values) > 0) {
 						di = make_declaration_info(gb_heap_allocator(), c->global_scope);
 						di->entities = entities;
 						di->entity_count = entity_count;
 						di->type_expr = vd->type;
-						di->init_expr = vd->value_list;
+						di->init_expr = vd->values[0]; // TODO(bill): Is this correct?
 					}
 
-					AstNode *value = vd->value_list;
-					for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
+					gb_for_array(i, vd->names) {
+						AstNode *name = vd->names[i];
+						AstNode *value = NULL;
+						if (i < gb_array_count(vd->values)) {
+							value = vd->values[i];
+						}
 						Entity *e = make_entity_variable(c->allocator, c->global_scope, name->Ident, NULL);
 						entities[entity_index++] = e;
 
@@ -801,9 +806,6 @@ void check_parsed_files(Checker *c) {
 						}
 
 						add_file_entity(c, name, e, d);
-
-						if (value != NULL)
-							value = value->next;
 					}
 				} break;
 				}

+ 160 - 133
src/checker/expr.cpp

@@ -250,7 +250,7 @@ void populate_using_entity_map(Checker *c, AstNode *node, Type *t, Map<Entity *>
 
 void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr);
 
-void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
+void check_fields(Checker *c, AstNode *node, AstNodeArray decls,
                   Entity **fields, isize field_count,
                   Entity **other_fields, isize other_field_count,
                   CycleChecker *cycle_checker, String context) {
@@ -268,19 +268,21 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
 	DeclInfo *d;d
 	check_entity_decl(c, e, d, NULL);
 #endif
-	for (AstNode *decl = decl_list; decl != NULL; decl = decl->next) {
+	gb_for_array(decl_index, decls) {
+		AstNode *decl = decls[decl_index];
 		if (decl->kind == AstNode_VarDecl) {
 			ast_node(vd, VarDecl, decl);
 			if (vd->kind != Declaration_Immutable)
 				continue;
 
-			isize entity_count = vd->name_count;
+			isize entity_count = gb_array_count(vd->names);
 			isize entity_index = 0;
 			Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
 
-			for (AstNode *name = vd->name_list, *value = vd->value_list;
-			     name != NULL && value != NULL;
-			     name = name->next, value = value->next) {
+			gb_for_array(i, vd->values) {
+				AstNode *name = vd->names[i];
+				AstNode *value = vd->values[i];
+
 				GB_ASSERT(name->kind == AstNode_Ident);
 				ExactValue v = {ExactValue_Invalid};
 				Token name_token = name->Ident;
@@ -289,8 +291,8 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
 				check_const_decl(c, e, vd->type, value);
 			}
 
-			isize lhs_count = vd->name_count;
-			isize rhs_count = vd->value_count;
+			isize lhs_count = gb_array_count(vd->names);
+			isize rhs_count = gb_array_count(vd->values);
 
 			// TODO(bill): Better error messages or is this good enough?
 			if (rhs_count == 0 && vd->type == NULL) {
@@ -299,8 +301,8 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
 				error(&c->error_collector, ast_node_token(node), "Extra initial expression");
 			}
 
-			AstNode *name = vd->name_list;
-			for (isize i = 0; i < entity_count; i++, name = name->next) {
+			gb_for_array(i, vd->names) {
+				AstNode *name = vd->names[i];
 				Entity *e = entities[i];
 				Token name_token = name->Ident;
 				HashKey key = hash_string(name_token.string);
@@ -337,15 +339,20 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
 	if (node->kind == AstNode_UnionType) {
 		isize field_index = 0;
 		fields[field_index++] = make_entity_type_name(c->allocator, c->context.scope, empty_token, NULL);
-		for (AstNode *decl = decl_list; decl != NULL; decl = decl->next) {
-			if (decl->kind != AstNode_VarDecl)
+		gb_for_array(decl_index, decls) {
+			AstNode *decl = decls[decl_index];
+			if (decl->kind != AstNode_VarDecl) {
 				continue;
+			}
+
 			ast_node(vd, VarDecl, decl);
-			if (vd->kind != Declaration_Mutable)
+			if (vd->kind != Declaration_Mutable) {
 				continue;
+			}
 			Type *base_type = check_type(c, vd->type, NULL, cycle_checker);
 
-			for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
+			gb_for_array(name_index, vd->names) {
+				AstNode *name = vd->names[name_index];
 				Token name_token = name->Ident;
 
 				Type *type = make_type_named(c->allocator, name_token.string, base_type, NULL);
@@ -366,22 +373,26 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
 		}
 	} else {
 		isize field_index = 0;
-		for (AstNode *decl = decl_list; decl != NULL; decl = decl->next) {
-			if (decl->kind != AstNode_VarDecl)
+		gb_for_array(decl_index, decls) {
+			AstNode *decl = decls[decl_index];
+			if (decl->kind != AstNode_VarDecl) {
 				continue;
+			}
 			ast_node(vd, VarDecl, decl);
-			if (vd->kind != Declaration_Mutable)
+			if (vd->kind != Declaration_Mutable) {
 				continue;
+			}
 			Type *type = check_type(c, vd->type, NULL, cycle_checker);
 
 			if (vd->is_using) {
-				if (vd->name_count > 1) {
-					error(&c->error_collector, ast_node_token(vd->name_list),
+				if (gb_array_count(vd->names) > 1) {
+					error(&c->error_collector, ast_node_token(vd->names[0]),
 					      "Cannot apply `using` to more than one of the same type");
 				}
 			}
 
-			for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
+			gb_for_array(name_index, vd->names) {
+				AstNode *name = vd->names[name_index];
 				Token name_token = name->Ident;
 
 				Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type, vd->is_using);
@@ -401,7 +412,7 @@ void check_fields(Checker *c, AstNode *node, AstNode *decl_list,
 			if (vd->is_using) {
 				Type *t = get_base_type(type_deref(type));
 				if (!is_type_struct(t) && !is_type_raw_union(t)) {
-					Token name_token = vd->name_list->Ident;
+					Token name_token = vd->names[0]->Ident;
 					error(&c->error_collector, name_token, "`using` on a field `%.*s` must be a type", LIT(name_token.string));
 					continue;
 				}
@@ -419,13 +430,14 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node, CycleChecke
 
 	isize field_count = 0;
 	isize other_field_count = 0;
-	for (AstNode *decl = st->decl_list; decl != NULL; decl = decl->next) {
+	gb_for_array(decl_index, st->decls) {
+		AstNode *decl = st->decls[decl_index];
 		switch (decl->kind) {
 		case_ast_node(vd, VarDecl, decl);
 			if (vd->kind == Declaration_Mutable) {
-				field_count += vd->name_count;
+				field_count += gb_array_count(vd->names);
 			} else {
-				other_field_count += vd->name_count;
+				other_field_count += gb_array_count(vd->names);
 			}
 		case_end;
 
@@ -438,7 +450,7 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node, CycleChecke
 	Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count);
 	Entity **other_fields = gb_alloc_array(c->allocator, Entity *, other_field_count);
 
-	check_fields(c, node, st->decl_list, fields, field_count, other_fields, other_field_count, cycle_checker, make_string("struct"));
+	check_fields(c, node, st->decls, fields, field_count, other_fields, other_field_count, cycle_checker, make_string("struct"));
 
 	struct_type->Record.struct_is_packed  = st->is_packed;
 	struct_type->Record.fields            = fields;
@@ -453,13 +465,14 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker
 
 	isize field_count = 1;
 	isize other_field_count = 0;
-	for (AstNode *decl = ut->decl_list; decl != NULL; decl = decl->next) {
+	gb_for_array(decl_index, ut->decls) {
+		AstNode *decl = ut->decls[decl_index];
 		switch (decl->kind) {
 		case_ast_node(vd, VarDecl, decl);
 			if (vd->kind == Declaration_Mutable) {
-				field_count += vd->name_count;
+				field_count += gb_array_count(vd->names);
 			} else {
-				other_field_count += vd->name_count;
+				other_field_count += gb_array_count(vd->names);
 			}
 		case_end;
 
@@ -472,7 +485,7 @@ void check_union_type(Checker *c, Type *union_type, AstNode *node, CycleChecker
 	Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count);
 	Entity **other_fields = gb_alloc_array(c->allocator, Entity *, other_field_count);
 
-	check_fields(c, node, ut->decl_list, fields, field_count, other_fields, other_field_count, cycle_checker, make_string("union"));
+	check_fields(c, node, ut->decls, fields, field_count, other_fields, other_field_count, cycle_checker, make_string("union"));
 
 	union_type->Record.fields            = fields;
 	union_type->Record.field_count       = field_count;
@@ -487,13 +500,14 @@ void check_raw_union_type(Checker *c, Type *union_type, AstNode *node, CycleChec
 
 	isize field_count = 0;
 	isize other_field_count = 0;
-	for (AstNode *decl = ut->decl_list; decl != NULL; decl = decl->next) {
+	gb_for_array(decl_index, ut->decls) {
+		AstNode *decl = ut->decls[decl_index];
 		switch (decl->kind) {
 		case_ast_node(vd, VarDecl, decl);
 			if (vd->kind == Declaration_Mutable) {
-				field_count += vd->name_count;
+				field_count += gb_array_count(vd->names);
 			} else {
-				other_field_count += vd->name_count;
+				other_field_count += gb_array_count(vd->names);
 			}
 		case_end;
 
@@ -506,7 +520,7 @@ void check_raw_union_type(Checker *c, Type *union_type, AstNode *node, CycleChec
 	Entity **fields = gb_alloc_array(c->allocator, Entity *, field_count);
 	Entity **other_fields = gb_alloc_array(c->allocator, Entity *, other_field_count);
 
-	check_fields(c, node, ut->decl_list, fields, field_count, other_fields, other_field_count, cycle_checker, make_string("raw union"));
+	check_fields(c, node, ut->decls, fields, field_count, other_fields, other_field_count, cycle_checker, make_string("raw union"));
 
 	union_type->Record.fields = fields;
 	union_type->Record.field_count = field_count;
@@ -538,10 +552,12 @@ void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *nod
 	}
 	enum_type->Record.enum_base = base_type;
 
-	Entity **fields = gb_alloc_array(c->allocator, Entity *, et->field_count);
+	Entity **fields = gb_alloc_array(c->allocator, Entity *, gb_array_count(et->fields));
 	isize field_index = 0;
 	ExactValue iota = make_exact_value_integer(-1);
-	for (AstNode *field = et->field_list; field != NULL; field = field->next) {
+	gb_for_array(i, et->fields) {
+		AstNode *field = et->fields[i];
+
 		ast_node(f, FieldValue, field);
 		Token name_token = f->field->Ident;
 
@@ -583,26 +599,34 @@ void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *nod
 		add_entity_use(&c->info, f->field, e);
 	}
 	enum_type->Record.other_fields = fields;
-	enum_type->Record.other_field_count = et->field_count;
+	enum_type->Record.other_field_count = gb_array_count(et->fields);
 }
 
-Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize field_count, b32 *is_variadic_) {
-	if (field_list == NULL || field_count == 0)
+Type *check_get_params(Checker *c, Scope *scope, AstNodeArray fields, b32 *is_variadic_) {
+	if (fields == NULL || gb_array_count(fields) == 0)
 		return NULL;
 
 	b32 is_variadic = false;
 
 	Type *tuple = make_type_tuple(c->allocator);
 
-	Entity **variables = gb_alloc_array(c->allocator, Entity *, field_count);
+	isize variable_count = 0;
+	gb_for_array(i, fields) {
+		AstNode *field = fields[i];
+		ast_node(f, Field, field);
+		variable_count += gb_array_count(f->names);
+	}
+
+	Entity **variables = gb_alloc_array(c->allocator, Entity *, variable_count);
 	isize variable_index = 0;
-	for (AstNode *field = field_list; field != NULL; field = field->next) {
+	gb_for_array(i, fields) {
+		AstNode *field = fields[i];
 		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) {
+				if (i+1 == gb_array_count(fields)) {
 					is_variadic = true;
 				} else {
 					error(&c->error_collector, ast_node_token(field), "Invalid AST: Invalid variadic parameter");
@@ -610,7 +634,8 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize fiel
 			}
 
 			Type *type = check_type(c, type_expr);
-			for (AstNode *name = f->name_list; name != NULL; name = name->next) {
+			gb_for_array(j, f->names) {
+				AstNode *name = f->names[j];
 				if (name->kind == AstNode_Ident) {
 					Entity *param = make_entity_param(c->allocator, scope, name->Ident, type, f->is_using);
 					add_entity(c, scope, name, param);
@@ -622,29 +647,31 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize fiel
 		}
 	}
 
-	if (is_variadic && field_count > 0) {
+	if (is_variadic && gb_array_count(fields) > 0) {
 		// NOTE(bill): Change last variadic parameter to be a slice
 		// Custom Calling convention for variadic parameters
-		Entity *end = variables[field_count-1];
+		Entity *end = variables[gb_array_count(fields)-1];
 		end->type = make_type_slice(c->allocator, end->type);
 	}
 
 	tuple->Tuple.variables = variables;
-	tuple->Tuple.variable_count = field_count;
+	tuple->Tuple.variable_count = variable_count;
 
 	if (is_variadic_) *is_variadic_ = is_variadic;
 
 	return tuple;
 }
 
-Type *check_get_results(Checker *c, Scope *scope, AstNode *list, isize list_count) {
-	if (list == NULL)
+Type *check_get_results(Checker *c, Scope *scope, AstNodeArray results) {
+	if (results == NULL || gb_array_count(results) == 0) {
 		return NULL;
+	}
 	Type *tuple = make_type_tuple(c->allocator);
 
-	Entity **variables = gb_alloc_array(c->allocator, Entity *, list_count);
+	Entity **variables = gb_alloc_array(c->allocator, Entity *, gb_array_count(results));
 	isize variable_index = 0;
-	for (AstNode *item = list; item != NULL; item = item->next) {
+	gb_for_array(i, results) {
+		AstNode *item = results[i];
 		Type *type = check_type(c, item);
 		Token token = ast_node_token(item);
 		token.string = make_string(""); // NOTE(bill): results are not named
@@ -654,7 +681,7 @@ Type *check_get_results(Checker *c, Scope *scope, AstNode *list, isize list_coun
 		variables[variable_index++] = param;
 	}
 	tuple->Tuple.variables = variables;
-	tuple->Tuple.variable_count = list_count;
+	tuple->Tuple.variable_count = gb_array_count(results);
 
 	return tuple;
 }
@@ -663,20 +690,23 @@ Type *check_get_results(Checker *c, Scope *scope, AstNode *list, isize list_coun
 void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
 	ast_node(pt, ProcType, proc_type_node);
 
-	isize param_count = pt->param_count;
-	isize result_count = pt->result_count;
 
 	// gb_printf("%td -> %td\n", param_count, result_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 *params  = check_get_params(c, c->context.scope, pt->params, &variadic);
+	Type *results = check_get_results(c, c->context.scope, pt->results);
+
+	isize param_count = 0;
+	isize result_count = 0;
+	if (params)  param_count  = params ->Tuple.variable_count;
+	if (results) result_count = results->Tuple.variable_count;
 
 	type->Proc.scope        = c->context.scope;
 	type->Proc.params       = params;
-	type->Proc.param_count  = pt->param_count;
+	type->Proc.param_count  = param_count;
 	type->Proc.results      = results;
-	type->Proc.result_count = pt->result_count;
+	type->Proc.result_count = result_count;
 	type->Proc.variadic     = variadic;
 }
 
@@ -1953,15 +1983,15 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 	BuiltinProc *bp = &builtin_procs[id];
 	{
 		char *err = NULL;
-		if (ce->arg_list_count < bp->arg_count)
+		if (gb_array_count(ce->args) < bp->arg_count)
 			err = "Too few";
-		if (ce->arg_list_count > bp->arg_count && !bp->variadic)
+		if (gb_array_count(ce->args) > bp->arg_count && !bp->variadic)
 			err = "Too many";
 		if (err) {
 			ast_node(proc, Ident, ce->proc);
 			error(&c->error_collector, ce->close, "`%s` arguments for `%.*s`, expected %td, got %td",
 			      err, LIT(proc->string),
-			      bp->arg_count, ce->arg_list_count);
+			      bp->arg_count, gb_array_count(ce->args));
 			return false;
 		}
 	}
@@ -1975,17 +2005,17 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		// NOTE(bill): The first arg is a Type, this will be checked case by case
 		break;
 	default:
-		check_multi_expr(c, operand, ce->arg_list);
+		check_multi_expr(c, operand, ce->args[0]);
 	}
 
 	switch (id) {
 	case BuiltinProc_new: {
 		// new :: proc(Type) -> ^Type
 		Operand op = {};
-		check_expr_or_type(c, &op, ce->arg_list);
+		check_expr_or_type(c, &op, ce->args[0]);
 		Type *type = op.type;
 		if (op.mode != Addressing_Type && type == NULL || type == t_invalid) {
-			error(&c->error_collector, ast_node_token(ce->arg_list), "Expected a type for `new`");
+			error(&c->error_collector, ast_node_token(ce->args[0]), "Expected a type for `new`");
 			return false;
 		}
 		operand->mode = Addressing_Value;
@@ -1994,15 +2024,15 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 	case BuiltinProc_new_slice: {
 		// new_slice :: proc(Type, len: int[, cap: int]) -> []Type
 		Operand op = {};
-		check_expr_or_type(c, &op, ce->arg_list);
+		check_expr_or_type(c, &op, ce->args[0]);
 		Type *type = op.type;
 		if (op.mode != Addressing_Type && type == NULL || type == t_invalid) {
-			error(&c->error_collector, ast_node_token(ce->arg_list), "Expected a type for `new_slice`");
+			error(&c->error_collector, ast_node_token(ce->args[0]), "Expected a type for `new_slice`");
 			return false;
 		}
 
-		AstNode *len = ce->arg_list->next;
-		AstNode *cap = len->next;
+		AstNode *len = ce->args[1];
+		AstNode *cap = ce->args[2];
 
 		check_expr(c, &op, len);
 		if (op.mode == Addressing_Invalid)
@@ -2028,7 +2058,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 				      type_str);
 				return false;
 			}
-			if (cap->next != NULL) {
+			if (ce->args[3] != NULL) {
 				error(&c->error_collector, ast_node_token(call),
 				      "Too many arguments to `new_slice`, expected either 2 or 3");
 				return false;
@@ -2057,10 +2087,10 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 	case BuiltinProc_size_of: {
 		// size_of :: proc(Type) -> int
 		Operand op = {};
-		check_expr_or_type(c, &op, ce->arg_list);
+		check_expr_or_type(c, &op, ce->args[0]);
 		Type *type = op.type;
 		if (!type) {
-			error(&c->error_collector, ast_node_token(ce->arg_list), "Expected a type for `size_of`");
+			error(&c->error_collector, ast_node_token(ce->args[0]), "Expected a type for `size_of`");
 			return false;
 		}
 
@@ -2084,10 +2114,10 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 	case BuiltinProc_align_of: {
 		// align_of :: proc(Type) -> int
 		Operand op = {};
-		check_expr_or_type(c, &op, ce->arg_list);
+		check_expr_or_type(c, &op, ce->args[0]);
 		Type *type = op.type;
 		if (!type) {
-			error(&c->error_collector, ast_node_token(ce->arg_list), "Expected a type for `align_of`");
+			error(&c->error_collector, ast_node_token(ce->args[0]), "Expected a type for `align_of`");
 			return false;
 		}
 		operand->mode = Addressing_Constant;
@@ -2109,15 +2139,15 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 	case BuiltinProc_offset_of: {
 		// offset_val :: proc(Type, field) -> int
 		Operand op = {};
-		check_expr_or_type(c, &op, ce->arg_list);
+		check_expr_or_type(c, &op, ce->args[0]);
 		Type *type = get_base_type(op.type);
-		AstNode *field_arg = unparen_expr(ce->arg_list->next);
+		AstNode *field_arg = unparen_expr(ce->args[1]);
 		if (type != NULL) {
-			error(&c->error_collector, ast_node_token(ce->arg_list), "Expected a type for `offset_of`");
+			error(&c->error_collector, ast_node_token(ce->args[0]), "Expected a type for `offset_of`");
 			return false;
 		}
 		if (!is_type_struct(type)) {
-			error(&c->error_collector, ast_node_token(ce->arg_list), "Expected a structure type for `offset_of`");
+			error(&c->error_collector, ast_node_token(ce->args[0]), "Expected a structure type for `offset_of`");
 			return false;
 		}
 		if (field_arg == NULL ||
@@ -2131,7 +2161,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		Selection sel = lookup_field(type, arg->string, operand->mode == Addressing_Type);
 		if (sel.entity == NULL) {
 			gbString type_str = type_to_string(type);
-			error(&c->error_collector, ast_node_token(ce->arg_list),
+			error(&c->error_collector, ast_node_token(ce->args[0]),
 			      "`%s` has no field named `%.*s`", type_str, LIT(arg->string));
 			return false;
 		}
@@ -2144,7 +2174,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 
 	case BuiltinProc_offset_of_val: {
 		// offset_val :: proc(val: expression) -> int
-		AstNode *arg = unparen_expr(ce->arg_list);
+		AstNode *arg = unparen_expr(ce->args[0]);
 		if (arg->kind != AstNode_SelectorExpr) {
 			gbString str = expr_to_string(arg);
 			error(&c->error_collector, ast_node_token(arg), "`%s` is not a selector expression", str);
@@ -2192,7 +2222,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		// assert :: proc(cond: bool)
 
 		if (!is_type_boolean(operand->type)) {
-			gbString str = expr_to_string(ce->arg_list);
+			gbString str = expr_to_string(ce->args[0]);
 			defer (gb_string_free(str));
 			error(&c->error_collector, ast_node_token(call),
 			      "`%s` is not a boolean", str);
@@ -2200,7 +2230,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		}
 		if (operand->mode == Addressing_Constant &&
 		    !operand->value.value_bool) {
-			gbString str = expr_to_string(ce->arg_list);
+			gbString str = expr_to_string(ce->args[0]);
 			defer (gb_string_free(str));
 			error(&c->error_collector, ast_node_token(call),
 			      "Compile time assertion: `%s`", str);
@@ -2272,7 +2302,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 			dest_type = d->Slice.elem;
 
 		Operand op = {};
-		check_expr(c, &op, ce->arg_list->next);
+		check_expr(c, &op, ce->args[1]);
 		if (op.mode == Addressing_Invalid)
 			return false;
 		Type *s = get_base_type(op.type);
@@ -2285,8 +2315,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		}
 
 		if (!are_types_identical(dest_type, src_type)) {
-			gbString d_arg = expr_to_string(ce->arg_list);
-			gbString s_arg = expr_to_string(ce->arg_list->next);
+			gbString d_arg = expr_to_string(ce->args[0]);
+			gbString s_arg = expr_to_string(ce->args[1]);
 			gbString d_str = type_to_string(dest_type);
 			gbString s_str = type_to_string(src_type);
 			defer (gb_string_free(d_arg));
@@ -2309,7 +2339,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		x_type = get_base_type(operand->type);
 
 		Operand op = {};
-		check_expr(c, &op, ce->arg_list->next);
+		check_expr(c, &op, ce->args[1]);
 		if (op.mode == Addressing_Invalid)
 			return false;
 		y_type = get_base_type(op.type);
@@ -2321,8 +2351,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 
 		Type *elem_type = x_type->Pointer.elem->Slice.elem;
 		if (!check_is_assignable_to(c, &op, elem_type)) {
-			gbString d_arg = expr_to_string(ce->arg_list);
-			gbString s_arg = expr_to_string(ce->arg_list->next);
+			gbString d_arg = expr_to_string(ce->args[0]);
+			gbString s_arg = expr_to_string(ce->args[1]);
 			gbString d_str = type_to_string(elem_type);
 			gbString s_str = type_to_string(y_type);
 			defer (gb_string_free(d_arg));
@@ -2353,7 +2383,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 
 		isize max_count = vector_type->Vector.count;
 		isize arg_count = 0;
-		for (AstNode *arg = ce->arg_list->next; arg != NULL; arg = arg->next) {
+		gb_for_array(i, ce->args) {
+			if (i == 0) continue;
+			AstNode *arg = ce->args[i];
 			Operand op = {};
 			check_expr(c, &op, arg);
 			if (op.mode == Addressing_Invalid)
@@ -2406,7 +2438,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 			return false;
 		}
 
-		AstNode *offset = ce->arg_list->next;
+		AstNode *offset = ce->args[1];
 		Operand op = {};
 		check_expr(c, &op, offset);
 		if (op.mode == Addressing_Invalid)
@@ -2447,7 +2479,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 			      "`rawptr` cannot have pointer arithmetic");
 			return false;
 		}
-		AstNode *offset = ce->arg_list->next;
+		AstNode *offset = ce->args[1];
 		Operand op = {};
 		check_expr(c, &op, offset);
 		if (op.mode == Addressing_Invalid)
@@ -2509,8 +2541,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 			return false;
 		}
 
-		AstNode *len = ce->arg_list->next;
-		AstNode *cap = len->next;
+		AstNode *len = ce->args[1];
+		AstNode *cap = ce->args[2];
 
 		Operand op = {};
 		check_expr(c, &op, len);
@@ -2537,7 +2569,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 				      type_str);
 				return false;
 			}
-			if (cap->next != NULL) {
+			if (ce->args[2] != NULL) {
 				error(&c->error_collector, ast_node_token(call),
 				      "Too many arguments to `slice_ptr`, expected either 2 or 3");
 				return false;
@@ -2560,7 +2592,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 			return false;
 		}
 
-		AstNode *other_arg = ce->arg_list->next;
+		AstNode *other_arg = ce->args[1];
 		Operand a = *operand;
 		Operand b = {};
 		check_expr(c, &b, other_arg);
@@ -2620,7 +2652,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 			return false;
 		}
 
-		AstNode *other_arg = ce->arg_list->next;
+		AstNode *other_arg = ce->args[1];
 		Operand a = *operand;
 		Operand b = {};
 		check_expr(c, &b, other_arg);
@@ -2710,6 +2742,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 	return true;
 }
 
+
 void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode *call) {
 	GB_ASSERT(call->kind == AstNode_CallExpr);
 	GB_ASSERT(proc_type->kind == Type_Proc);
@@ -2734,21 +2767,22 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
 		}
 	}
 
-	if (ce->arg_list_count == 0) {
+	if (gb_array_count(ce->args) == 0) {
 		if (variadic && param_count-1 == 0)
 			return;
 		if (param_count == 0)
 			return;
 	}
 
+	// TODO(bill): Completely redo this entire code.
+	// It's from when I used linked lists instead of arrays in the parser
 
-
-	if (ce->arg_list_count > param_count && !variadic) {
+	if (gb_array_count(ce->args) > param_count && !variadic) {
 		error_code = +1;
 	} else {
 		Entity **sig_params = proc_type->Proc.params->Tuple.variables;
-		AstNode *call_arg = ce->arg_list;
-		for (; call_arg != NULL; call_arg = call_arg->next) {
+		gb_for_array(arg_index, ce->args) {
+			AstNode *call_arg = ce->args[arg_index];
 			check_multi_expr(c, operand, call_arg);
 			if (operand->mode == Addressing_Invalid)
 				continue;
@@ -2804,8 +2838,6 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
 		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;
 		}
 	}
 
@@ -2832,8 +2864,8 @@ ExprKind check_call_expr(Checker *c, Operand *operand, AstNode *call) {
 	check_expr_or_type(c, operand, ce->proc);
 
 	if (operand->mode == Addressing_Invalid) {
-		for (AstNode *arg = ce->arg_list; arg != NULL; arg = arg->next) {
-			check_expr_base(c, operand, arg);
+		gb_for_array(i, ce->args) {
+			check_expr_base(c, operand, ce->args[i]);
 		}
 		operand->mode = Addressing_Invalid;
 		operand->expr = call;
@@ -2977,17 +3009,16 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 		case Type_Record: {
 			if (!is_type_struct(t))
 				break;
-			if (cl->elem_count == 0)
+			if (gb_array_count(cl->elems) == 0) {
 				break; // NOTE(bill): No need to init
+			}
 			{ // Checker values
-				AstNode *elem = cl->elem_list;
 				isize field_count = t->Record.field_count;
-				if (elem->kind == AstNode_FieldValue) {
+				if (cl->elems[0]->kind == AstNode_FieldValue) {
 					b32 *fields_visited = gb_alloc_array(c->allocator, b32, field_count);
 
-					for (;
-					     elem != NULL;
-					     elem = elem->next) {
+					gb_for_array(i, cl->elems) {
+						AstNode *elem = cl->elems[i];
 						if (elem->kind != AstNode_FieldValue) {
 							error(&c->error_collector, ast_node_token(elem),
 							      "Mixture of `field = value` and value elements in a structure literal is not allowed");
@@ -3030,10 +3061,8 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 						check_assignment(c, o, field->type, make_string("structure literal"));
 					}
 				} else {
-					isize index = 0;
-					for (;
-					     elem != NULL;
-					     elem = elem->next, index++) {
+					gb_for_array(index, cl->elems) {
+						AstNode *elem = cl->elems[index];
 						if (elem->kind == AstNode_FieldValue) {
 							error(&c->error_collector, ast_node_token(elem),
 							      "Mixture of `field = value` and value elements in a structure literal is not allowed");
@@ -3048,8 +3077,8 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 						}
 						check_assignment(c, o, field->type, make_string("structure literal"));
 					}
-					if (cl->elem_count < field_count) {
-						error(&c->error_collector, cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elem_count);
+					if (gb_array_count(cl->elems) < field_count) {
+						error(&c->error_collector, cl->close, "Too few values in structure literal, expected %td, got %td", field_count, gb_array_count(cl->elems));
 					}
 				}
 			}
@@ -3074,10 +3103,10 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 			}
 
 
-			i64 index = 0;
 			i64 max = 0;
-			for (AstNode *elem = cl->elem_list; elem != NULL; elem = elem->next, index++) {
-				AstNode *e = elem;
+			isize index = 0;
+			for (; index < gb_array_count(cl->elems); index++) {
+				AstNode *e = cl->elems[index];
 				if (e->kind == AstNode_FieldValue) {
 					error(&c->error_collector, ast_node_token(e),
 					      "`field = value` is only allowed in structure literals");
@@ -3088,12 +3117,12 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 				if (t->kind == Type_Array &&
 				    t->Array.count >= 0 &&
 				    index >= t->Array.count) {
-					error(&c->error_collector, ast_node_token(elem), "Index %lld is out of bounds (>= %lld) for array literal", index, t->Array.count);
+					error(&c->error_collector, ast_node_token(e), "Index %lld is out of bounds (>= %lld) for array literal", index, t->Array.count);
 				}
 				if (t->kind == Type_Vector &&
 				    t->Vector.count >= 0 &&
 				    index >= t->Vector.count) {
-					error(&c->error_collector, ast_node_token(elem), "Index %lld is out of bounds (>= %lld) for vector literal", index, t->Vector.count);
+					error(&c->error_collector, ast_node_token(e), "Index %lld is out of bounds (>= %lld) for vector literal", index, t->Vector.count);
 				}
 
 				Operand o = {};
@@ -3105,7 +3134,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 
 			if (t->kind == Type_Vector) {
 				if (t->Vector.count > 1 && gb_is_between(index, 2, t->Vector.count-1)) {
-					error(&c->error_collector, ast_node_token(cl->elem_list),
+					error(&c->error_collector, ast_node_token(cl->elems[0]),
 					      "Expected either 1 (broadcast) or %td elements in vector literal, got %td", t->Vector.count, index);
 				}
 			}
@@ -3454,15 +3483,14 @@ void check_expr_or_type(Checker *c, Operand *o, AstNode *e) {
 
 gbString write_expr_to_string(gbString str, AstNode *node);
 
-gbString write_field_list_to_string(gbString str, AstNode *field_list, char *sep) {
-	isize i = 0;
-	for (AstNode *field = field_list; field != NULL; field = field->next) {
+gbString write_fields_to_string(gbString str, AstNodeArray fields, char *sep) {
+	gb_for_array(i, fields) {
+		AstNode *field = fields[i];
 		ast_node(f, Field, field);
 		if (i > 0)
 			str = gb_string_appendc(str, sep);
 
 		str = write_expr_to_string(str, field);
-		i++;
 	}
 	return str;
 }
@@ -3594,12 +3622,11 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 		if (f->is_using) {
 			str = gb_string_appendc(str, "using ");
 		}
-		isize i = 0;
-		for (AstNode *name = f->name_list; name != NULL; name = name->next) {
+		gb_for_array(i, f->names) {
+			AstNode *name = f->names[i];
 			if (i > 0)
 				str = gb_string_appendc(str, ", ");
 			str = write_expr_to_string(str, name);
-			i++;
 		}
 
 		str = gb_string_appendc(str, ": ");
@@ -3609,32 +3636,32 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 	case_ast_node(ce, CallExpr, node);
 		str = write_expr_to_string(str, ce->proc);
 		str = gb_string_appendc(str, "(");
-		isize i = 0;
-		for (AstNode *arg = ce->arg_list; arg != NULL; arg = arg->next) {
+
+		gb_for_array(i, ce->args) {
+			AstNode *arg = ce->args[i];
 			if (i > 0) {
 				str = gb_string_appendc(str, ", ");
 			}
 			str = write_expr_to_string(str, arg);
-			i++;
 		}
 		str = gb_string_appendc(str, ")");
 	case_end;
 
 	case_ast_node(pt, ProcType, node);
 		str = gb_string_appendc(str, "proc(");
-		str = write_field_list_to_string(str, pt->param_list, ", ");
+		str = write_fields_to_string(str, pt->params, ", ");
 		str = gb_string_appendc(str, ")");
 	case_end;
 
 	case_ast_node(st, StructType, node);
 		str = gb_string_appendc(str, "struct{");
-		// str = write_field_list_to_string(str, st->decl_list, ", ");
+		// str = write_fields_to_string(str, st->decl_list, ", ");
 		str = gb_string_appendc(str, "}");
 	case_end;
 
 	case_ast_node(st, RawUnionType, node);
 		str = gb_string_appendc(str, "raw_union{");
-		// str = write_field_list_to_string(str, st->decl_list, ", ");
+		// str = write_fields_to_string(str, st->decl_list, ", ");
 		str = gb_string_appendc(str, "}");
 	case_end;
 

+ 78 - 73
src/checker/stmt.cpp

@@ -9,17 +9,17 @@ enum StmtFlag : u32 {
 
 void check_stmt(Checker *c, AstNode *node, u32 flags);
 
-void check_stmt_list(Checker *c, AstNode *list, isize list_count, u32 flags) {
+void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
 	b32 ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
 	u32 f = flags & (~Stmt_FallthroughAllowed);
 
-	isize i = 0;
-	for (AstNode *n = list; n != NULL; n = n->next, i++) {
+	gb_for_array(i, stmts) {
+		AstNode *n = stmts[i];
 		if (n->kind == AstNode_EmptyStmt) {
 			continue;
 		}
 		u32 new_flags = f;
-		if (ft_ok && i+1 == list_count) {
+		if (ft_ok && i+1 == gb_array_count(stmts)) {
 			new_flags |= Stmt_FallthroughAllowed;
 		}
 		check_stmt(c, n, new_flags);
@@ -29,24 +29,22 @@ void check_stmt_list(Checker *c, AstNode *list, isize list_count, u32 flags) {
 b32 check_is_terminating(AstNode *node);
 b32 check_has_break(AstNode *stmt, b32 implicit);
 
-b32 check_is_terminating_list(AstNode *list) {
-	// Get to end of list
-	for (; list != NULL; list = list->next) {
-		if (list->next == NULL)
-			break;
-	}
+b32 check_is_terminating_list(AstNodeArray stmts) {
 
 	// Iterate backwards
-	for (AstNode *n = list; n != NULL; n = n->prev) {
-		if (n->kind != AstNode_EmptyStmt)
-			return check_is_terminating(n);
+	for (isize n = gb_array_count(stmts)-1; n >= 0; n--) {
+		AstNode *stmt = stmts[n];
+		if (stmt->kind != AstNode_EmptyStmt) {
+			return check_is_terminating(stmt);
+		}
 	}
 
 	return false;
 }
 
-b32 check_has_break_list(AstNode *list, b32 implicit) {
-	for (AstNode *stmt = list; stmt != NULL; stmt = stmt->next) {
+b32 check_has_break_list(AstNodeArray stmts, b32 implicit) {
+	gb_for_array(i, stmts) {
+		AstNode *stmt = stmts[i];
 		if (check_has_break(stmt, implicit)) {
 			return true;
 		}
@@ -63,7 +61,7 @@ b32 check_has_break(AstNode *stmt, b32 implicit) {
 		}
 		break;
 	case AstNode_BlockStmt:
-		return check_has_break_list(stmt->BlockStmt.list, implicit);
+		return check_has_break_list(stmt->BlockStmt.stmts, implicit);
 
 	case AstNode_IfStmt:
 		if (check_has_break(stmt->IfStmt.body, implicit) ||
@@ -91,7 +89,7 @@ b32 check_is_terminating(AstNode *node) {
 	case_end;
 
 	case_ast_node(bs, BlockStmt, node);
-		return check_is_terminating_list(bs->list);
+		return check_is_terminating_list(bs->stmts);
 	case_end;
 
 	case_ast_node(es, ExprStmt, node);
@@ -115,7 +113,8 @@ b32 check_is_terminating(AstNode *node) {
 
 	case_ast_node(ms, MatchStmt, node);
 		b32 has_default = false;
-		for (AstNode *clause = ms->body->BlockStmt.list; clause != NULL; clause = clause->next) {
+		gb_for_array(i, ms->body->BlockStmt.stmts) {
+			AstNode *clause = ms->body->BlockStmt.stmts[i];
 			ast_node(cc, CaseClause, clause);
 			if (cc->list == NULL) {
 				has_default = true;
@@ -130,7 +129,8 @@ b32 check_is_terminating(AstNode *node) {
 
 	case_ast_node(ms, TypeMatchStmt, node);
 		b32 has_default = false;
-		for (AstNode *clause = ms->body->BlockStmt.list; clause != NULL; clause = clause->next) {
+		gb_for_array(i, ms->body->BlockStmt.stmts) {
+			AstNode *clause = ms->body->BlockStmt.stmts[i];
 			ast_node(cc, CaseClause, clause);
 			if (cc->list == NULL) {
 				has_default = true;
@@ -259,8 +259,8 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
 	return e->type;
 }
 
-void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNode *init_list, isize init_count, String context_name) {
-	if ((lhs == NULL || lhs_count == 0) && init_count == 0)
+void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArray inits, String context_name) {
+	if ((lhs == NULL || lhs_count == 0) && gb_array_count(inits) == 0)
 		return;
 
 	// TODO(bill): Do not use heap allocation here if I can help it
@@ -268,7 +268,8 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNode *in
 	gb_array_init(operands, gb_heap_allocator());
 	defer (gb_array_free(operands));
 
-	for (AstNode *rhs = init_list; rhs != NULL; rhs = rhs->next) {
+	gb_for_array(i, inits) {
+		AstNode *rhs = inits[i];
 		Operand o = {};
 		check_multi_expr(c, &o, rhs);
 		if (o.type->kind != Type_Tuple) {
@@ -422,7 +423,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
 	push_procedure(c, type);
 	ast_node(bs, BlockStmt, body);
 	// TODO(bill): Check declarations first (except mutable variable declarations)
-	check_stmt_list(c, bs->list, bs->list_count, 0);
+	check_stmt_list(c, bs->stmts, 0);
 	if (type->Proc.result_count > 0) {
 		if (!check_is_terminating(body)) {
 			error(&c->error_collector, bs->close, "Missing return statement at the end of the procedure");
@@ -548,7 +549,10 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count
 			entities[i]->type = e->type;
 	}
 
-	check_init_variables(c, entities, entity_count, init_expr, 1, make_string("variable declaration"));
+	AstNodeArray inits;
+	gb_array_init(inits, c->allocator);
+	gb_array_append(inits, init_expr);
+	check_init_variables(c, entities, entity_count, inits, make_string("variable declaration"));
 }
 
 
@@ -587,7 +591,7 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type, Cyc
 
 void check_var_decl_node(Checker *c, AstNode *node) {
 	ast_node(vd, VarDecl, node);
-	isize entity_count = vd->name_count;
+	isize entity_count = gb_array_count(vd->names);
 	isize entity_index = 0;
 	Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
 	switch (vd->kind) {
@@ -595,7 +599,8 @@ void check_var_decl_node(Checker *c, AstNode *node) {
 		Entity **new_entities = gb_alloc_array(c->allocator, Entity *, entity_count);
 		isize new_entity_count = 0;
 
-		for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
+		gb_for_array(i, vd->names) {
+			AstNode *name = vd->names[i];
 			Entity *entity = NULL;
 			Token token = name->Ident;
 			if (name->kind == AstNode_Ident) {
@@ -648,23 +653,22 @@ void check_var_decl_node(Checker *c, AstNode *node) {
 				e->type = init_type;
 		}
 
-		check_init_variables(c, entities, entity_count, vd->value_list, vd->value_count, make_string("variable declaration"));
+		check_init_variables(c, entities, entity_count, vd->values, make_string("variable declaration"));
 
-		AstNode *name = vd->name_list;
-		for (isize i = 0; i < new_entity_count; i++, name = name->next) {
-			add_entity(c, c->context.scope, name, new_entities[i]);
+		gb_for_array(i, vd->names) {
+			add_entity(c, c->context.scope, vd->names[i], new_entities[i]);
 		}
 
 	} break;
 
 	case Declaration_Immutable: {
-		for (AstNode *name = vd->name_list, *value = vd->value_list;
-		     name != NULL && value != NULL;
-		     name = name->next, value = value->next) {
+		gb_for_array(i, vd->values) {
+			AstNode *name = vd->names[i];
+			AstNode *value = vd->values[i];
+
 			GB_ASSERT(name->kind == AstNode_Ident);
 			ExactValue v = {ExactValue_Invalid};
-			ast_node(i, Ident, name);
-			String str = i->string;
+			String str = name->Ident.string;
 			Entity *found = current_scope_lookup_entity(c->context.scope, str);
 			if (found == NULL) {
 				Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v);
@@ -675,8 +679,8 @@ void check_var_decl_node(Checker *c, AstNode *node) {
 			}
 		}
 
-		isize lhs_count = vd->name_count;
-		isize rhs_count = vd->value_count;
+		isize lhs_count = gb_array_count(vd->names);
+		isize rhs_count = gb_array_count(vd->values);
 
 		// TODO(bill): Better error messages or is this good enough?
 		if (rhs_count == 0 && vd->type == NULL) {
@@ -685,9 +689,8 @@ void check_var_decl_node(Checker *c, AstNode *node) {
 			error(&c->error_collector, ast_node_token(node), "Extra initial expression");
 		}
 
-		AstNode *name = vd->name_list;
-		for (isize i = 0; i < entity_count; i++, name = name->next) {
-			add_entity(c, c->context.scope, name, entities[i]);
+		gb_for_array(i, vd->names) {
+			add_entity(c, c->context.scope, vd->names[i], entities[i]);
 		}
 	} break;
 
@@ -769,7 +772,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		switch (as->op.kind) {
 		case Token_Eq: {
 			// a, b, c = 1, 2, 3;  // Multisided
-			if (as->lhs_count == 0) {
+			if (gb_array_count(as->lhs) == 0) {
 				error(&c->error_collector, as->op, "Missing lhs in assignment statement");
 				return;
 			}
@@ -779,7 +782,8 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 			gb_array_init(operands, gb_heap_allocator());
 			defer (gb_array_free(operands));
 
-			for (AstNode *rhs = as->rhs_list; rhs != NULL; rhs = rhs->next) {
+			gb_for_array(i, as->rhs) {
+				AstNode *rhs = as->rhs[i];
 				Operand o = {};
 				check_multi_expr(c, &o, rhs);
 				if (o.type->kind != Type_Tuple) {
@@ -793,25 +797,23 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 				}
 			}
 
-			isize lhs_count = as->lhs_count;
+			isize lhs_count = gb_array_count(as->lhs);
 			isize rhs_count = gb_array_count(operands);
 
 			isize operand_index = 0;
-			for (AstNode *lhs = as->lhs_list;
-			     lhs != NULL;
-			     lhs = lhs->next, operand_index++) {
-				check_assignment_variable(c, &operands[operand_index], lhs);
-
+			gb_for_array(i, as->lhs) {
+				AstNode *lhs = as->lhs[i];
+				check_assignment_variable(c, &operands[i], lhs);
 			}
 			if (lhs_count != rhs_count) {
-				error(&c->error_collector, ast_node_token(as->lhs_list), "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
+				error(&c->error_collector, ast_node_token(as->lhs[0]), "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
 			}
 		} break;
 
 		default: {
 			// a += 1; // Single-sided
 			Token op = as->op;
-			if (as->lhs_count != 1 || as->rhs_count != 1) {
+			if (gb_array_count(as->lhs) != 1 || gb_array_count(as->rhs) != 1) {
 				error(&c->error_collector, op, "Assignment operation `%.*s` requires single-valued expressions", LIT(op.string));
 				return;
 			}
@@ -825,21 +827,21 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 			ast_node(be, BinaryExpr, &binary_expr);
 			be->op    = op;
 			 // NOTE(bill): Only use the first one will be used
-			be->left  = as->lhs_list;
-			be->right = as->rhs_list;
+			be->left  = as->lhs[0];
+			be->right = as->rhs[0];
 
 			check_binary_expr(c, &operand, &binary_expr);
 			if (operand.mode == Addressing_Invalid)
 				return;
 			// NOTE(bill): Only use the first one will be used
-			check_assignment_variable(c, &operand, as->lhs_list);
+			check_assignment_variable(c, &operand, as->lhs[0]);
 		} break;
 		}
 	case_end;
 
 	case_ast_node(bs, BlockStmt, node);
 		check_open_scope(c, node);
-		check_stmt_list(c, bs->list, bs->list_count, mod_flags);
+		check_stmt_list(c, bs->stmts, mod_flags);
 		check_close_scope(c);
 	case_end;
 
@@ -887,15 +889,15 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		isize result_count = 0;
 		if (proc_type->Proc.results)
 			result_count = proc_type->Proc.results->Tuple.variable_count;
-		if (result_count != rs->result_count) {
+		if (result_count != gb_array_count(rs->results)) {
 			error(&c->error_collector, rs->token, "Expected %td return %s, got %td",
 			      result_count,
 			      (result_count != 1 ? "values" : "value"),
-			      rs->result_count);
+			      gb_array_count(rs->results));
 		} else if (result_count > 0) {
 			auto *tuple = &proc_type->Proc.results->Tuple;
 			check_init_variables(c, tuple->variables, tuple->variable_count,
-			                     rs->result_list, rs->result_count, make_string("return statement"));
+			                     rs->results, make_string("return statement"));
 		}
 	case_end;
 
@@ -947,11 +949,12 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		// NOTE(bill): Check for multiple defaults
 		AstNode *first_default = NULL;
 		ast_node(bs, BlockStmt, ms->body);
-		for (AstNode *stmt = bs->list; stmt != NULL; stmt = stmt->next) {
+		gb_for_array(i, bs->stmts) {
+			AstNode *stmt = bs->stmts[i];
 			AstNode *default_stmt = NULL;
 			if (stmt->kind == AstNode_CaseClause) {
 				ast_node(c, CaseClause, stmt);
-				if (c->list_count == 0) {
+				if (gb_array_count(c->list) == 0) {
 					default_stmt = stmt;
 				}
 			} else {
@@ -979,8 +982,8 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		Map<TypeAndToken> seen = {}; // Multimap
 		map_init(&seen, gb_heap_allocator());
 		defer (map_destroy(&seen));
-		isize i = 0;
-		for (AstNode *stmt = bs->list; stmt != NULL; stmt = stmt->next) {
+		gb_for_array(i, bs->stmts) {
+			AstNode *stmt = bs->stmts[i];
 			if (stmt->kind != AstNode_CaseClause) {
 				// NOTE(bill): error handled by above multiple default checker
 				continue;
@@ -988,7 +991,8 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 			ast_node(cc, CaseClause, stmt);
 
 
-			for (AstNode *expr = cc->list; expr != NULL; expr = expr->next) {
+			gb_for_array(j, cc->list) {
+				AstNode *expr = cc->list[j];
 				Operand y = {};
 				Operand z = {};
 				Token eq = {Token_CmpEq};
@@ -1051,12 +1055,11 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 
 			check_open_scope(c, stmt);
 			u32 ft_flags = mod_flags;
-			if (i+1 < bs->list_count) {
+			if (i+1 < gb_array_count(bs->stmts)) {
 				ft_flags |= Stmt_FallthroughAllowed;
 			}
-			check_stmt_list(c, cc->stmts, cc->stmt_count, ft_flags);
+			check_stmt_list(c, cc->stmts, ft_flags);
 			check_close_scope(c);
-			i++;
 		}
 	case_end;
 
@@ -1083,11 +1086,12 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		// NOTE(bill): Check for multiple defaults
 		AstNode *first_default = NULL;
 		ast_node(bs, BlockStmt, ms->body);
-		for (AstNode *stmt = bs->list; stmt != NULL; stmt = stmt->next) {
+		gb_for_array(i, bs->stmts) {
+			AstNode *stmt = bs->stmts[i];
 			AstNode *default_stmt = NULL;
 			if (stmt->kind == AstNode_CaseClause) {
 				ast_node(c, CaseClause, stmt);
-				if (c->list_count == 0) {
+				if (gb_array_count(c->list) == 0) {
 					default_stmt = stmt;
 				}
 			} else {
@@ -1116,15 +1120,15 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		defer (map_destroy(&seen));
 
 
-
-		for (AstNode *stmt = bs->list; stmt != NULL; stmt = stmt->next) {
+		gb_for_array(i, bs->stmts) {
+			AstNode *stmt = bs->stmts[i];
 			if (stmt->kind != AstNode_CaseClause) {
 				// NOTE(bill): error handled by above multiple default checker
 				continue;
 			}
 			ast_node(cc, CaseClause, stmt);
 
-			AstNode *type_expr = cc->list;
+			AstNode *type_expr = cc->list[0];
 			Type *tag_type = NULL;
 			if (type_expr != NULL) { // Otherwise it's a default expression
 				Operand y = {};
@@ -1171,7 +1175,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 				Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, ms->var->Ident, tag_ptr_type);
 				add_entity(c, c->context.scope, ms->var, tag_var);
 			}
-			check_stmt_list(c, cc->stmts, cc->stmt_count, mod_flags);
+			check_stmt_list(c, cc->stmts, mod_flags);
 			check_close_scope(c);
 		}
 	case_end;
@@ -1311,12 +1315,13 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		case_end;
 
 		case_ast_node(vd, VarDecl, us->node);
-			if (vd->name_count > 1 && vd->type != NULL) {
+			if (gb_array_count(vd->names) > 1 && vd->type != NULL) {
 				error(&c->error_collector, us->token, "`using` can only be applied to one variable of the same type");
 			}
 			check_var_decl_node(c, us->node);
 
-			for (AstNode *item = vd->name_list; item != NULL; item = item->next) {
+			gb_for_array(name_index, vd->names) {
+				AstNode *item = vd->names[name_index];
 				ast_node(i, Ident, item);
 				String name = i->string;
 				Entity *e = scope_lookup_entity(c, c->context.scope, name);

+ 103 - 117
src/codegen/ssa.cpp

@@ -1792,9 +1792,8 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 		case Type_Vector: {
 			isize index = 0;
 			ssaValue *result = ssa_emit_load(proc, v);
-			for (AstNode *elem = cl->elem_list;
-				elem != NULL;
-				elem = elem->next, index++) {
+			for (; index < gb_array_count(cl->elems); index++) {
+				AstNode *elem = cl->elems[index];
 				ssaValue *field_elem = ssa_build_expr(proc, elem);
 				Type *t = ssa_type(field_elem);
 				GB_ASSERT(t->kind != Type_Tuple);
@@ -1819,13 +1818,9 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 		case Type_Record: {
 			GB_ASSERT(is_type_struct(base_type));
 			auto *st = &base_type->Record;
-			if (cl->elem_list != NULL) {
-				isize index = 0;
-				AstNode *elem = cl->elem_list;
-				for (;
-				     elem != NULL;
-				     elem = elem->next, index++) {
-					isize field_index = index;
+			if (cl->elems != NULL && gb_array_count(cl->elems) > 0) {
+				gb_for_array(field_index, cl->elems) {
+					AstNode *elem = cl->elems[field_index];
 					ssaValue *field_expr = NULL;
 					Entity *field = NULL;
 
@@ -1850,32 +1845,29 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 			}
 		} break;
 		case Type_Array: {
-			isize index = 0;
-			for (AstNode *elem = cl->elem_list;
-				elem != NULL;
-				elem = elem->next, index++) {
+			gb_for_array(i, cl->elems) {
+				AstNode *elem = cl->elems[i];
 				ssaValue *field_expr = ssa_build_expr(proc, elem);
 				Type *t = ssa_type(field_expr);
 				GB_ASSERT(t->kind != Type_Tuple);
 				ssaValue *ev = ssa_emit_conv(proc, field_expr, et);
-				ssaValue *gep = ssa_emit_struct_gep(proc, v, index, et);
+				ssaValue *gep = ssa_emit_struct_gep(proc, v, i, et);
 				ssa_emit_store(proc, gep, ev);
 			}
 		} break;
 		case Type_Slice: {
-			i64 count = cl->elem_count;
+			i64 count = gb_array_count(cl->elems);
 			Type *elem_type = base_type->Slice.elem;
 			Type *elem_ptr_type = make_type_pointer(proc->module->allocator, elem_type);
 			ssaValue *array = ssa_add_local_generated(proc, make_type_array(proc->module->allocator, elem_type, count));
-			isize index = 0;
-			for (AstNode *elem = cl->elem_list;
-				elem != NULL;
-				elem = elem->next, index++) {
+
+			gb_for_array(i, cl->elems) {
+				AstNode *elem = cl->elems[i];
 				ssaValue *field_expr = ssa_build_expr(proc, elem);
 				Type *t = ssa_type(field_expr);
 				GB_ASSERT(t->kind != Type_Tuple);
 				ssaValue *ev = ssa_emit_conv(proc, field_expr, elem_type);
-				ssaValue *gep = ssa_emit_struct_gep(proc, array, index, elem_ptr_type);
+				ssaValue *gep = ssa_emit_struct_gep(proc, array, i, elem_ptr_type);
 				ssa_emit_store(proc, gep, ev);
 			}
 
@@ -1906,7 +1898,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					// new :: proc(Type) -> ^Type
 					gbAllocator allocator = proc->module->allocator;
 
-					Type *type = type_of_expr(proc->module->info, ce->arg_list);
+					Type *type = type_of_expr(proc->module->info, ce->args[0]);
 					Type *ptr_type = make_type_pointer(allocator, type);
 
 					i64 s = type_size_of(proc->module->sizes, allocator, type);
@@ -1925,7 +1917,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					// new_slice :: proc(Type, len: int[, cap: int]) -> ^Type
 					gbAllocator allocator = proc->module->allocator;
 
-					Type *type = type_of_expr(proc->module->info, ce->arg_list);
+					Type *type = type_of_expr(proc->module->info, ce->args[0]);
 					Type *ptr_type = make_type_pointer(allocator, type);
 					Type *slice_type = make_type_slice(allocator, type);
 
@@ -1935,13 +1927,10 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					ssaValue *elem_size  = ssa_make_value_constant(allocator, t_int, make_exact_value_integer(s));
 					ssaValue *elem_align = ssa_make_value_constant(allocator, t_int, make_exact_value_integer(a));
 
-					AstNode *len_node = ce->arg_list->next;
-					AstNode *cap_node = len_node->next;
-
-					ssaValue *len = ssa_build_expr(proc, len_node);
+					ssaValue *len = ssa_build_expr(proc, ce->args[1]);
 					ssaValue *cap = len;
-					if (cap_node != NULL) {
-						cap = ssa_build_expr(proc, cap_node);
+					if (gb_array_count(ce->args) == 3) {
+						cap = ssa_build_expr(proc, ce->args[2]);
 					}
 
 					Token mul = {Token_Mul};
@@ -1966,7 +1955,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					// delete :: proc(slice: []Type)
 					gbAllocator allocator = proc->module->allocator;
 
-					ssaValue *value = ssa_build_expr(proc, ce->arg_list);
+					ssaValue *value = ssa_build_expr(proc, ce->args[0]);
 
 					if (is_type_slice(ssa_type(value))) {
 						Type *etp = get_base_type(ssa_type(value));
@@ -1982,7 +1971,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 
 				case BuiltinProc_assert: {
 					ssa_emit_comment(proc, make_string("assert"));
-					ssaValue *cond = ssa_build_expr(proc, ce->arg_list);
+					ssaValue *cond = ssa_build_expr(proc, ce->args[0]);
 					GB_ASSERT(is_type_boolean(ssa_type(cond)));
 
 					Token eq = {Token_CmpEq};
@@ -1993,9 +1982,9 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					ssa_emit_if(proc, cond, err, done);
 					proc->curr_block = err;
 
-					Token token = ast_node_token(ce->arg_list);
+					Token token = ast_node_token(ce->args[0]);
 					TokenPos pos = token.pos;
-					gbString expr = expr_to_string(ce->arg_list);
+					gbString expr = expr_to_string(ce->args[0]);
 					defer (gb_string_free(expr));
 
 					isize err_len = pos.file.len + 1 + 10 + 1 + 10 + 1;
@@ -2029,7 +2018,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					ssa_emit_comment(proc, make_string("len"));
 					// len :: proc(v: Type) -> int
 					// NOTE(bill): len of an array is a constant expression
-					ssaValue *v = ssa_build_expr(proc, ce->arg_list);
+					ssaValue *v = ssa_build_expr(proc, ce->args[0]);
 					Type *t = get_base_type(ssa_type(v));
 					if (t == t_string)
 						return ssa_string_len(proc, v);
@@ -2040,15 +2029,15 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					ssa_emit_comment(proc, make_string("cap"));
 					// cap :: proc(v: Type) -> int
 					// NOTE(bill): cap of an array is a constant expression
-					ssaValue *v = ssa_build_expr(proc, ce->arg_list);
+					ssaValue *v = ssa_build_expr(proc, ce->args[0]);
 					Type *t = get_base_type(ssa_type(v));
 					return ssa_slice_cap(proc, v);
 				} break;
 				case BuiltinProc_copy: {
 					ssa_emit_comment(proc, make_string("copy"));
 					// copy :: proc(dst, src: []Type) -> int
-					AstNode *dst_node = ce->arg_list;
-					AstNode *src_node = ce->arg_list->next;
+					AstNode *dst_node = ce->args[0];
+					AstNode *src_node = ce->args[1];
 					ssaValue *dst_slice = ssa_build_expr(proc, dst_node);
 					ssaValue *src_slice = ssa_build_expr(proc, src_node);
 					Type *slice_type = get_base_type(ssa_type(dst_slice));
@@ -2083,8 +2072,8 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 				case BuiltinProc_append: {
 					ssa_emit_comment(proc, make_string("append"));
 					// append :: proc(s: ^[]Type, item: Type) -> bool
-					AstNode *sptr_node = ce->arg_list;
-					AstNode *item_node = ce->arg_list->next;
+					AstNode *sptr_node = ce->args[0];
+					AstNode *item_node = ce->args[1];
 					ssaValue *slice_ptr = ssa_build_expr(proc, sptr_node);
 					ssaValue *slice = ssa_emit_load(proc, slice_ptr);
 
@@ -2143,16 +2132,17 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 
 				case BuiltinProc_swizzle: {
 					ssa_emit_comment(proc, make_string("swizzle"));
-					ssaValue *vector = ssa_build_expr(proc, ce->arg_list);
-					isize index_count = ce->arg_list_count-1;
+					ssaValue *vector = ssa_build_expr(proc, ce->args[0]);
+					isize index_count = gb_array_count(ce->args)-1;
 					if (index_count == 0) {
 						return vector;
 					}
 
 					i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count);
 					isize index = 0;
-					for (AstNode *arg = ce->arg_list->next; arg != NULL; arg = arg->next) {
-						TypeAndValue *tv = type_and_value_of_expression(proc->module->info, arg);
+					gb_for_array(i, ce->args) {
+						if (i == 0) continue;
+						TypeAndValue *tv = type_and_value_of_expression(proc->module->info, ce->args[i]);
 						GB_ASSERT(is_type_integer(tv->type));
 						GB_ASSERT(tv->value.kind == ExactValue_Integer);
 						indices[index++] = cast(i32)tv->value.value_integer;
@@ -2164,15 +2154,15 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 
 				case BuiltinProc_ptr_offset: {
 					ssa_emit_comment(proc, make_string("ptr_offset"));
-					ssaValue *ptr = ssa_build_expr(proc, ce->arg_list);
-					ssaValue *offset = ssa_build_expr(proc, ce->arg_list->next);
+					ssaValue *ptr = ssa_build_expr(proc, ce->args[0]);
+					ssaValue *offset = ssa_build_expr(proc, ce->args[1]);
 					return ssa_emit_ptr_offset(proc, ptr, offset);
 				} break;
 
 				case BuiltinProc_ptr_sub: {
 					ssa_emit_comment(proc, make_string("ptr_sub"));
-					ssaValue *ptr_a = ssa_build_expr(proc, ce->arg_list);
-					ssaValue *ptr_b = ssa_build_expr(proc, ce->arg_list->next);
+					ssaValue *ptr_a = ssa_build_expr(proc, ce->args[0]);
+					ssaValue *ptr_b = ssa_build_expr(proc, ce->args[1]);
 					Type *ptr_type = get_base_type(ssa_type(ptr_a));
 					GB_ASSERT(ptr_type->kind == Type_Pointer);
 					isize elem_size = type_size_of(proc->module->sizes, proc->module->allocator, ptr_type->Pointer.elem);
@@ -2190,14 +2180,14 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 
 				case BuiltinProc_slice_ptr: {
 					ssa_emit_comment(proc, make_string("slice_ptr"));
-					ssaValue *ptr = ssa_build_expr(proc, ce->arg_list);
-					ssaValue *len = ssa_build_expr(proc, ce->arg_list->next);
+					ssaValue *ptr = ssa_build_expr(proc, ce->args[0]);
+					ssaValue *len = ssa_build_expr(proc, ce->args[1]);
 					ssaValue *cap = len;
 
 					len = ssa_emit_conv(proc, len, t_int, true);
 
-					if (ce->arg_list->next->next != NULL) {
-						cap = ssa_build_expr(proc, ce->arg_list->next->next);
+					if (gb_array_count(ce->args) == 3) {
+						cap = ssa_build_expr(proc, ce->args[2]);
 						cap = ssa_emit_conv(proc, cap, t_int, true);
 					}
 
@@ -2212,8 +2202,8 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 
 				case BuiltinProc_min: {
 					ssa_emit_comment(proc, make_string("min"));
-					ssaValue *x = ssa_build_expr(proc, ce->arg_list);
-					ssaValue *y = ssa_build_expr(proc, ce->arg_list->next);
+					ssaValue *x = ssa_build_expr(proc, ce->args[0]);
+					ssaValue *y = ssa_build_expr(proc, ce->args[1]);
 					Type *t = get_base_type(ssa_type(x));
 					Token lt = {Token_Lt};
 					ssaValue *cond = ssa_emit_comp(proc, lt, x, y);
@@ -2222,8 +2212,8 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 
 				case BuiltinProc_max: {
 					ssa_emit_comment(proc, make_string("max"));
-					ssaValue *x = ssa_build_expr(proc, ce->arg_list);
-					ssaValue *y = ssa_build_expr(proc, ce->arg_list->next);
+					ssaValue *x = ssa_build_expr(proc, ce->args[0]);
+					ssaValue *y = ssa_build_expr(proc, ce->args[1]);
 					Type *t = get_base_type(ssa_type(x));
 					Token gt = {Token_Gt};
 					ssaValue *cond = ssa_emit_comp(proc, gt, x, y);
@@ -2235,7 +2225,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					Token lt = {Token_Lt};
 					Token sub = {Token_Sub};
 
-					ssaValue *x = ssa_build_expr(proc, ce->arg_list);
+					ssaValue *x = ssa_build_expr(proc, ce->args[0]);
 					Type *t = ssa_type(x);
 
 					ssaValue *neg_x = ssa_emit_arith(proc, sub, v_zero, x, t);
@@ -2248,8 +2238,8 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					ssaValue **found = map_get(&proc->module->members, hash_string(make_string("__type_info_data")));
 					GB_ASSERT(found != NULL);
 					ssaValue *type_info_data = *found;
-					ssaValue *x = ssa_build_expr(proc, ce->arg_list);
-					Type *t = default_type(type_of_expr(proc->module->info, ce->arg_list));
+					ssaValue *x = ssa_build_expr(proc, ce->args[0]);
+					Type *t = default_type(type_of_expr(proc->module->info, ce->args[0]));
 					MapFindResult fr = map__find(&proc->module->info->type_info_types, hash_pointer(t));
 					GB_ASSERT(fr.entry_index >= 0);
 					// Zero is null and void
@@ -2272,7 +2262,8 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 		isize arg_index = 0;
 
 		isize arg_count = 0;
-		for (AstNode *a = ce->arg_list; a != NULL; a = a->next) {
+		gb_for_array(i, ce->args) {
+			AstNode *a = ce->args[i];
 			Type *at = get_base_type(type_of_expr(proc->module->info, a));
 			if (at->kind == Type_Tuple) {
 				arg_count += at->Tuple.variable_count;
@@ -2283,11 +2274,8 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 		ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, arg_count);
 		b32 variadic = proc_type_->Proc.variadic;
 
-		AstNode *arg = ce->arg_list;
-		for (;
-		     arg != NULL;
-		     arg = arg->next) {
-			ssaValue *a = ssa_build_expr(proc, arg);
+		gb_for_array(i, ce->args) {
+			ssaValue *a = ssa_build_expr(proc, ce->args[i]);
 			Type *at = ssa_type(a);
 			if (at->kind == Type_Tuple) {
 				for (isize i = 0; i < at->Tuple.variable_count; i++) {
@@ -2732,9 +2720,10 @@ void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name) {
 
 
 
-void ssa_build_stmt_list(ssaProcedure *proc, AstNode *list) {
-	for (AstNode *stmt = list ; stmt != NULL; stmt = stmt->next)
-		ssa_build_stmt(proc, stmt);
+void ssa_build_stmt_list(ssaProcedure *proc, AstNodeArray stmts) {
+	gb_for_array(i, stmts) {
+		ssa_build_stmt(proc, stmts[i]);
+	}
 }
 
 void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
@@ -2751,15 +2740,16 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 	case_ast_node(vd, VarDecl, node);
 		if (vd->kind == Declaration_Mutable) {
-			if (vd->name_count == vd->value_count) { // 1:1 assigment
+			if (gb_array_count(vd->names) == gb_array_count(vd->values)) { // 1:1 assigment
 				gbArray(ssaAddr)  lvals;
 				gbArray(ssaValue *) inits;
-				gb_array_init_reserve(lvals, gb_heap_allocator(), vd->name_count);
-				gb_array_init_reserve(inits, gb_heap_allocator(), vd->name_count);
+				gb_array_init_reserve(lvals, gb_heap_allocator(), gb_array_count(vd->names));
+				gb_array_init_reserve(inits, gb_heap_allocator(), gb_array_count(vd->names));
 				defer (gb_array_free(lvals));
 				defer (gb_array_free(inits));
 
-				for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
+				gb_for_array(i, vd->names) {
+					AstNode *name = vd->names[i];
 					ssaAddr lval = ssa_make_addr(NULL, NULL);
 					if (!ssa_is_blank_ident(name)) {
 						ssa_add_local_for_identifier(proc, name, false);
@@ -2769,9 +2759,8 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 					gb_array_append(lvals, lval);
 				}
-
-				for (AstNode *value = vd->value_list; value != NULL; value = value->next) {
-					ssaValue *init = ssa_build_expr(proc, value);
+				gb_for_array(i, vd->values) {
+					ssaValue *init = ssa_build_expr(proc, vd->values[i]);
 					gb_array_append(inits, init);
 				}
 
@@ -2781,8 +2770,9 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 					ssa_lvalue_store(proc, lvals[i], v);
 				}
 
-			} else if (vd->value_count == 0) { // declared and zero-initialized
-				for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
+			} else if (gb_array_count(vd->values) == 0) { // declared and zero-initialized
+				gb_for_array(i, vd->names) {
+					AstNode *name = vd->names[i];
 					if (!ssa_is_blank_ident(name)) {
 						ssa_add_local_for_identifier(proc, name, true);
 					}
@@ -2790,12 +2780,13 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 			} else { // Tuple(s)
 				gbArray(ssaAddr)  lvals;
 				gbArray(ssaValue *) inits;
-				gb_array_init_reserve(lvals, gb_heap_allocator(), vd->name_count);
-				gb_array_init_reserve(inits, gb_heap_allocator(), vd->name_count);
+				gb_array_init_reserve(lvals, gb_heap_allocator(), gb_array_count(vd->names));
+				gb_array_init_reserve(inits, gb_heap_allocator(), gb_array_count(vd->names));
 				defer (gb_array_free(lvals));
 				defer (gb_array_free(inits));
 
-				for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
+				gb_for_array(i, vd->names) {
+					AstNode *name = vd->names[i];
 					ssaAddr lval = ssa_make_addr(NULL, NULL);
 					if (!ssa_is_blank_ident(name)) {
 						ssa_add_local_for_identifier(proc, name, false);
@@ -2805,8 +2796,8 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 					gb_array_append(lvals, lval);
 				}
 
-				for (AstNode *value = vd->value_list; value != NULL; value = value->next) {
-					ssaValue *init = ssa_build_expr(proc, value);
+				gb_for_array(i, vd->values) {
+					ssaValue *init = ssa_build_expr(proc, vd->values[i]);
 					Type *t = ssa_type(init);
 					if (t->kind == Type_Tuple) {
 						for (isize i = 0; i < t->Tuple.variable_count; i++) {
@@ -2923,9 +2914,8 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 			gb_array_init(lvals, gb_heap_allocator());
 			defer (gb_array_free(lvals));
 
-			for (AstNode *lhs = as->lhs_list;
-			     lhs != NULL;
-			     lhs = lhs->next) {
+			gb_for_array(i, as->lhs) {
+				AstNode *lhs = as->lhs[i];
 				ssaAddr lval = {};
 				if (!ssa_is_blank_ident(lhs)) {
 					lval = ssa_build_addr(proc, lhs);
@@ -2933,9 +2923,9 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 				gb_array_append(lvals, lval);
 			}
 
-			if (as->lhs_count == as->rhs_count) {
-				if (as->lhs_count == 1) {
-					AstNode *rhs = as->rhs_list;
+			if (gb_array_count(as->lhs) == gb_array_count(as->rhs)) {
+				if (gb_array_count(as->lhs) == 1) {
+					AstNode *rhs = as->rhs[0];
 					ssaValue *init = ssa_build_expr(proc, rhs);
 					ssa_lvalue_store(proc, lvals[0], init);
 				} else {
@@ -2943,8 +2933,8 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 					gb_array_init_reserve(inits, gb_heap_allocator(), gb_array_count(lvals));
 					defer (gb_array_free(inits));
 
-					for (AstNode *rhs = as->rhs_list; rhs != NULL; rhs = rhs->next) {
-						ssaValue *init = ssa_build_expr(proc, rhs);
+					gb_for_array(i, as->rhs) {
+						ssaValue *init = ssa_build_expr(proc, as->rhs[i]);
 						gb_array_append(inits, init);
 					}
 
@@ -2957,8 +2947,8 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 				gb_array_init_reserve(inits, gb_heap_allocator(), gb_array_count(lvals));
 				defer (gb_array_free(inits));
 
-				for (AstNode *rhs = as->rhs_list; rhs != NULL; rhs = rhs->next) {
-					ssaValue *init = ssa_build_expr(proc, rhs);
+				gb_for_array(i, as->rhs) {
+					ssaValue *init = ssa_build_expr(proc, as->rhs[i]);
 					Type *t = ssa_type(init);
 					// TODO(bill): refactor for code reuse as this is repeated a bit
 					if (t->kind == Type_Tuple) {
@@ -2986,8 +2976,8 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 			i32 kind = op.kind;
 			kind += Token_Add - Token_AddEq; // Convert += to +
 			op.kind = cast(TokenKind)kind;
-			ssaAddr lhs = ssa_build_addr(proc, as->lhs_list);
-			ssaValue *value = ssa_build_expr(proc, as->rhs_list);
+			ssaAddr lhs = ssa_build_addr(proc, as->lhs[0]);
+			ssaValue *value = ssa_build_expr(proc, as->rhs[0]);
 			ssa_build_assign_op(proc, lhs, value, op);
 		} break;
 		}
@@ -3000,7 +2990,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 	case_ast_node(bs, BlockStmt, node);
 		proc->scope_index++;
-		ssa_build_stmt_list(proc, bs->list);
+		ssa_build_stmt_list(proc, bs->stmts);
 		ssa_emit_defer_stmts(proc, ssaDefer_Default, NULL);
 		proc->scope_index--;
 	case_end;
@@ -3019,24 +3009,20 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 		ssaValue *v = NULL;
 		auto *return_type_tuple  = &proc->type->Proc.results->Tuple;
 		isize return_count = proc->type->Proc.result_count;
-		if (rs->result_count == 1 && return_count > 1) {
+		if (gb_array_count(rs->results) == 1 && return_count > 1) {
 			GB_PANIC("ReturnStmt tuple return statement");
 		} else if (return_count == 1) {
 			Entity *e = return_type_tuple->variables[0];
-			v = ssa_emit_conv(proc, ssa_build_expr(proc, rs->result_list), e->type);
+			v = ssa_emit_conv(proc, ssa_build_expr(proc, rs->results[0]), e->type);
 		} else if (return_count == 0) {
 			// No return values
 		} else {
 			// 1:1 multiple return values
 			Type *ret_type = proc->type->Proc.results;
 			v = ssa_add_local_generated(proc, ret_type);
-			isize i = 0;
-			AstNode *r = rs->result_list;
-			for (;
-			     i < return_count && r != NULL;
-			     i++, r = r->next) {
+			gb_for_array(i, rs->results) {
 				Entity *e = return_type_tuple->variables[i];
-				ssaValue *res = ssa_emit_conv(proc, ssa_build_expr(proc, r), e->type);
+				ssaValue *res = ssa_emit_conv(proc, ssa_build_expr(proc, rs->results[i]), e->type);
 				ssaValue *field = ssa_emit_struct_gep(proc, v, i, e->type);
 				ssa_emit_store(proc, field, res);
 			}
@@ -3150,18 +3136,16 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 		ast_node(body, BlockStmt, ms->body);
 
 
-		AstNode *default_stmts = NULL;
+		AstNodeArray default_stmts = NULL;
 		ssaBlock *default_fall = NULL;
 		ssaBlock *default_block = NULL;
 
 		ssaBlock *fall = NULL;
 		b32 append_fall = false;
 
-		isize case_count = body->list_count;
-		isize i = 0;
-		for (AstNode *clause = body->list;
-		     clause != NULL;
-		     clause = clause->next, i++) {
+		isize case_count = gb_array_count(body->stmts);
+		gb_for_array(i, body->stmts) {
+			AstNode *clause = body->stmts[i];
 			ssaBlock *body = fall;
 			b32 append_body = false;
 
@@ -3170,7 +3154,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 			if (body == NULL) {
 				append_body = true;
-				if (cc->list == NULL) {
+				if (gb_array_count(cc->list)) {
 					body = ssa__make_block(proc, clause, make_string("match.dflt.body"));
 				} else {
 					body = ssa__make_block(proc, clause, make_string("match.case.body"));
@@ -3187,7 +3171,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 				fall = ssa__make_block(proc, clause, make_string("match.fall.body"));
 			}
 
-			if (cc->list == NULL) {
+			if (gb_array_count(cc->list)) {
 				// default case
 				default_stmts  = cc->stmts;
 				default_fall  = fall;
@@ -3197,7 +3181,8 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 			ssaBlock *next_cond = NULL;
 			Token eq = {Token_CmpEq};
-			for (AstNode *expr = cc->list; expr != NULL; expr = expr->next) {
+			gb_for_array(j, cc->list) {
+				AstNode *expr = cc->list[j];
 				next_cond = ssa__make_block(proc, clause, make_string("match.case.next"));
 
 				ssaValue *cond = ssa_emit_comp(proc, eq, tag, ssa_build_expr(proc, expr));
@@ -3256,16 +3241,17 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 		String tag_var_name = ms->var->Ident.string;
 
-		AstNode *default_stmts = NULL;
+		AstNodeArray default_stmts = NULL;
 		ssaBlock *default_block = NULL;
 
-		isize case_count = body->list_count;
-		for (AstNode *clause = body->list; clause != NULL; clause = clause->next) {
+		isize case_count = gb_array_count(body->stmts);
+		gb_for_array(i, body->stmts) {
+			AstNode *clause = body->stmts[i];
 			ast_node(cc, CaseClause, clause);
 
-			if (cc->list == NULL) {
+			if (gb_array_count(cc->list) == 0) {
 				// default case
-				default_stmts  = cc->stmts;
+				default_stmts = cc->stmts;
 				default_block = ssa__make_block(proc, clause, make_string("type-match.dflt.body"));
 				continue;
 			}
@@ -3276,7 +3262,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 
 			Scope *scope = *map_get(&proc->module->info->scopes, hash_pointer(clause));
 			Entity *tag_var_entity = current_scope_lookup_entity(scope, tag_var_name);
-			GB_ASSERT(tag_var_entity != NULL);
+			GB_ASSERT_MSG(tag_var_entity != NULL, "%.*s", LIT(tag_var_name));
 			ssaValue *tag_var = ssa_add_local(proc, tag_var_entity);
 			ssaValue *data_ptr = ssa_emit_conv(proc, data, tag_var_entity->type);
 			ssa_emit_store(proc, tag_var, data_ptr);

+ 3 - 2
src/main.cpp

@@ -41,7 +41,7 @@ i32 win32_exec_command_line_app(char *fmt, ...) {
 }
 
 
-#if 0
+#if 1
 #define INIT_TIMER() u64 start_time, end_time = 0, total_time = 0; start_time = gb_utc_time_now()
 #define PRINT_TIMER(section) do { \
 	u64 diff; \
@@ -92,6 +92,7 @@ int main(int argc, char **argv) {
 
 	// print_ast(parser.files[0].decls, 0);
 
+#if 1
 	Checker checker = {};
 
 	init_checker(&checker, &parser);
@@ -101,7 +102,7 @@ int main(int argc, char **argv) {
 
 
 	PRINT_TIMER("Semantic Checker");
-
+#endif
 #if 1
 	ssaGen ssa = {};
 	if (!ssa_gen_init(&ssa, &checker))

File diff suppressed because it is too large
+ 192 - 236
src/parser.cpp


+ 43 - 18
src/printer.cpp

@@ -29,7 +29,9 @@ void print_ast(AstNode *node, isize indent) {
 		print_indent(indent);
 		gb_printf("(compound lit)\n");
 		print_ast(node->CompoundLit.type, indent+1);
-		print_ast(node->CompoundLit.elem_list, indent+1);
+		gb_for_array(i, node->CompoundLit.elems) {
+			print_ast(node->CompoundLit.elems[i], indent+1);
+		}
 		break;
 
 
@@ -56,7 +58,9 @@ void print_ast(AstNode *node, isize indent) {
 		print_indent(indent);
 		gb_printf("(call)\n");
 		print_ast(node->CallExpr.proc, indent+1);
-		print_ast(node->CallExpr.arg_list, indent+1);
+		gb_for_array(i, node->CallExpr.args) {
+			print_ast(node->CallExpr.args[i], indent+1);
+		}
 		break;
 	case AstNode_SelectorExpr:
 		print_indent(indent);
@@ -88,13 +92,19 @@ void print_ast(AstNode *node, isize indent) {
 	case AstNode_AssignStmt:
 		print_indent(indent);
 		print_token(node->AssignStmt.op);
-		print_ast(node->AssignStmt.lhs_list, indent+1);
-		print_ast(node->AssignStmt.rhs_list, indent+1);
+		gb_for_array(i, node->AssignStmt.lhs) {
+			print_ast(node->AssignStmt.lhs[i], indent+1);
+		}
+		gb_for_array(i, node->AssignStmt.rhs) {
+			print_ast(node->AssignStmt.rhs[i], indent+1);
+		}
 		break;
 	case AstNode_BlockStmt:
 		print_indent(indent);
 		gb_printf("(block)\n");
-		print_ast(node->BlockStmt.list, indent+1);
+		gb_for_array(i, node->BlockStmt.stmts) {
+			print_ast(node->BlockStmt.stmts[i], indent+1);
+		}
 		break;
 
 	case AstNode_IfStmt:
@@ -111,7 +121,9 @@ void print_ast(AstNode *node, isize indent) {
 	case AstNode_ReturnStmt:
 		print_indent(indent);
 		gb_printf("(return)\n");
-		print_ast(node->ReturnStmt.result_list, indent+1);
+		gb_for_array(i, node->ReturnStmt.results) {
+			print_ast(node->ReturnStmt.results[i], indent+1);
+		}
 		break;
 	case AstNode_ForStmt:
 		print_indent(indent);
@@ -130,13 +142,18 @@ void print_ast(AstNode *node, isize indent) {
 
 	case AstNode_VarDecl:
 		print_indent(indent);
-		if (node->VarDecl.kind == Declaration_Mutable)
+		if (node->VarDecl.kind == Declaration_Mutable) {
 			gb_printf("(decl:var,mutable)\n");
-		else if (node->VarDecl.kind == Declaration_Immutable)
+		} else if (node->VarDecl.kind == Declaration_Immutable) {
 			gb_printf("(decl:var,immutable)\n");
-		print_ast(node->VarDecl.name_list, indent+1);
+		}
+		gb_for_array(i, node->VarDecl.names) {
+			print_ast(node->VarDecl.names[i], indent+1);
+		}
 		print_ast(node->VarDecl.type, indent+1);
-		print_ast(node->VarDecl.value_list, indent+1);
+		gb_for_array(i, node->VarDecl.values) {
+			print_ast(node->VarDecl.values[i], indent+1);
+		}
 		break;
 	case AstNode_ProcDecl:
 		print_indent(indent);
@@ -154,16 +171,22 @@ void print_ast(AstNode *node, isize indent) {
 
 	case AstNode_ProcType:
 		print_indent(indent);
-		gb_printf("(type:proc)(%td -> %td)\n", node->ProcType.param_count, node->ProcType.result_count);
-		print_ast(node->ProcType.param_list, indent+1);
-		if (node->ProcType.result_list) {
+		gb_printf("(type:proc)(%td -> %td)\n", gb_array_count(node->ProcType.params), gb_array_count(node->ProcType.results));
+		gb_for_array(i, node->ProcType.params) {
+			print_ast(node->ProcType.params[i], indent+1);
+		}
+		if (gb_array_count(node->ProcType.results) > 0) {
 			print_indent(indent+1);
 			gb_printf("->\n");
-			print_ast(node->ProcType.result_list, indent+1);
+			gb_for_array(i, node->ProcType.results) {
+				print_ast(node->ProcType.results[i], indent+1);
+			}
 		}
 		break;
 	case AstNode_Field:
-		print_ast(node->Field.name_list, indent);
+		gb_for_array(i, node->Field.names) {
+			print_ast(node->Field.names[i], indent+1);
+		}
 		print_ast(node->Field.type, indent);
 		break;
 	case AstNode_PointerType:
@@ -180,10 +203,12 @@ void print_ast(AstNode *node, isize indent) {
 	case AstNode_StructType:
 		print_indent(indent);
 		gb_printf("(struct)\n");
-		print_ast(node->StructType.decl_list, indent+1);
+		gb_for_array(i, node->StructType.decls) {
+			print_ast(node->StructType.decls[i], indent+1);
+		}
 		break;
 	}
 
-	if (node->next)
-		print_ast(node->next, indent);
+	// if (node->next)
+		// print_ast(node->next, indent);
 }

Some files were not shown because too many files changed in this diff