소스 검색

Multiple type cases for `match in`

Ginger Bill 8 년 전
부모
커밋
c26990c22d
5개의 변경된 파일192개의 추가작업 그리고 187개의 파일을 삭제
  1. 15 0
      code/demo.odin
  2. 48 51
      src/check_stmt.c
  3. 10 1
      src/checker.c
  4. 106 130
      src/ir.c
  5. 13 5
      src/parser.c

+ 15 - 0
code/demo.odin

@@ -68,6 +68,21 @@ main :: proc() {
 		}
 	}
 
+	{
+		t := type_info(int);
+		using Type_Info;
+		match i in t {
+		case Integer, Float:
+			fmt.println("It's a number");
+		}
+
+		x: any = 123;
+		match i in x {
+		case int, f32:
+			fmt.println("It's an int or f32");
+		}
+	}
+
 	{
 		cond := true;
 		x: int;

+ 48 - 51
src/check_stmt.c

@@ -942,7 +942,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 			}
 			ast_node(cc, CaseClause, stmt);
 
-
 			for_array(j, cc->list) {
 				AstNode *expr = cc->list.e[j];
 				Operand y = {0};
@@ -1058,7 +1057,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 			break;
 		}
 
-
 		// NOTE(bill): Check for multiple defaults
 		AstNode *first_default = NULL;
 		ast_node(bs, BlockStmt, ms->body);
@@ -1093,7 +1091,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 		}
 
 
-		MapBool seen = {0};
+		MapBool seen = {0}; // Multimap
 		map_bool_init(&seen, heap_allocator());
 
 		for_array(i, bs->stmts) {
@@ -1107,69 +1105,68 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 			// TODO(bill): Make robust
 			Type *bt = base_type(type_deref(x.type));
 
-			AstNode *type_expr = cc->list.count > 0 ? cc->list.e[0] : NULL;
 			Type *case_type = NULL;
-			if (type_expr != NULL) { // Otherwise it's a default expression
-				Operand y = {0};
-				check_expr_or_type(c, &y, type_expr);
-
-				if (match_type_kind == MatchType_Union) {
-					GB_ASSERT(is_type_union(bt));
-					bool tag_type_found = false;
-					for (isize i = 0; i < bt->Record.variant_count; i++) {
-						Entity *f = bt->Record.variants[i];
-						if (are_types_identical(f->type, y.type)) {
-							tag_type_found = true;
-							break;
+			for_array(type_index, cc->list) {
+				AstNode *type_expr = cc->list.e[type_index];
+				if (type_expr != NULL) { // Otherwise it's a default expression
+					Operand y = {0};
+					check_expr_or_type(c, &y, type_expr);
+
+					if (match_type_kind == MatchType_Union) {
+						GB_ASSERT(is_type_union(bt));
+						bool tag_type_found = false;
+						for (isize i = 0; i < bt->Record.variant_count; i++) {
+							Entity *f = bt->Record.variants[i];
+							if (are_types_identical(f->type, y.type)) {
+								tag_type_found = true;
+								break;
+							}
 						}
+						if (!tag_type_found) {
+							gbString type_str = type_to_string(y.type);
+							error_node(y.expr, "Unknown tag type, got `%s`", type_str);
+							gb_string_free(type_str);
+							continue;
+						}
+						case_type = y.type;
+					} else if (match_type_kind == MatchType_Any) {
+						case_type = y.type;
+					} else {
+						GB_PANIC("Unknown type to type match statement");
 					}
-					if (!tag_type_found) {
-						gbString type_str = type_to_string(y.type);
-						error_node(y.expr, "Unknown tag type, got `%s`", type_str);
-						gb_string_free(type_str);
-						continue;
-					}
-					case_type = y.type;
-				} else if (match_type_kind == MatchType_Any) {
-					case_type = y.type;
-				} else {
-					GB_PANIC("Unknown type to type match statement");
-				}
 
-				HashKey key = hash_pointer(y.type);
-				bool *found = map_bool_get(&seen, key);
-				if (found) {
-					TokenPos pos = cc->token.pos;
-					gbString expr_str = expr_to_string(y.expr);
-					error_node(y.expr,
-					           "Duplicate type case `%s`\n"
-					           "\tprevious type case at %.*s(%td:%td)",
-					           expr_str,
-					           LIT(pos.file), pos.line, pos.column);
-					gb_string_free(expr_str);
-					break;
+					HashKey key = hash_pointer(y.type);
+					bool *found = map_bool_get(&seen, key);
+					if (found) {
+						TokenPos pos = cc->token.pos;
+						gbString expr_str = expr_to_string(y.expr);
+						error_node(y.expr,
+						           "Duplicate type case `%s`\n"
+						           "\tprevious type case at %.*s(%td:%td)",
+						           expr_str,
+						           LIT(pos.file), pos.line, pos.column);
+						gb_string_free(expr_str);
+						break;
+					}
+					map_bool_set(&seen, key, cast(bool)true);
 				}
-				map_bool_set(&seen, key, cast(bool)true);
 			}
 
-			check_open_scope(c, stmt);
+			if (cc->list.count > 1) {
+				case_type = NULL;
+			}
 			if (case_type == NULL) {
-				case_type = type_deref(x.type);
+				case_type = x.type;
 			}
-
 			add_type_info_type(c, case_type);
 
+			check_open_scope(c, stmt);
 			{
-				// NOTE(bill): Dummy type
-				Type *tt = case_type;
-				if (is_type_pointer(x.type)) {
-					tt = make_type_pointer(c->allocator, case_type);
-					add_type_info_type(c, tt);
-				}
-				Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, lhs->Ident, tt, true);
+				Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, lhs->Ident, case_type, true);
 				tag_var->flags |= EntityFlag_Used;
 				add_entity(c, c->context.scope, lhs, tag_var);
 				add_entity_use(c, lhs, tag_var);
+				add_implicit_entity(c, stmt, tag_var);
 			}
 			check_stmt_list(c, cc->stmts, mod_flags);
 			check_close_scope(c);

+ 10 - 1
src/checker.c

@@ -165,7 +165,7 @@ bool is_operand_nil(Operand o) {
 
 typedef struct BlockLabel {
 	String   name;
-	AstNode *label; // AstNode_Label
+	AstNode *label; //  AstNode_Label;
 } BlockLabel;
 
 // DeclInfo is used to store information of certain declarations to allow for "any order" usage
@@ -286,6 +286,7 @@ typedef struct CheckerInfo {
 	MapScope             scopes;          // Key: AstNode * | Node       -> Scope
 	MapExprInfo          untyped;         // Key: AstNode * | Expression -> ExprInfo
 	MapDeclInfo          entities;        // Key: Entity *
+	MapEntity            implicits;        // Key: AstNode *
 	MapEntity            foreigns;        // Key: String
 	MapAstFile           files;           // Key: String (full path)
 	MapIsize             type_info_map;   // Key: Type *
@@ -674,6 +675,7 @@ void init_checker_info(CheckerInfo *i) {
 	map_decl_info_init(&i->entities,   a);
 	map_expr_info_init(&i->untyped,    a);
 	map_entity_init(&i->foreigns,      a);
+	map_entity_init(&i->implicits,     a);
 	map_isize_init(&i->type_info_map,  a);
 	map_ast_file_init(&i->files,       a);
 	i->type_info_count = 0;
@@ -688,6 +690,7 @@ void destroy_checker_info(CheckerInfo *i) {
 	map_decl_info_destroy(&i->entities);
 	map_expr_info_destroy(&i->untyped);
 	map_entity_destroy(&i->foreigns);
+	map_entity_destroy(&i->implicits);
 	map_isize_destroy(&i->type_info_map);
 	map_ast_file_destroy(&i->files);
 }
@@ -873,6 +876,12 @@ void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclIn
 }
 
 
+void add_implicit_entity(Checker *c, AstNode *node, Entity *e) {
+	GB_ASSERT(node != NULL);
+	GB_ASSERT(e != NULL);
+	map_entity_set(&c->info.implicits, hash_pointer(node), e);
+}
+
 
 void add_type_info_type(Checker *c, Type *t) {
 	if (t == NULL) {

+ 106 - 130
src/ir.c

@@ -341,8 +341,8 @@ typedef struct irValueTypeName {
 typedef struct irValueGlobal {
 	Entity *      entity;
 	Type *        type;
-	irValue *    value;
-	irValueArray referrers;
+	irValue *     value;
+	irValueArray  referrers;
 	bool          is_constant;
 	bool          is_private;
 	bool          is_thread_local;
@@ -359,7 +359,8 @@ typedef struct irValueParam {
 
 typedef struct irValue {
 	irValueKind kind;
-	i32 index;
+	i32         index;
+	bool        index_set;
 	union {
 		irValueConstant      Constant;
 		irValueConstantSlice ConstantSlice;
@@ -1015,6 +1016,8 @@ irValue *ir_emit(irProcedure *proc, irValue *instr) {
 		if (!ir_is_instr_terminating(i)) {
 			array_add(&b->instrs, instr);
 		}
+	} else if (instr->Instr.kind != irInstr_Unreachable) {
+		GB_PANIC("ir_emit: Instruction missing parent block");
 	}
 	return instr;
 }
@@ -1108,7 +1111,9 @@ irBlock *ir_new_block(irProcedure *proc, AstNode *node, char *label) {
 
 void ir_add_block_to_proc(irProcedure *proc, irBlock *b) {
 	for_array(i, proc->blocks) {
-		GB_ASSERT(proc->blocks.e[i] != b);
+		if (proc->blocks.e[i] == b) {
+			return;
+		}
 	}
 	array_add(&proc->blocks, b);
 	b->index = proc->block_count++;
@@ -4876,7 +4881,25 @@ void ir_build_range_interval(irProcedure *proc, AstNodeIntervalExpr *node, Type
 	if (done_) *done_ = done;
 }
 
+void ir_store_type_case_implicit(irProcedure *proc, AstNode *clause, irValue *value) {
+	Entity **found = map_entity_get(&proc->module->info->implicits, hash_pointer(clause));
+	GB_ASSERT(found != NULL);
+	Entity *e = *found; GB_ASSERT(e != NULL);
+	irValue *x = ir_add_local(proc, e, NULL);
+	ir_emit_store(proc, x, value);
+}
+
+void ir_type_case_body(irProcedure *proc, AstNode *label, AstNode *clause, irBlock *body, irBlock *done) {
+	ast_node(cc, CaseClause, clause);
 
+	ir_push_target_list(proc, label, done, NULL, NULL);
+	ir_open_scope(proc);
+	ir_build_stmt_list(proc, cc->stmts);
+	ir_close_scope(proc, irDeferExit_Default, body);
+	ir_pop_target_list(proc);
+
+	ir_emit_jump(proc, done);
+}
 
 
 void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
@@ -5504,166 +5527,116 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 		ast_node(as, AssignStmt, ms->tag);
 		GB_ASSERT(as->lhs.count == 1);
 		GB_ASSERT(as->rhs.count == 1);
-		AstNode *lhs = as->lhs.e[0];
-		AstNode *rhs = as->rhs.e[0];
 
-		irValue *parent = ir_build_expr(proc, rhs);
+		irValue *parent = ir_build_expr(proc, as->rhs.e[0]);
+		Type *parent_type = ir_type(parent);
 		bool is_parent_ptr = is_type_pointer(ir_type(parent));
+
 		MatchTypeKind match_type_kind = check_valid_type_match_type(ir_type(parent));
 		GB_ASSERT(match_type_kind != MatchType_Invalid);
 
+		irValue *parent_value = parent;
+
+		irValue *parent_ptr = parent;
+		if (!is_parent_ptr) {
+			parent_ptr = ir_address_from_load_or_generate_local(proc, parent_ptr);
+		}
+
 		irValue *tag_index = NULL;
 		irValue *union_data = NULL;
 		if (match_type_kind == MatchType_Union) {
-			if (!is_parent_ptr) {
-				parent = ir_address_from_load_or_generate_local(proc, parent);
-			}
 			ir_emit_comment(proc, str_lit("get union's tag"));
-			tag_index = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, parent));
-			union_data = ir_emit_conv(proc, parent, t_rawptr);
-		} else if (match_type_kind == MatchType_Any) {
-			if (!is_parent_ptr) {
-				parent = ir_address_from_load_or_generate_local(proc, parent);
-			}
+			tag_index = ir_emit_load(proc, ir_emit_union_tag_ptr(proc, parent_ptr));
+			union_data = ir_emit_conv(proc, parent_ptr, t_rawptr);
 		}
 
-		irBlock *start_block = ir_new_block(proc, node, "type-match.case.first");
+		irBlock *start_block = ir_new_block(proc, node, "typematch.case.first");
 		ir_emit_jump(proc, start_block);
 		ir_start_block(proc, start_block);
 
-		irBlock *done = ir_new_block(proc, node, "type-match.done"); // NOTE(bill): Append later
+		// NOTE(bill): Append this later
+		irBlock *done = ir_new_block(proc, node, "typematch.done");
+		AstNode *default_ = NULL;
 
 		ast_node(body, BlockStmt, ms->body);
 
-		String tag_var_name = lhs->Ident.string;
-
-
-		AstNodeArray default_stmts = {0};
-		irBlock *default_block = NULL;
-
+		gb_local_persist i32 weird_count = 0;
 
-		isize case_count = body->stmts.count;
 		for_array(i, body->stmts) {
 			AstNode *clause = body->stmts.e[i];
 			ast_node(cc, CaseClause, clause);
-
-			Entity *tag_var_entity = NULL;
-			Type *tag_var_type = NULL;
-			if (str_eq(tag_var_name, str_lit("_"))) {
-				Type *t = type_of_expr(proc->module->info, cc->list.e[0]);
-				if (match_type_kind == MatchType_Union) {
-					t = make_type_pointer(proc->module->allocator, t);
-				}
-				tag_var_type = t;
-			} else {
-				Scope *scope = *map_scope_get(&proc->module->info->scopes, hash_pointer(clause));
-				tag_var_entity = current_scope_lookup_entity(scope, tag_var_name);
-				GB_ASSERT_MSG(tag_var_entity != NULL, "%.*s", LIT(tag_var_name));
-				tag_var_type = tag_var_entity->type;
-			}
-			GB_ASSERT(tag_var_type != NULL);
-
-			irBlock *next_cond = NULL;
-			irValue *cond = NULL;
-
 			if (cc->list.count == 0) {
-				// default case
-				default_stmts = cc->stmts;
-				default_block = ir_new_block(proc, clause, "type-match.dflt.body");
-
-
-				irValue *tag_var = NULL;
-				if (tag_var_entity != NULL) {
-					tag_var = ir_add_local(proc, tag_var_entity, NULL);
-				} else {
-					tag_var = ir_add_local_generated(proc, tag_var_type);
-				}
-
-				if (!is_parent_ptr) {
-					ir_emit_store(proc, tag_var, ir_emit_load(proc, parent));
-				} else {
-					ir_emit_store(proc, tag_var, parent);
-				}
+				default_ = clause;
 				continue;
 			}
-			GB_ASSERT(cc->list.count == 1);
 
-			irBlock *body = ir_new_block(proc, clause, "type-match.case.body");
-
-
-			if (match_type_kind == MatchType_Union) {
-				Type *bt = type_deref(tag_var_type);
-				irValue *index = NULL;
-				Type *ut = base_type(type_deref(ir_type(parent)));
-				GB_ASSERT(ut->Record.kind == TypeRecord_Union);
-				for (isize variant_index = 1; variant_index < ut->Record.variant_count; variant_index++) {
-					Entity *f = ut->Record.variants[variant_index];
-					if (are_types_identical(f->type, bt)) {
-						index = ir_const_int(allocator, variant_index);
-						break;
+			irBlock *body = ir_new_block(proc, clause, "typematch.body");
+			irBlock *next = NULL;
+			Type *case_type = NULL;
+			for_array(type_index, cc->list) {
+				next = ir_new_block(proc, NULL, "typematch.next");
+				case_type = type_of_expr(proc->module->info, cc->list.e[type_index]);
+				irValue *cond = NULL;
+				if (match_type_kind == MatchType_Union) {
+					Type *bt = type_deref(case_type);
+					irValue *index = NULL;
+					Type *ut = base_type(type_deref(parent_type));
+					GB_ASSERT(ut->Record.kind == TypeRecord_Union);
+					for (isize variant_index = 1; variant_index < ut->Record.variant_count; variant_index++) {
+						Entity *f = ut->Record.variants[variant_index];
+						if (are_types_identical(f->type, bt)) {
+							index = ir_const_int(allocator, variant_index);
+							break;
+						}
 					}
+					GB_ASSERT(index != NULL);
+					cond = ir_emit_comp(proc, Token_CmpEq, tag_index, index);
+				} else if (match_type_kind == MatchType_Any) {
+					irValue *any_ti  = ir_emit_load(proc, ir_emit_struct_ep(proc, parent_ptr, 0));
+					irValue *case_ti = ir_type_info(proc, case_type);
+					cond = ir_emit_comp(proc, Token_CmpEq, any_ti, case_ti);
 				}
-				GB_ASSERT(index != NULL);
+				GB_ASSERT(cond != NULL);
 
-				irValue *tag_var = NULL;
-				if (tag_var_entity != NULL) {
-					tag_var = ir_add_local(proc, tag_var_entity, NULL);
-				} else {
-					tag_var = ir_add_local_generated(proc, tag_var_type);
-				}
-
-				Type *bt_ptr = make_type_pointer(proc->module->allocator, bt);
-				irValue *data_ptr = ir_emit_conv(proc, union_data, bt_ptr);
-				if (!is_type_pointer(type_deref(ir_type(tag_var)))) {
-					data_ptr = ir_emit_load(proc, data_ptr);
-				}
-				ir_emit_store(proc, tag_var, data_ptr);
-
-				cond = ir_emit_comp(proc, Token_CmpEq, tag_index, index);
-			} else if (match_type_kind == MatchType_Any) {
-				Type *type = tag_var_type;
-				irValue *any_data = ir_emit_load(proc, ir_emit_struct_ep(proc, parent, 1));
-				irValue *data = ir_emit_conv(proc, any_data, make_type_pointer(proc->module->allocator, type));
-				if (tag_var_entity != NULL) {
-					ir_module_add_value(proc->module, tag_var_entity, data);
-				}
+				ir_emit_if(proc, cond, body, next);
+				ir_start_block(proc, next);
+			}
 
-				irValue *any_ti  = ir_emit_load(proc, ir_emit_struct_ep(proc, parent, 0));
-				irValue *case_ti = ir_type_info(proc, type);
-				cond = ir_emit_comp(proc, Token_CmpEq, any_ti, case_ti);
-			} else {
-				GB_PANIC("Invalid type for type match statement");
+			Entity *case_entity = NULL;
+			{
+				Entity **found = map_entity_get(&proc->module->info->implicits, hash_pointer(clause));
+				GB_ASSERT(found != NULL);
+				case_entity = *found;
 			}
 
-			next_cond = ir_new_block(proc, clause, "type-match.case.next");
-			ir_emit_if(proc, cond, body, next_cond);
-			ir_start_block(proc, next_cond);
+
+			irValue *value = parent_value;
 
 			ir_start_block(proc, body);
 
-			ir_push_target_list(proc, ms->label, done, NULL, NULL);
-			ir_open_scope(proc);
-			ir_build_stmt_list(proc, cc->stmts);
-			ir_close_scope(proc, irDeferExit_Default, body);
-			ir_pop_target_list(proc);
+			if (cc->list.count == 1) {
+				Type *ct = make_type_pointer(proc->module->allocator, case_entity->type);
+				irValue *data = NULL;
+				if (match_type_kind == MatchType_Union) {
+					data = union_data;
+				} else if (match_type_kind == MatchType_Any) {
+					irValue *any_data = ir_emit_load(proc, ir_emit_struct_ep(proc, parent_ptr, 1));
+					data = any_data;
+				}
+				value = ir_emit_load(proc, ir_emit_conv(proc, data, ct));
+			}
 
-			ir_emit_jump(proc, done);
-			proc->curr_block = next_cond;
-			// ir_start_block(proc, next_cond);
+			ir_store_type_case_implicit(proc, clause, value);
+			ir_type_case_body(proc, ms->label, clause, body, done);
+			ir_start_block(proc, next);
 		}
 
-		if (default_block != NULL) {
-			ir_emit_jump(proc, default_block);
-			ir_start_block(proc, default_block);
-
-			ir_push_target_list(proc, ms->label, done, NULL, NULL);
-			ir_open_scope(proc);
-			ir_build_stmt_list(proc, default_stmts);
-			ir_close_scope(proc, irDeferExit_Default, default_block);
-			ir_pop_target_list(proc);
+		if (default_ != NULL) {
+			ir_store_type_case_implicit(proc, default_, parent_value);
+			ir_type_case_body(proc, ms->label, default_, proc->curr_block, done);
+		} else {
+			ir_emit_jump(proc, done);
 		}
-
-		ir_emit_jump(proc, done);
 		ir_start_block(proc, done);
 	case_end;
 
@@ -5774,9 +5747,11 @@ void ir_number_proc_registers(irProcedure *proc) {
 			GB_ASSERT(value->kind == irValue_Instr);
 			irInstr *instr = &value->Instr;
 			if (ir_instr_type(instr) == NULL) { // NOTE(bill): Ignore non-returning instructions
+				value->index = -1;
 				continue;
 			}
 			value->index = reg_index;
+			value->index_set = true;
 			reg_index++;
 		}
 	}
@@ -5785,10 +5760,10 @@ void ir_number_proc_registers(irProcedure *proc) {
 void ir_begin_procedure_body(irProcedure *proc) {
 	array_add(&proc->module->procs, proc);
 
-	array_init(&proc->blocks,        heap_allocator());
-	array_init(&proc->defer_stmts,   heap_allocator());
-	array_init(&proc->children,      heap_allocator());
-	array_init(&proc->branch_blocks, heap_allocator());
+	array_init(&proc->blocks,           heap_allocator());
+	array_init(&proc->defer_stmts,      heap_allocator());
+	array_init(&proc->children,         heap_allocator());
+	array_init(&proc->branch_blocks,    heap_allocator());
 
 	DeclInfo **found = map_decl_info_get(&proc->module->info->entities, hash_pointer(proc->entity));
 	if (found != NULL) {
@@ -6949,6 +6924,7 @@ void ir_gen_tree(irGen *s) {
 	}
 
 
+
 	// m->layout = str_lit("e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64");
 }
 

+ 13 - 5
src/parser.c

@@ -2162,7 +2162,7 @@ AstNode *parse_expr(AstFile *f, bool lhs) {
 
 AstNodeArray parse_expr_list(AstFile *f, bool lhs) {
 	AstNodeArray list = make_ast_node_array(f);
-	do {
+	for (;;) {
 		AstNode *e = parse_expr(f, lhs);
 		array_add(&list, e);
 		if (f->curr_token.kind != Token_Comma ||
@@ -2170,7 +2170,7 @@ AstNodeArray parse_expr_list(AstFile *f, bool lhs) {
 		    break;
 		}
 		next_token(f);
-	} while (true);
+	}
 
 	return list;
 }
@@ -3099,9 +3099,17 @@ AstNode *parse_case_clause(AstFile *f) {
 
 AstNode *parse_type_case_clause(AstFile *f) {
 	Token token = f->curr_token;
-	AstNodeArray clause = make_ast_node_array(f);
+	AstNodeArray list = make_ast_node_array(f);
 	if (allow_token(f, Token_case)) {
-		array_add(&clause, parse_type(f));
+		for (;;) {
+			AstNode *t = parse_type(f);
+			array_add(&list, t);
+			if (f->curr_token.kind != Token_Comma ||
+			    f->curr_token.kind == Token_EOF) {
+			    break;
+			}
+			next_token(f);
+		}
 	} else {
 		expect_token(f, Token_default);
 	}
@@ -3109,7 +3117,7 @@ AstNode *parse_type_case_clause(AstFile *f) {
 	// expect_token(f, Token_ArrowRight); // TODO(bill): Is this the best syntax?
 	AstNodeArray stmts = parse_stmt_list(f);
 
-	return ast_case_clause(f, token, clause, stmts);
+	return ast_case_clause(f, token, list, stmts);
 }