authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-04-25 21:51:04-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-04-25 21:51:04-07:00
logd0378057d1276d0bbbf0f0cac05e252bcc131692
tree945fed7b6d4914c629cd201c41bd52ca57765401
parentd1b65c6f46ab0891db3c80db2eedbb434168aabe

support switching on error union type

closes #23

5 files changed, 143 insertions(+), 15 deletions(-)

src/all_types.hpp+1
......@@ -691,6 +691,7 @@ struct AstNodeSymbolExpr {
691691 // set this to instead of analyzing the node, pretend it's a type entry and it's this one.
692692 TypeTableEntry *override_type_entry;
693693 TypeEnumField *enum_field;
694 uint32_t err_value;
694695};
695696
696697struct AstNodeBoolLiteral {
src/analyze.cpp+61-4
......@@ -5116,8 +5116,11 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
51165116
51175117
51185118 int *field_use_counts = nullptr;
5119 HashMap<int, AstNode *, int_hash, int_eq> err_use_nodes;
51195120 if (expr_type->id == TypeTableEntryIdEnum) {
51205121 field_use_counts = allocate<int>(expr_type->data.enumeration.field_count);
5122 } else if (expr_type->id == TypeTableEntryIdErrorUnion) {
5123 err_use_nodes.init(10);
51215124 }
51225125
51235126 int *const_chosen_prong_index = &node->data.switch_expr.const_chosen_prong_index;
......@@ -5186,8 +5189,54 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
51865189 add_node_error(g, item_node, buf_sprintf("expected enum tag name"));
51875190 any_errors = true;
51885191 }
5192 } else if (expr_type->id == TypeTableEntryIdErrorUnion) {
5193 if (item_node->type == NodeTypeSymbol) {
5194 Buf *err_name = &item_node->data.symbol_expr.symbol;
5195 bool is_ok_case = buf_eql_str(err_name, "Ok");
5196 auto err_table_entry = is_ok_case ? nullptr: g->error_table.maybe_get(err_name);
5197 if (is_ok_case || err_table_entry) {
5198 uint32_t err_value = is_ok_case ? 0 : err_table_entry->value->value;
5199 item_node->data.symbol_expr.err_value = err_value;
5200 TypeTableEntry *this_var_type;
5201 if (is_ok_case) {
5202 this_var_type = expr_type->data.error.child_type;
5203 } else {
5204 this_var_type = g->builtin_types.entry_pure_error;
5205 }
5206 if (!var_type) {
5207 var_type = this_var_type;
5208 }
5209 if (this_var_type != var_type) {
5210 all_agree_on_var_type = false;
5211 }
5212
5213 // detect duplicate switch values
5214 auto existing_entry = err_use_nodes.maybe_get(err_value);
5215 if (existing_entry) {
5216 add_node_error(g, existing_entry->value,
5217 buf_sprintf("duplicate switch value: '%s'", buf_ptr(err_name)));
5218 any_errors = true;
5219 } else {
5220 err_use_nodes.put(err_value, item_node);
5221 }
5222
5223 if (!any_errors && expr_val->ok) {
5224 if (expr_val->data.x_err.err->value == err_value) {
5225 *const_chosen_prong_index = prong_i;
5226 }
5227 }
5228 } else {
5229 add_node_error(g, item_node,
5230 buf_sprintf("use of undeclared error value '%s'", buf_ptr(err_name)));
5231 any_errors = true;
5232 }
5233 } else {
5234 add_node_error(g, item_node, buf_sprintf("expected error value name"));
5235 any_errors = true;
5236 }
51895237 } else {
51905238 if (!any_errors && expr_val->ok) {
5239 // note: there is now a function in eval.cpp for doing const expr comparison
51915240 zig_panic("TODO determine if const exprs are equal");
51925241 }
51935242 TypeTableEntry *item_type = analyze_expression(g, import, context, expr_type, item_node);
......@@ -5252,17 +5301,25 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
52525301 return g->builtin_types.entry_invalid;
52535302 }
52545303
5304 TypeTableEntry *result_type = resolve_peer_type_compatibility(g, import, context, node,
5305 peer_nodes, peer_types, prong_count);
5306
52555307 if (expr_val->ok) {
52565308 assert(*const_chosen_prong_index != -1);
52575309
52585310 *const_val = get_resolved_expr(peer_nodes[*const_chosen_prong_index])->const_val;
5259 // the target expr depends on a compile var,
5260 // so the entire if statement does too
5311 // the target expr depends on a compile var because we have an error on unnecessary
5312 // switch statement, so the entire switch statement does too
52615313 const_val->depends_on_compile_var = true;
5262 }
52635314
5315 if (!const_val->ok) {
5316 return add_error_if_type_is_num_lit(g, result_type, node);
5317 }
5318 } else {
5319 return add_error_if_type_is_num_lit(g, result_type, node);
5320 }
52645321
5265 return resolve_peer_type_compatibility(g, import, context, node, peer_nodes, peer_types, prong_count);
5322 return result_type;
52665323}
52675324
52685325static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
src/codegen.cpp+40-11
......@@ -2627,12 +2627,6 @@ static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) {
26272627 }
26282628
26292629 zig_unreachable();
2630
2631 /* TODO delete
2632 FnTableEntry *fn_entry = node->data.symbol_expr.fn_entry;
2633 assert(fn_entry);
2634 return fn_entry->fn_value;
2635 */
26362630}
26372631
26382632static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
......@@ -2653,6 +2647,10 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
26532647 add_debug_source_node(g, node);
26542648 LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, target_value_handle, 0, "");
26552649 target_value = LLVMBuildLoad(g->builder, tag_field_ptr, "");
2650 } else if (target_type->id == TypeTableEntryIdErrorUnion) {
2651 add_debug_source_node(g, node);
2652 LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, target_value_handle, 0, "");
2653 target_value = LLVMBuildLoad(g->builder, tag_field_ptr, "");
26562654 } else {
26572655 zig_unreachable();
26582656 }
......@@ -2696,12 +2694,23 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
26962694
26972695 assert(item_node->type != NodeTypeSwitchRange);
26982696 LLVMValueRef val;
2699 if (target_type->id == TypeTableEntryIdEnum) {
2697 if (target_type->id == TypeTableEntryIdEnum ||
2698 target_type->id == TypeTableEntryIdErrorUnion)
2699 {
27002700 assert(item_node->type == NodeTypeSymbol);
2701 TypeEnumField *enum_field = item_node->data.symbol_expr.enum_field;
2702 assert(enum_field);
2703 val = LLVMConstInt(target_type->data.enumeration.tag_type->type_ref,
2704 enum_field->value, false);
2701 TypeEnumField *enum_field = nullptr;
2702 uint32_t err_value = 0;
2703 if (target_type->id == TypeTableEntryIdEnum) {
2704 enum_field = item_node->data.symbol_expr.enum_field;
2705 assert(enum_field);
2706 val = LLVMConstInt(target_type->data.enumeration.tag_type->type_ref,
2707 enum_field->value, false);
2708 } else if (target_type->id == TypeTableEntryIdErrorUnion) {
2709 err_value = item_node->data.symbol_expr.err_value;
2710 val = LLVMConstInt(g->err_tag_type->type_ref, err_value, false);
2711 } else {
2712 zig_unreachable();
2713 }
27052714
27062715 if (prong_var && type_has_bits(prong_var->type)) {
27072716 LLVMBasicBlockRef item_block;
......@@ -2721,6 +2730,7 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
27212730 gen_assign_raw(g, var_node, BinOpTypeAssign,
27222731 prong_var->value_ref, target_value, prong_var->type, target_type);
27232732 } else if (target_type->id == TypeTableEntryIdEnum) {
2733 assert(enum_field);
27242734 assert(type_has_bits(enum_field->type_entry));
27252735 LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, target_value_handle,
27262736 1, "");
......@@ -2731,6 +2741,25 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
27312741
27322742 gen_assign_raw(g, var_node, BinOpTypeAssign,
27332743 prong_var->value_ref, handle_val, prong_var->type, enum_field->type_entry);
2744 } else if (target_type->id == TypeTableEntryIdErrorUnion) {
2745 if (err_value == 0) {
2746 // variable is the payload
2747 LLVMValueRef err_payload_ptr = LLVMBuildStructGEP(g->builder,
2748 target_value_handle, 1, "");
2749 LLVMValueRef handle_val = get_handle_value(g, var_node,
2750 err_payload_ptr, prong_var->type);
2751 gen_assign_raw(g, var_node, BinOpTypeAssign,
2752 prong_var->value_ref, handle_val, prong_var->type, prong_var->type);
2753 } else {
2754 // variable is the pure error value
2755 LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder,
2756 target_value_handle, 0, "");
2757 LLVMValueRef handle_val = LLVMBuildLoad(g->builder, err_tag_ptr, "");
2758 gen_assign_raw(g, var_node, BinOpTypeAssign,
2759 prong_var->value_ref, handle_val, prong_var->type, g->err_tag_type);
2760 }
2761 } else {
2762 zig_unreachable();
27342763 }
27352764 if (make_item_blocks) {
27362765 LLVMBuildBr(g->builder, prong_block);
test/run_tests.cpp+12
......@@ -1233,6 +1233,18 @@ fn bad_eql_2(a: EnumWithData, b: EnumWithData) -> bool {
12331233 )SOURCE", 2,
12341234 ".tmp_source.zig:3:7: error: operator not allowed for type '[]u8'",
12351235 ".tmp_source.zig:10:7: error: operator not allowed for type 'EnumWithData'");
1236
1237 add_compile_fail_case("non-const switch number literal", R"SOURCE(
1238fn foo() {
1239 const x = switch (bar()) {
1240 1, 2 => 1,
1241 3, 4 => 2,
1242 else => 3,
1243 };
1244}
1245#static_eval_enable(false)
1246fn bar() -> i32 { 2 }
1247 )SOURCE", 1, ".tmp_source.zig:3:15: error: unable to infer expression type");
12361248}
12371249
12381250//////////////////////////////////////////////////////////////////////////////
test/self_hosted.zig+29
......@@ -1339,3 +1339,32 @@ fn character_literals() {
13391339 assert('\'' == single_quote);
13401340}
13411341const single_quote = '\'';
1342
1343
1344#attribute("test")
1345fn switch_with_multiple_expressions() {
1346 const x: i32 = switch (returns_five()) {
1347 1, 2, 3 => 1,
1348 4, 5, 6 => 2,
1349 else => 3,
1350 };
1351 assert(x == 2);
1352}
1353#static_eval_enable(false)
1354fn returns_five() -> i32 { 5 }
1355
1356
1357#attribute("test")
1358fn switch_on_error_union() {
1359 const x = switch (returns_ten()) {
1360 Ok => |val| val + 1,
1361 ItBroke, NoMem => 1,
1362 CrappedOut => 2,
1363 };
1364 assert(x == 11);
1365}
1366error ItBroke;
1367error NoMem;
1368error CrappedOut;
1369#static_eval_enable(false)
1370fn returns_ten() -> %i32 { 10 }