authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-02-04 17:21:08-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-02-04 17:21:08-07:00
log093e0d1bb020e6b7245d142b1fc75bdbd7868045
tree1ec1d75ae181952ab99c69808210ced4405f9c09
parenta4cba900e53154abd5595bacf709fe8fdcc86b27

support variable in switch expression prongs

See #43

4 files changed, 105 insertions(+), 36 deletions(-)

src/all_types.hpp+1
...@@ -510,6 +510,7 @@ struct AstNodeSwitchProng {...@@ -510,6 +510,7 @@ struct AstNodeSwitchProng {
510 // populated by semantic analyzer510 // populated by semantic analyzer
511 BlockContext *block_context;511 BlockContext *block_context;
512 VariableTableEntry *var;512 VariableTableEntry *var;
513 bool var_is_target_expr;
513};514};
514515
515struct AstNodeSwitchRange {516struct AstNodeSwitchRange {
src/analyze.cpp+18-1
...@@ -4495,6 +4495,7 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,...@@ -4495,6 +4495,7 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
4495 AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);4495 AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
44964496
4497 TypeTableEntry *var_type;4497 TypeTableEntry *var_type;
4498 bool var_is_target_expr;
4498 if (prong_node->data.switch_prong.items.length == 0) {4499 if (prong_node->data.switch_prong.items.length == 0) {
4499 if (else_prong) {4500 if (else_prong) {
4500 add_node_error(g, prong_node, buf_sprintf("multiple else prongs in switch expression"));4501 add_node_error(g, prong_node, buf_sprintf("multiple else prongs in switch expression"));
...@@ -4502,7 +4503,11 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,...@@ -4502,7 +4503,11 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
4502 else_prong = prong_node;4503 else_prong = prong_node;
4503 }4504 }
4504 var_type = expr_type;4505 var_type = expr_type;
4506 var_is_target_expr = true;
4505 } else {4507 } else {
4508 bool all_agree_on_var_type = true;
4509 var_type = nullptr;
4510
4506 for (int item_i = 0; item_i < prong_node->data.switch_prong.items.length; item_i += 1) {4511 for (int item_i = 0; item_i < prong_node->data.switch_prong.items.length; item_i += 1) {
4507 AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);4512 AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
4508 if (item_node->type == NodeTypeSwitchRange) {4513 if (item_node->type == NodeTypeSwitchRange) {
...@@ -4515,6 +4520,12 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,...@@ -4515,6 +4520,12 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
4515 TypeEnumField *type_enum_field = get_enum_field(expr_type, field_name);4520 TypeEnumField *type_enum_field = get_enum_field(expr_type, field_name);
4516 if (type_enum_field) {4521 if (type_enum_field) {
4517 item_node->data.symbol_expr.enum_field = type_enum_field;4522 item_node->data.symbol_expr.enum_field = type_enum_field;
4523 if (!var_type) {
4524 var_type = type_enum_field->type_entry;
4525 }
4526 if (type_enum_field->type_entry != var_type) {
4527 all_agree_on_var_type = false;
4528 }
4518 } else {4529 } else {
4519 add_node_error(g, item_node,4530 add_node_error(g, item_node,
4520 buf_sprintf("enum '%s' has no field '%s'",4531 buf_sprintf("enum '%s' has no field '%s'",
...@@ -4534,7 +4545,12 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,...@@ -4534,7 +4545,12 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
4534 }4545 }
4535 }4546 }
4536 }4547 }
4537 var_type = expr_type;4548 if (!var_type || !all_agree_on_var_type) {
4549 var_type = expr_type;
4550 var_is_target_expr = true;
4551 } else {
4552 var_is_target_expr = false;
4553 }
4538 }4554 }
45394555
4540 BlockContext *child_context = new_block_context(node, context);4556 BlockContext *child_context = new_block_context(node, context);
...@@ -4546,6 +4562,7 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,...@@ -4546,6 +4562,7 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
4546 var_node->block_context = child_context;4562 var_node->block_context = child_context;
4547 prong_node->data.switch_prong.var = add_local_var(g, var_node, child_context, var_name,4563 prong_node->data.switch_prong.var = add_local_var(g, var_node, child_context, var_name,
4548 var_type, true);4564 var_type, true);
4565 prong_node->data.switch_prong.var_is_target_expr = var_is_target_expr;
4549 }4566 }
45504567
4551 peer_types[prong_i] = analyze_expression(g, import, child_context, expected_type,4568 peer_types[prong_i] = analyze_expression(g, import, child_context, expected_type,
src/codegen.cpp+60-35
...@@ -184,6 +184,15 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,...@@ -184,6 +184,15 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,
184 return *fn;184 return *fn;
185}185}
186186
187static LLVMValueRef get_handle_value(CodeGen *g, AstNode *source_node, LLVMValueRef ptr, TypeTableEntry *type) {
188 if (handle_is_ptr(type)) {
189 return ptr;
190 } else {
191 add_debug_source_node(g, source_node);
192 return LLVMBuildLoad(g->builder, ptr, "");
193 }
194}
195
187static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {196static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) {
188 assert(node->type == NodeTypeFnCallExpr);197 assert(node->type == NodeTypeFnCallExpr);
189 AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;198 AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
...@@ -1024,11 +1033,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {...@@ -1024,11 +1033,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
10241033
1025 if (type_has_bits(child_type)) {1034 if (type_has_bits(child_type)) {
1026 LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, "");1035 LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, "");
1027 if (handle_is_ptr(child_type)) {1036 return get_handle_value(g, expr_node, child_val_ptr, child_type);
1028 return child_val_ptr;
1029 } else {
1030 return LLVMBuildLoad(g->builder, child_val_ptr, "");
1031 }
1032 } else {1037 } else {
1033 return nullptr;1038 return nullptr;
1034 }1039 }
...@@ -1073,11 +1078,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {...@@ -1073,11 +1078,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
1073 } else {1078 } else {
1074 add_debug_source_node(g, node);1079 add_debug_source_node(g, node);
1075 LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, expr_val, 0, "");1080 LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, expr_val, 0, "");
1076 if (handle_is_ptr(child_type)) {1081 return get_handle_value(g, node, maybe_field_ptr, child_type);
1077 return maybe_field_ptr;
1078 } else {
1079 return LLVMBuildLoad(g->builder, maybe_field_ptr, "");
1080 }
1081 }1082 }
1082 }1083 }
1083 }1084 }
...@@ -1412,11 +1413,7 @@ static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef may...@@ -1412,11 +1413,7 @@ static LLVMValueRef gen_unwrap_maybe(CodeGen *g, AstNode *node, LLVMValueRef may
1412 } else {1413 } else {
1413 add_debug_source_node(g, node);1414 add_debug_source_node(g, node);
1414 LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 0, "");1415 LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_struct_ref, 0, "");
1415 if (handle_is_ptr(child_type)) {1416 return get_handle_value(g, node, maybe_field_ptr, child_type);
1416 return maybe_field_ptr;
1417 } else {
1418 return LLVMBuildLoad(g->builder, maybe_field_ptr, "");
1419 }
1420 }1417 }
1421}1418}
14221419
...@@ -1580,12 +1577,7 @@ static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) {...@@ -1580,12 +1577,7 @@ static LLVMValueRef gen_unwrap_err_expr(CodeGen *g, AstNode *node) {
1580 return nullptr;1577 return nullptr;
1581 }1578 }
1582 LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, "");1579 LLVMValueRef child_val_ptr = LLVMBuildStructGEP(g->builder, expr_val, 1, "");
1583 LLVMValueRef child_val;1580 LLVMValueRef child_val = get_handle_value(g, node, child_val_ptr, child_type);
1584 if (handle_is_ptr(child_type)) {
1585 child_val = child_val_ptr;
1586 } else {
1587 child_val = LLVMBuildLoad(g->builder, child_val_ptr, "");
1588 }
15891581
1590 if (!have_end_block) {1582 if (!have_end_block) {
1591 return child_val;1583 return child_val;
...@@ -1667,11 +1659,7 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {...@@ -1667,11 +1659,7 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
1667 if (type_has_bits(child_type)) {1659 if (type_has_bits(child_type)) {
1668 add_debug_source_node(g, node);1660 add_debug_source_node(g, node);
1669 LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, value, 1, "");1661 LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, value, 1, "");
1670 if (handle_is_ptr(child_type)) {1662 return get_handle_value(g, node, val_ptr, child_type);
1671 return val_ptr;
1672 } else {
1673 return LLVMBuildLoad(g->builder, val_ptr, "");
1674 }
1675 } else {1663 } else {
1676 return nullptr;1664 return nullptr;
1677 }1665 }
...@@ -2294,12 +2282,7 @@ static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) {...@@ -2294,12 +2282,7 @@ static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) {
2294 return nullptr;2282 return nullptr;
2295 } else if (variable->is_ptr) {2283 } else if (variable->is_ptr) {
2296 assert(variable->value_ref);2284 assert(variable->value_ref);
2297 if (handle_is_ptr(variable->type)) {2285 return get_handle_value(g, node, variable->value_ref, variable->type);
2298 return variable->value_ref;
2299 } else {
2300 add_debug_source_node(g, node);
2301 return LLVMBuildLoad(g->builder, variable->value_ref, "");
2302 }
2303 } else {2286 } else {
2304 return variable->value_ref;2287 return variable->value_ref;
2305 }2288 }
...@@ -2347,6 +2330,8 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {...@@ -2347,6 +2330,8 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
2347 AstNode *else_prong = nullptr;2330 AstNode *else_prong = nullptr;
2348 for (int prong_i = 0; prong_i < prong_count; prong_i += 1) {2331 for (int prong_i = 0; prong_i < prong_count; prong_i += 1) {
2349 AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);2332 AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
2333 VariableTableEntry *prong_var = prong_node->data.switch_prong.var;
2334
2350 LLVMBasicBlockRef prong_block;2335 LLVMBasicBlockRef prong_block;
2351 if (prong_node->data.switch_prong.items.length == 0) {2336 if (prong_node->data.switch_prong.items.length == 0) {
2352 assert(!else_prong);2337 assert(!else_prong);
...@@ -2354,8 +2339,12 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {...@@ -2354,8 +2339,12 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
2354 prong_block = else_block;2339 prong_block = else_block;
2355 } else {2340 } else {
2356 prong_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchProng");2341 prong_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchProng");
2357 for (int item_i = 0; item_i < prong_node->data.switch_prong.items.length; item_i += 1) {2342 int prong_item_count = prong_node->data.switch_prong.items.length;
2343 bool make_item_blocks = prong_var && prong_item_count > 1;
2344
2345 for (int item_i = 0; item_i < prong_item_count; item_i += 1) {
2358 AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);2346 AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
2347
2359 assert(item_node->type != NodeTypeSwitchRange);2348 assert(item_node->type != NodeTypeSwitchRange);
2360 LLVMValueRef val;2349 LLVMValueRef val;
2361 if (target_type->id == TypeTableEntryIdEnum) {2350 if (target_type->id == TypeTableEntryIdEnum) {
...@@ -2364,14 +2353,50 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {...@@ -2364,14 +2353,50 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
2364 assert(enum_field);2353 assert(enum_field);
2365 val = LLVMConstInt(target_type->data.enumeration.tag_type->type_ref,2354 val = LLVMConstInt(target_type->data.enumeration.tag_type->type_ref,
2366 enum_field->value, false);2355 enum_field->value, false);
2356
2357 if (prong_var && type_has_bits(prong_var->type)) {
2358 LLVMBasicBlockRef item_block;
2359
2360 if (make_item_blocks) {
2361 item_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchProngItem");
2362 LLVMAddCase(switch_instr, val, item_block);
2363 LLVMPositionBuilderAtEnd(g->builder, item_block);
2364 } else {
2365 LLVMAddCase(switch_instr, val, prong_block);
2366 LLVMPositionBuilderAtEnd(g->builder, prong_block);
2367 }
2368
2369 AstNode *var_node = prong_node->data.switch_prong.var_symbol;
2370 add_debug_source_node(g, var_node);
2371 if (prong_node->data.switch_prong.var_is_target_expr) {
2372 gen_assign_raw(g, var_node, BinOpTypeAssign,
2373 prong_var->value_ref, target_value, prong_var->type, target_type);
2374 } else if (target_type->id == TypeTableEntryIdEnum) {
2375 assert(type_has_bits(enum_field->type_entry));
2376 LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, target_value_handle,
2377 1, "");
2378 LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr,
2379 LLVMPointerType(enum_field->type_entry->type_ref, 0), "");
2380 LLVMValueRef handle_val = get_handle_value(g, var_node, bitcasted_union_field_ptr,
2381 enum_field->type_entry);
2382
2383 gen_assign_raw(g, var_node, BinOpTypeAssign,
2384 prong_var->value_ref, handle_val, prong_var->type, enum_field->type_entry);
2385 }
2386 if (make_item_blocks) {
2387 LLVMBuildBr(g->builder, prong_block);
2388 }
2389 } else {
2390 LLVMAddCase(switch_instr, val, prong_block);
2391 }
2367 } else {2392 } else {
2368 assert(get_resolved_expr(item_node)->const_val.ok);2393 assert(get_resolved_expr(item_node)->const_val.ok);
2369 val = gen_expr(g, item_node);2394 val = gen_expr(g, item_node);
2395 LLVMAddCase(switch_instr, val, prong_block);
2370 }2396 }
2371 LLVMAddCase(switch_instr, val, prong_block);
2372 }2397 }
2373 }2398 }
2374 assert(!prong_node->data.switch_prong.var_symbol);2399
2375 LLVMPositionBuilderAtEnd(g->builder, prong_block);2400 LLVMPositionBuilderAtEnd(g->builder, prong_block);
2376 AstNode *prong_expr = prong_node->data.switch_prong.expr;2401 AstNode *prong_expr = prong_node->data.switch_prong.expr;
2377 LLVMValueRef prong_val = gen_expr(g, prong_expr);2402 LLVMValueRef prong_val = gen_expr(g, prong_expr);
test/self_hosted.zig+26
...@@ -147,3 +147,29 @@ enum SwitchStatmentFoo {...@@ -147,3 +147,29 @@ enum SwitchStatmentFoo {
147 C,147 C,
148 D,148 D,
149}149}
150
151
152#attribute("test")
153fn switch_prong_with_var() {
154 switch_prong_with_var_fn(SwitchProngWithVarEnum.One(13));
155 switch_prong_with_var_fn(SwitchProngWithVarEnum.Two(13.0));
156 switch_prong_with_var_fn(SwitchProngWithVarEnum.Meh);
157}
158enum SwitchProngWithVarEnum {
159 One: i32,
160 Two: f32,
161 Meh,
162}
163fn switch_prong_with_var_fn(a: SwitchProngWithVarEnum) {
164 switch(a) {
165 One => |x| {
166 if (x != 13) unreachable{};
167 },
168 Two => |x| {
169 if (x != 13.0) unreachable{};
170 },
171 Meh => |x| {
172 const v: void = x;
173 },
174 }
175}