Browse Source

`when` statement; Better entity collection system (for both local and global); Better parsing for record declarations

Ginger Bill 8 years ago
parent
commit
d9c686b53d
9 changed files with 307 additions and 263 deletions
  1. 9 20
      code/demo.odin
  2. 3 1
      core/opengl.odin
  3. 3 1
      core/os.odin
  4. 4 4
      core/win32.odin
  5. 23 16
      src/checker/checker.c
  6. 205 89
      src/checker/expr.c
  7. 4 64
      src/checker/stmt.c
  8. 1 16
      src/checker/types.c
  9. 55 52
      src/parser.c

+ 9 - 20
code/demo.odin

@@ -1,29 +1,18 @@
-// #import "fmt.odin"
+#import "fmt.odin"
 #import "utf8.odin"
 
-when ODIN_OS == "window" {
-	when ODIN_OS != "window" {
-	} else {
-		MAX :: 64
-	}
-	#import "fmt.odin"
-} else {
-
-}
-
-
 main :: proc() {
-	when true {
-		OffsetType :: type int
-	}
 
-	// MAX :: 64
 	buf:     [MAX]rune
 	backing: [MAX]byte
-	offset:  OffsetType
+	offset:  int
+
+	when MAX > 0 {
+		msg := "Hello"
+	}
 
+	MAX :: 64
 
-	msg := "Hello"
 	count := utf8.rune_count(msg)
 	assert(count <= MAX)
 	runes := buf[:count]
@@ -33,14 +22,14 @@ main :: proc() {
 		s := msg[offset:]
 		r, len := utf8.decode_rune(s)
 		runes[count-i-1] = r
-		offset += len as OffsetType
+		offset += len
 	}
 
 	offset = 0
 	for i := 0; i < count; i++ {
 		data, len := utf8.encode_rune(runes[i])
 		copy(backing[offset:], data[:len])
-		offset += len as OffsetType
+		offset += len
 	}
 
 	reverse := backing[:offset] as string

+ 3 - 1
core/opengl.odin

@@ -1,4 +1,6 @@
-#foreign_system_library "opengl32"
+when ODIN_OS == "windows" {
+	#foreign_system_library "opengl32"
+}
 #import "win32.odin"
 #load "opengl_constants.odin"
 

+ 3 - 1
core/os.odin

@@ -1,4 +1,6 @@
-#import "win32.odin"
+when ODIN_OS == "windows" {
+	#import "win32.odin"
+}
 #import "fmt.odin"
 
 File_Time :: type u64

+ 4 - 4
core/win32.odin

@@ -1,7 +1,7 @@
-#foreign_system_library "user32"
-#foreign_system_library "gdi32"
-
-_:= compile_assert(ODIN_OS == "windows")
+when ODIN_OS == "windows" {
+	#foreign_system_library "user32"
+	#foreign_system_library "gdi32"
+}
 
 HANDLE    :: type rawptr
 HWND      :: type HANDLE

+ 23 - 16
src/checker/checker.c

@@ -917,10 +917,12 @@ Type *const curr_procedure(Checker *c) {
 }
 
 void add_curr_ast_file(Checker *c, AstFile *file) {
-	TokenPos zero_pos = {0};
-	global_error_collector.prev = zero_pos;
-	c->curr_ast_file = file;
-	c->context.decl = file->decl_info;
+	if (file != NULL) {
+		TokenPos zero_pos = {0};
+		global_error_collector.prev = zero_pos;
+		c->curr_ast_file = file;
+		c->context.decl = file->decl_info;
+	}
 }
 
 
@@ -966,9 +968,6 @@ MapEntity generate_minimum_dependency_map(CheckerInfo *info, Entity *start) {
 	return map;
 }
 
-void check_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes, MapScope *file_scopes);
-
-
 
 #include "expr.c"
 #include "decl.c"
@@ -1072,6 +1071,7 @@ void check_global_entities_by_kind(Checker *c, EntityKind kind) {
 	}
 }
 
+void check_global_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes, MapScope *file_scopes);
 
 void check_global_when_stmt(Checker *c, Scope *parent_scope, AstNodeWhenStmt *ws, MapScope *file_scopes) {
 	Operand operand = {Addressing_Invalid};
@@ -1088,11 +1088,11 @@ void check_global_when_stmt(Checker *c, Scope *parent_scope, AstNodeWhenStmt *ws
 		if (operand.value.kind == ExactValue_Bool &&
 		    operand.value.value_bool == true) {
 			ast_node(body, BlockStmt, ws->body);
-			check_collect_entities(c, parent_scope, body->stmts, file_scopes);
+			check_global_collect_entities(c, parent_scope, body->stmts, file_scopes);
 		} else if (ws->else_stmt) {
 			switch (ws->else_stmt->kind) {
 			case AstNode_BlockStmt:
-				check_collect_entities(c, parent_scope, ws->else_stmt->BlockStmt.stmts, file_scopes);
+				check_global_collect_entities(c, parent_scope, ws->else_stmt->BlockStmt.stmts, file_scopes);
 				break;
 			case AstNode_WhenStmt:
 				check_global_when_stmt(c, parent_scope, &ws->else_stmt->WhenStmt, file_scopes);
@@ -1104,7 +1104,7 @@ void check_global_when_stmt(Checker *c, Scope *parent_scope, AstNodeWhenStmt *ws
 		}
 	}
 }
-void check_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes, MapScope *file_scopes) {
+void check_global_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes, MapScope *file_scopes) {
 	for_array(decl_index, nodes) {
 		AstNode *decl = nodes.e[decl_index];
 		if (!is_ast_node_decl(decl) && !is_ast_node_when_stmt(decl)) {
@@ -1242,9 +1242,8 @@ void check_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes,
 			try_add_foreign_library_path(c, file_str);
 		case_end;
 		case_ast_node(ws, WhenStmt, decl);
-			check_global_when_stmt(c, parent_scope, ws, file_scopes);
+			// Will be handled later
 		case_end;
-
 		case_ast_node(cd, ConstDecl, decl);
 			for_array(i, cd->values) {
 				AstNode *name = cd->names.e[i];
@@ -1267,7 +1266,6 @@ void check_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes,
 				error(ast_node_token(decl), "Extra initial expression");
 			}
 		case_end;
-
 		case_ast_node(vd, VarDecl, decl);
 			if (!parent_scope->is_file) {
 				// NOTE(bill): Within a procedure, variables must be in order
@@ -1308,7 +1306,6 @@ void check_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes,
 				add_entity_and_decl_info(c, name, e, d);
 			}
 		case_end;
-
 		case_ast_node(td, TypeDecl, decl);
 			ast_node(n, Ident, td->name);
 			Entity *e = make_entity_type_name(c->allocator, parent_scope, *n, NULL);
@@ -1317,7 +1314,6 @@ void check_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes,
 			d->type_expr = td->type;
 			add_entity_and_decl_info(c, td->name, e, d);
 		case_end;
-
 		case_ast_node(pd, ProcDecl, decl);
 			ast_node(n, Ident, pd->name);
 			Token token = *n;
@@ -1335,6 +1331,17 @@ void check_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes,
 			break;
 		}
 	}
+
+	// NOTE(bill): `when` stmts need to be handled after the other as the condition may refer to something
+	// declared after this stmt in source
+	for_array(decl_index, nodes) {
+		AstNode *decl = nodes.e[decl_index];
+		switch (decl->kind) {
+		case_ast_node(ws, WhenStmt, decl);
+			check_global_when_stmt(c, parent_scope, ws, file_scopes);
+		case_end;
+		}
+	}
 }
 
 
@@ -1371,7 +1378,7 @@ void check_parsed_files(Checker *c) {
 	for_array(i, c->parser->files) {
 		AstFile *f = &c->parser->files.e[i];
 		add_curr_ast_file(c, f);
-		check_collect_entities(c, f->scope, f->decls, &file_scopes);
+		check_global_collect_entities(c, f->scope, f->decls, &file_scopes);
 	}
 
 	check_global_entities_by_kind(c, Entity_TypeName);

+ 205 - 89
src/checker/expr.c

@@ -20,6 +20,206 @@ gb_inline Type *check_type(Checker *c, AstNode *expression) {
 
 
 
+
+typedef struct DelayedEntity {
+	Entity *entity;
+	DeclInfo *decl;
+} DelayedEntity;
+
+typedef struct DelayedOtherFields {
+	Entity **other_fields;
+	isize other_field_count;
+	isize other_field_index;
+
+	MapEntity *entity_map;
+} DelayedOtherFields;
+
+typedef Array(DelayedEntity) DelayedEntities;
+
+void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntities *delayed_entities, DelayedOtherFields *dof);
+
+void check_local_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, DelayedEntities *delayed_entities, DelayedOtherFields *dof) {
+	Operand operand = {Addressing_Invalid};
+	check_expr(c, &operand, ws->cond);
+	if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
+		error(ast_node_token(ws->cond), "Non-boolean condition in `when` statement");
+	}
+	if (operand.mode != Addressing_Constant) {
+		error(ast_node_token(ws->cond), "Non-constant condition in `when` statement");
+	}
+	if (ws->body == NULL || ws->body->kind != AstNode_BlockStmt) {
+		error(ast_node_token(ws->cond), "Invalid body for `when` statement");
+	} else {
+		if (operand.value.kind == ExactValue_Bool &&
+		    operand.value.value_bool) {
+			check_local_collect_entities(c, ws->body->BlockStmt.stmts, delayed_entities, dof);
+		} else if (ws->else_stmt) {
+			switch (ws->else_stmt->kind) {
+			case AstNode_BlockStmt:
+				check_local_collect_entities(c, ws->else_stmt->BlockStmt.stmts, delayed_entities, dof);
+				break;
+			case AstNode_WhenStmt:
+				check_local_collect_entities_from_when_stmt(c, &ws->else_stmt->WhenStmt, delayed_entities, dof);
+				break;
+			default:
+				error(ast_node_token(ws->else_stmt), "Invalid `else` statement in `when` statement");
+				break;
+			}
+		}
+	}
+}
+
+// NOTE(bill): The `dof` is for use within records
+void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntities *delayed_entities, DelayedOtherFields *dof) {
+	for_array(i, nodes) {
+		AstNode *node = nodes.e[i];
+		switch (node->kind) {
+		case_ast_node(ws, WhenStmt, node);
+			// Will be handled later
+		case_end;
+		case_ast_node(cd, ConstDecl, node);
+			gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
+
+			isize entity_count = cd->names.count;
+			isize entity_index = 0;
+			Entity **entities = gb_alloc_array(c->tmp_allocator, Entity *, entity_count);
+
+			for_array(i, cd->values) {
+				AstNode *name = cd->names.e[i];
+				AstNode *value = cd->values.e[i];
+				ExactValue v = {ExactValue_Invalid};
+
+				Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v);
+				e->identifier = name;
+				entities[entity_index++] = e;
+
+				DeclInfo *d = make_declaration_info(c->allocator, e->scope);
+				d->type_expr = cd->type;
+				d->init_expr = value;
+
+				add_entity_and_decl_info(c, name, e, d);
+
+				DelayedEntity delay = {e, d};
+				array_add(delayed_entities, delay);
+			}
+
+			isize lhs_count = cd->names.count;
+			isize rhs_count = cd->values.count;
+
+			// TODO(bill): Better error messages or is this good enough?
+			if (rhs_count == 0 && cd->type == NULL) {
+				error(ast_node_token(node), "Missing type or initial expression");
+			} else if (lhs_count < rhs_count) {
+				error(ast_node_token(node), "Extra initial expression");
+			}
+
+			if (dof != NULL) {
+				// NOTE(bill): Within a record
+				for_array(i, cd->names) {
+					AstNode *name = cd->names.e[i];
+					Entity *e = entities[i];
+					Token name_token = name->Ident;
+					if (str_eq(name_token.string, str_lit("_"))) {
+						dof->other_fields[dof->other_field_index++] = e;
+					} else {
+						HashKey key = hash_string(name_token.string);
+						if (map_entity_get(dof->entity_map, key) != NULL) {
+							// TODO(bill): Scope checking already checks the declaration
+							error(name_token, "`%.*s` is already declared in this record", LIT(name_token.string));
+						} else {
+							map_entity_set(dof->entity_map, key, e);
+							dof->other_fields[dof->other_field_index++] = e;
+						}
+						add_entity(c, c->context.scope, name, e);
+					}
+				}
+			}
+
+			gb_temp_arena_memory_end(tmp);
+		case_end;
+
+		case_ast_node(pd, ProcDecl, node);
+			// NOTE(bill): This must be handled here so it has access to the parent scope stuff
+			// e.g. using
+			Entity *e = make_entity_procedure(c->allocator, c->context.scope, pd->name->Ident, NULL);
+			e->identifier = pd->name;
+
+			DeclInfo *d = make_declaration_info(c->allocator, e->scope);
+			d->proc_decl = node;
+
+			add_entity_and_decl_info(c, pd->name, e, d);
+			check_entity_decl(c, e, d, NULL, NULL);
+		case_end;
+
+		case_ast_node(td, TypeDecl, node);
+			Token name_token = td->name->Ident;
+
+			Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, NULL);
+			e->identifier = td->name;
+
+			DeclInfo *d = make_declaration_info(c->allocator, e->scope);
+			d->type_expr = td->type;
+
+			add_entity_and_decl_info(c, td->name, e, d);
+
+			DelayedEntity delay = {e, d};
+			array_add(delayed_entities, delay);
+
+
+			if (dof != NULL) {
+				if (str_eq(name_token.string, str_lit("_"))) {
+					dof->other_fields[dof->other_field_index++] = e;
+				} else {
+					HashKey key = hash_string(name_token.string);
+					if (map_entity_get(dof->entity_map, key) != NULL) {
+						// TODO(bill): Scope checking already checks the declaration
+						error(name_token, "`%.*s` is already declared in this record", LIT(name_token.string));
+					} else {
+						map_entity_set(dof->entity_map, key, e);
+						dof->other_fields[dof->other_field_index++] = e;
+					}
+					add_entity(c, c->context.scope, td->name, e);
+					add_entity_use(c, td->name, e);
+				}
+			}
+		case_end;
+		}
+	}
+
+	// NOTE(bill): `when` stmts need to be handled after the other as the condition may refer to something
+	// declared after this stmt in source
+	for_array(i, nodes) {
+		AstNode *node = nodes.e[i];
+		switch (node->kind) {
+		case_ast_node(ws, WhenStmt, node);
+			check_local_collect_entities_from_when_stmt(c, ws, delayed_entities, dof);
+		case_end;
+		}
+	}
+}
+
+void check_scope_decls(Checker *c, AstNodeArray nodes, isize reserve_size, DelayedOtherFields *dof) {
+	DelayedEntities delayed_entities;
+	array_init_reserve(&delayed_entities, heap_allocator(), reserve_size);
+	check_local_collect_entities(c, nodes, &delayed_entities, dof);
+
+	for_array(i, delayed_entities) {
+		DelayedEntity delayed = delayed_entities.e[i];
+		if (delayed.entity->kind == Entity_TypeName) {
+			check_entity_decl(c, delayed.entity, delayed.decl, NULL, NULL);
+		}
+	}
+	for_array(i, delayed_entities) {
+		DelayedEntity delayed = delayed_entities.e[i];
+		if (delayed.entity->kind == Entity_Constant) {
+			check_entity_decl(c, delayed.entity, delayed.decl, NULL, NULL);
+		}
+	}
+
+	array_free(&delayed_entities);
+}
+
+
 bool check_is_assignable_to_using_subtype(Type *dst, Type *src) {
 	Type *prev_src = src;
 	// Type *prev_dst = dst;
@@ -247,96 +447,12 @@ void check_fields(Checker *c, AstNode *node, AstNodeArray decls,
 	isize other_field_index = 0;
 	Entity *using_index_expr = NULL;
 
+	DelayedOtherFields dof = {0};
+	dof.other_fields = other_fields;
+	dof.other_field_count = other_field_count;
+	dof.entity_map = &entity_map;
 
-	typedef struct {
-		Entity *e;
-		AstNode *t;
-	} Delay;
-	Array(Delay) delayed_const; array_init_reserve(&delayed_const, c->tmp_allocator, other_field_count);
-	Array(Delay) delayed_type;  array_init_reserve(&delayed_type,  c->tmp_allocator, other_field_count);
-
-	for_array(decl_index, decls) {
-		AstNode *decl = decls.e[decl_index];
-		if (decl->kind == AstNode_ConstDecl) {
-			ast_node(cd, ConstDecl, decl);
-
-			isize entity_count = cd->names.count;
-			isize entity_index = 0;
-			Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
-
-			for_array(i, cd->values) {
-				AstNode *name = cd->names.e[i];
-				AstNode *value = cd->values.e[i];
-
-				GB_ASSERT(name->kind == AstNode_Ident);
-				ExactValue v = {ExactValue_Invalid};
-				Token name_token = name->Ident;
-				Entity *e = make_entity_constant(c->allocator, c->context.scope, name_token, NULL, v);
-				entities[entity_index++] = e;
-
-				Delay delay = {e, cd->type};
-				array_add(&delayed_const, delay);
-			}
-
-			isize lhs_count = cd->names.count;
-			isize rhs_count = cd->values.count;
-
-			// TODO(bill): Better error messages or is this good enough?
-			if (rhs_count == 0 && cd->type == NULL) {
-				error(ast_node_token(node), "Missing type or initial expression");
-			} else if (lhs_count < rhs_count) {
-				error(ast_node_token(node), "Extra initial expression");
-			}
-
-			for_array(i, cd->names) {
-				AstNode *name = cd->names.e[i];
-				Entity *e = entities[i];
-				Token name_token = name->Ident;
-				if (str_eq(name_token.string, str_lit("_"))) {
-					other_fields[other_field_index++] = e;
-				} else {
-					HashKey key = hash_string(name_token.string);
-					if (map_entity_get(&entity_map, key) != NULL) {
-						// TODO(bill): Scope checking already checks the declaration
-						error(name_token, "`%.*s` is already declared in this structure", LIT(name_token.string));
-					} else {
-						map_entity_set(&entity_map, key, e);
-						other_fields[other_field_index++] = e;
-					}
-					add_entity(c, c->context.scope, name, e);
-				}
-			}
-		} else if (decl->kind == AstNode_TypeDecl) {
-			ast_node(td, TypeDecl, decl);
-			Token name_token = td->name->Ident;
-
-			Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, NULL);
-			Delay delay = {e, td->type};
-			array_add(&delayed_type, delay);
-
-			if (str_eq(name_token.string, str_lit("_"))) {
-				other_fields[other_field_index++] = e;
-			} else {
-				HashKey key = hash_string(name_token.string);
-				if (map_entity_get(&entity_map, key) != NULL) {
-					// TODO(bill): Scope checking already checks the declaration
-					error(name_token, "`%.*s` is already declared in this structure", LIT(name_token.string));
-				} else {
-					map_entity_set(&entity_map, key, e);
-					other_fields[other_field_index++] = e;
-				}
-				add_entity(c, c->context.scope, td->name, e);
-				add_entity_use(c, td->name, e);
-			}
-		}
-	}
-
-	for_array(i, delayed_type) {
-		check_const_decl(c, delayed_type.e[i].e, delayed_type.e[i].t, NULL);
-	}
-	for_array(i, delayed_const) {
-		check_type_decl(c, delayed_const.e[i].e, delayed_const.e[i].t, NULL, NULL);
-	}
+	check_scope_decls(c, decls, 1.2*other_field_count, &dof);
 
 	if (node->kind == AstNode_UnionType) {
 		isize field_index = 0;

+ 4 - 64
src/checker/stmt.c

@@ -11,74 +11,12 @@ typedef enum StmtFlag {
 } StmtFlag;
 
 
-
 void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
 	if (stmts.count == 0) {
 		return;
 	}
 
-	gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
-
-	typedef struct {
-		Entity *e;
-		DeclInfo *d;
-	} Delay;
-	Array(Delay) delayed_const; array_init_reserve(&delayed_const, c->tmp_allocator, stmts.count);
-	Array(Delay) delayed_type;  array_init_reserve(&delayed_type,  c->tmp_allocator, stmts.count);
-
-	for_array(i, stmts) {
-		AstNode *node = stmts.e[i];
-		switch (node->kind) {
-		case_ast_node(cd, ConstDecl, node);
-			for_array(i, cd->values) {
-				AstNode *name = cd->names.e[i];
-				AstNode *value = cd->values.e[i];
-				ExactValue v = {ExactValue_Invalid};
-
-				Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v);
-				e->identifier = name;
-
-				DeclInfo *d = make_declaration_info(c->allocator, e->scope);
-				d->type_expr = cd->type;
-				d->init_expr = value;
-
-				add_entity_and_decl_info(c, name, e, d);
-
-				Delay delay = {e, d};
-				array_add(&delayed_const, delay);
-			}
-
-			isize lhs_count = cd->names.count;
-			isize rhs_count = cd->values.count;
-
-			if (rhs_count == 0 && cd->type == NULL) {
-				error(ast_node_token(node), "Missing type or initial expression");
-			} else if (lhs_count < rhs_count) {
-				error(ast_node_token(node), "Extra initial expression");
-			}
-		case_end;
-
-		case_ast_node(td, TypeDecl, node);
-			Entity *e = make_entity_type_name(c->allocator, c->context.scope, td->name->Ident, NULL);
-			e->identifier = td->name;
-
-			DeclInfo *d = make_declaration_info(c->allocator, e->scope);
-			d->type_expr = td->type;
-
-			add_entity_and_decl_info(c, td->name, e, d);
-
-			Delay delay = {e, d};
-			array_add(&delayed_type, delay);
-		case_end;
-		}
-	}
-
-	for_array(i, delayed_type) {
-		check_entity_decl(c, delayed_type.e[i].e, delayed_type.e[i].d, NULL, NULL);
-	}
-	for_array(i, delayed_const) {
-		check_entity_decl(c, delayed_const.e[i].e, delayed_const.e[i].d, NULL, NULL);
-	}
+	check_scope_decls(c, stmts, 1.2*stmts.count, NULL);
 
 	bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
 	u32 f = flags & (~Stmt_FallthroughAllowed);
@@ -95,7 +33,6 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
 		check_stmt(c, n, new_flags);
 	}
 
-	gb_temp_arena_memory_end(tmp);
 }
 
 bool check_is_terminating_list(AstNodeArray stmts) {
@@ -1147,6 +1084,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 	case_end;
 
 	case_ast_node(pd, ProcDecl, node);
+		// NOTE(bill): Handled elsewhere
+	#if 0
 		// NOTE(bill): This must be handled here so it has access to the parent scope stuff
 		// e.g. using
 		Entity *e = make_entity_procedure(c->allocator, c->context.scope, pd->name->Ident, NULL);
@@ -1157,6 +1096,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 
 		add_entity_and_decl_info(c, pd->name, e, d);
 		check_entity_decl(c, e, d, NULL, NULL);
+	#endif
 	case_end;
 	}
 }

+ 1 - 16
src/checker/types.c

@@ -667,24 +667,9 @@ bool is_type_comparable(Type *t) {
 	t = base_type(get_enum_base_type(t));
 	switch (t->kind) {
 	case Type_Basic:
-		return t->kind != Basic_UntypedNil;
+		return t->kind != Basic_UntypedNil && t->kind != Basic_any;
 	case Type_Pointer:
 		return true;
-	case Type_Record: {
-		if (false && is_type_struct(t)) {
-			// TODO(bill): Should I even allow this?
-			for (isize i = 0; i < t->Record.field_count; i++) {
-				if (!is_type_comparable(t->Record.fields[i]->type))
-					return false;
-			}
-		} else if (is_type_enum(t)) {
-			return is_type_comparable(t->Record.enum_base);
-		}
-		return false;
-	} break;
-	case Type_Array:
-		return false;
-		// return is_type_comparable(t->Array.elem);
 	case Type_Vector:
 		return is_type_comparable(t->Vector.elem);
 	case Type_Proc:

+ 55 - 52
src/parser.c

@@ -1086,6 +1086,7 @@ void fix_advance_to_next_stmt(AstFile *f) {
 			return;
 
 		case Token_if:
+		case Token_when:
 		case Token_return:
 		case Token_for:
 		case Token_match:
@@ -1966,55 +1967,61 @@ AstNodeArray parse_parameter_list(AstFile *f) {
 }
 
 
-AstNodeArray parse_struct_params(AstFile *f, isize *decl_count_, bool using_allowed) {
+AstNodeArray parse_record_params(AstFile *f, isize *decl_count_, bool using_allowed, String context) {
 	AstNodeArray decls = make_ast_node_array(f);
 	isize decl_count = 0;
 
-	while (f->curr_token.kind == Token_Identifier ||
-	       f->curr_token.kind == Token_using) {
-		bool is_using = false;
-		if (allow_token(f, Token_using)) {
-			is_using = true;
-		}
-		AstNodeArray names = parse_lhs_expr_list(f);
-		if (names.count == 0) {
-			syntax_error(f->curr_token, "Empty field declaration");
-		}
-
-		if (!using_allowed && is_using) {
-			syntax_error(f->curr_token, "Cannot apply `using` to members of a union");
-			is_using = false;
-		}
-		if (names.count > 1 && is_using) {
-			syntax_error(f->curr_token, "Cannot apply `using` to more than one of the same type");
-		}
+	while (f->curr_token.kind != Token_CloseBrace &&
+	       f->curr_token.kind != Token_EOF) {
+		AstNode *decl = parse_stmt(f);
+		if (is_ast_node_decl(decl) ||
+		    decl->kind == AstNode_UsingStmt ||
+		    decl->kind == AstNode_EmptyStmt) {
+			switch (decl->kind) {
+			case AstNode_EmptyStmt:
+				break;
 
-		AstNode *decl = NULL;
+			case AstNode_ProcDecl:
+				syntax_error(f->curr_token, "Procedure declarations are not allowed within a %.*s", LIT(context));
+				break;
 
-		if (f->curr_token.kind == Token_Colon) {
-			decl = parse_decl(f, names);
+			case AstNode_UsingStmt: {
+				bool is_using = true;
+				if (!using_allowed) {
+					syntax_error(f->curr_token, "Cannot apply `using` to members of a %.*s", LIT(context));
+					is_using = false;
+				}
+				if (decl->UsingStmt.node->kind == AstNode_VarDecl) {
+					AstNode *vd = decl->UsingStmt.node;
+					vd->VarDecl.is_using = is_using && using_allowed;
+					if (vd->VarDecl.values.count > 0) {
+						syntax_error(f->curr_token, "Default variable assignments within a %.*s will be ignored", LIT(context));
+					}
+					array_add(&decls, vd);
+				} else {
+					syntax_error(ast_node_token(decl), "Illegal %.*s field", LIT(context));
+				}
+			} break;
 
-			if (decl->kind == AstNode_ProcDecl) {
-				syntax_error(f->curr_token, "Procedure declarations are not allowed within a structure");
-				decl = make_bad_decl(f, ast_node_token(names.e[0]), f->curr_token);
+			case AstNode_VarDecl: {
+				if (decl->VarDecl.values.count > 0) {
+					syntax_error(f->curr_token, "Default variable assignments within a %.*s will be ignored", LIT(context));
+				}
+				array_add(&decls, decl);
 			}
-		} else {
-			syntax_error(f->curr_token, "Illegal structure field");
-			decl = make_bad_decl(f, ast_node_token(names.e[0]), f->curr_token);
-		}
 
-		expect_semicolon_after_stmt(f, decl);
+			case AstNode_BadDecl:
+				break;
 
-		if (is_ast_node_decl(decl)) {
-			array_add(&decls, decl);
-			if (decl->kind == AstNode_VarDecl) {
-				decl->VarDecl.is_using = is_using && using_allowed;
-				if (decl->VarDecl.values.count > 0) {
-					syntax_error(f->curr_token, "Default variable assignments within a structure will be ignored (at the moment)");
-				}
-			} else {
+			case AstNode_ConstDecl:
+			case AstNode_TypeDecl:
+			default:
 				decl_count += 1;
+				array_add(&decls, decl);
+				break;
 			}
+		} else {
+			syntax_error(ast_node_token(decl), "Illegal record field: %.*s", LIT(ast_node_strings[decl->kind]));
 		}
 	}
 
@@ -2063,7 +2070,11 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags) {
 			next_token(f);
 		} else if (f->curr_token.kind == Token_vector) {
 			next_token(f);
-			count_expr = parse_expr(f, false);
+			if (f->curr_token.kind != Token_CloseBracket) {
+				count_expr = parse_expr(f, false);
+			} else {
+				syntax_error(f->curr_token, "Vector type missing count");
+			}
 			is_vector = true;
 		} else if (f->curr_token.kind != Token_CloseBracket) {
 			count_expr = parse_expr(f, false);
@@ -2076,15 +2087,6 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags) {
 		return make_array_type(f, token, count_expr, parse_type(f));
 	}
 
-	// case Token_OpenBrace: {
-	// 	f->expr_level++;
-	// 	Token token = expect_token(f, Token_OpenBrace);
-	// 	AstNode *count_expr = parse_expr(f, false);
-	// 	expect_token(f, Token_CloseBrace);
-	// 	f->expr_level--;
-	// 	return make_vector_type(f, token, count_expr, parse_type(f));
-	// }
-
 	case Token_struct: {
 		Token token = expect_token(f, Token_struct);
 		bool is_packed = false;
@@ -2112,7 +2114,7 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags) {
 
 		Token open = expect_token_after(f, Token_OpenBrace, "`struct`");
 		isize decl_count = 0;
-		AstNodeArray decls = parse_struct_params(f, &decl_count, true);
+		AstNodeArray decls = parse_record_params(f, &decl_count, true, str_lit("struct"));
 		Token close = expect_token(f, Token_CloseBrace);
 
 		return make_struct_type(f, token, decls, decl_count, is_packed, is_ordered);
@@ -2122,7 +2124,7 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags) {
 		Token token = expect_token(f, Token_union);
 		Token open = expect_token_after(f, Token_OpenBrace, "`union`");
 		isize decl_count = 0;
-		AstNodeArray decls = parse_struct_params(f, &decl_count, false);
+		AstNodeArray decls = parse_record_params(f, &decl_count, false, str_lit("union"));
 		Token close = expect_token(f, Token_CloseBrace);
 
 		return make_union_type(f, token, decls, decl_count);
@@ -2132,7 +2134,7 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags) {
 		Token token = expect_token(f, Token_raw_union);
 		Token open = expect_token_after(f, Token_OpenBrace, "`raw_union`");
 		isize decl_count = 0;
-		AstNodeArray decls = parse_struct_params(f, &decl_count, true);
+		AstNodeArray decls = parse_record_params(f, &decl_count, true, str_lit("raw_union"));
 		Token close = expect_token(f, Token_CloseBrace);
 
 		return make_raw_union_type(f, token, decls, decl_count);
@@ -2192,8 +2194,9 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags) {
 		break;
 
 	case Token_Eq:
-		if (f->prev_token.kind == Token_Colon)
+		if (f->prev_token.kind == Token_Colon) {
 			break;
+		}
 		// fallthrough
 	default:
 		syntax_error(f->curr_token,