authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-20 02:12:24-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-20 02:12:24-07:00
logad9759bc8e993364490d7268d491620ee4a2cc24
tree952a04c2e6d7e37132a0881e45e4ac1d4227e2e0
parent3eca42c17be1105dbc76c22fdc8447bccf11de0d

basic support for switch expression


7 files changed, 209 insertions(+), 21 deletions(-)

doc/langref.md+3-3
...@@ -94,7 +94,7 @@ BlockExpression : IfExpression | Block | WhileExpression | ForExpression | Switc...@@ -94,7 +94,7 @@ BlockExpression : IfExpression | Block | WhileExpression | ForExpression | Switc
9494
95SwitchExpression : "switch" "(" Expression ")" "{" many(SwitchProng) "}"95SwitchExpression : "switch" "(" Expression ")" "{" many(SwitchProng) "}"
9696
97SwitchProng : (list(SwitchItem, ",") | "else") option("," "(" "Symbol" ")") "=>" Expression ","97SwitchProng : (list(SwitchItem, ",") | "else") option(":" "(" "Symbol" ")") "=>" Expression ","
9898
99SwitchItem : Expression | (Expression "..." Expression)99SwitchItem : Expression | (Expression "..." Expression)
100100
...@@ -197,8 +197,8 @@ x{}...@@ -197,8 +197,8 @@ x{}
197 | Example | Characters | Escapes | Null Term | Type197 | Example | Characters | Escapes | Null Term | Type
198----------------|----------|-------------|----------------|-----------|----------198----------------|----------|-------------|----------------|-----------|----------
199 Byte | 'H' | All ASCII | Byte | No | u8199 Byte | 'H' | All ASCII | Byte | No | u8
200 UTF-8 Bytes | "hello" | All Unicode | Byte & Unicode | No | [5; u8]200 UTF-8 Bytes | "hello" | All Unicode | Byte & Unicode | No | [5]u8
201 UTF-8 C string | c"hello" | All Unicode | Byte & Unicode | Yes | *const u8201 UTF-8 C string | c"hello" | All Unicode | Byte & Unicode | Yes | &const u8
202202
203### Byte Escapes203### Byte Escapes
204204
example/list/list.zig+12-14
...@@ -68,37 +68,35 @@ pub fn free#(T: type)(ptr: ?&T) {...@@ -68,37 +68,35 @@ pub fn free#(T: type)(ptr: ?&T) {
6868
69////////////////// alternate69////////////////// alternate
7070
71// previously proposed, but with : instead of ->71// previously proposed but without ->
72// `:` means "parser should expect a type now"72fn max#(T: type)(a: T, b: T) T {
73fn max#(T :type)(a :T, b :T) :T {
74 if (a > b) a else b73 if (a > b) a else b
75}74}
7675
77// andy's new idea76// andy's new idea
78// parameters can talk about @typeof() for previous parameters.77// parameters can reference other inline parameters.
79// using :T here is equivalent to @child_type(@typeof(T))78fn max(inline T: type, a: T, b: T) T {
80fn max(T :type, a :T, b :T) :T {
81 if (a > b) a else b79 if (a > b) a else b
82}80}
8381
84fn f() {82fn f() {
85 const x :i32 = 1234;83 const x: i32 = 1234;
86 const y :i32 = 5678;84 const y: i32 = 5678;
87 const z = max(@typeof(x), x, y);85 const z = max(@typeof(x), x, y);
88}86}
8987
90// So, type-generic functions don't need any fancy syntax. type-generic88// So, type-generic functions don't need any fancy syntax. type-generic
91// containers still do, though:89// containers still do, though:
9290
93pub struct List(T :type) {91pub struct List(T: type) {
94 items :?&T,92 items: ?&T,
95 length :isize,93 length: isize,
96 capacity :isize,94 capacity: isize,
97}95}
9896
99// Types are always marked with ':' so we don't need '#' to indicate type generic parameters.97// we don't need '#' to indicate type generic parameters.
10098
101fn f() {99fn f() {
102 var list :List(:u8);100 var list: List(u8);
103}101}
104102
src/all_types.hpp+12
...@@ -26,6 +26,7 @@ struct BuiltinFnEntry;...@@ -26,6 +26,7 @@ struct BuiltinFnEntry;
26struct LabelTableEntry;26struct LabelTableEntry;
27struct TypeStructField;27struct TypeStructField;
28struct CodeGen;28struct CodeGen;
29struct ConstExprValue;
2930
30enum OutType {31enum OutType {
31 OutTypeUnknown,32 OutTypeUnknown,
...@@ -57,6 +58,11 @@ struct Cast {...@@ -57,6 +58,11 @@ struct Cast {
57 AstNode *source_node;58 AstNode *source_node;
58};59};
5960
61struct ConstEnumValue {
62 uint64_t tag;
63 ConstExprValue *payload;
64};
65
60struct ConstExprValue {66struct ConstExprValue {
61 bool ok; // true if constant expression evalution worked67 bool ok; // true if constant expression evalution worked
62 bool depends_on_compile_var;68 bool depends_on_compile_var;
...@@ -69,6 +75,7 @@ struct ConstExprValue {...@@ -69,6 +75,7 @@ struct ConstExprValue {
69 FnTableEntry *x_fn;75 FnTableEntry *x_fn;
70 TypeTableEntry *x_type;76 TypeTableEntry *x_type;
71 ConstExprValue *x_maybe;77 ConstExprValue *x_maybe;
78 ConstEnumValue x_enum;
72 } data;79 } data;
73};80};
7481
...@@ -426,6 +433,10 @@ struct AstNodeSwitchProng {...@@ -426,6 +433,10 @@ struct AstNodeSwitchProng {
426 ZigList<AstNode *> items;433 ZigList<AstNode *> items;
427 AstNode *var_symbol;434 AstNode *var_symbol;
428 AstNode *expr;435 AstNode *expr;
436
437 // populated by semantic analyzer
438 BlockContext *block_context;
439 VariableTableEntry *var;
429};440};
430441
431struct AstNodeSwitchRange {442struct AstNodeSwitchRange {
...@@ -933,6 +944,7 @@ struct CodeGen {...@@ -933,6 +944,7 @@ struct CodeGen {
933944
934 OutType out_type;945 OutType out_type;
935 FnTableEntry *cur_fn;946 FnTableEntry *cur_fn;
947 // TODO remove this in favor of get_resolved_expr(expr_node)->context
936 BlockContext *cur_block_context;948 BlockContext *cur_block_context;
937 ZigList<LLVMBasicBlockRef> break_block_stack;949 ZigList<LLVMBasicBlockRef> break_block_stack;
938 ZigList<LLVMBasicBlockRef> continue_block_stack;950 ZigList<LLVMBasicBlockRef> continue_block_stack;
src/analyze.cpp+80-1
...@@ -1350,6 +1350,11 @@ static TypeTableEntry *analyze_enum_value_expr(CodeGen *g, ImportTableEntry *imp...@@ -1350,6 +1350,11 @@ static TypeTableEntry *analyze_enum_value_expr(CodeGen *g, ImportTableEntry *imp
1350 buf_ptr(&enum_type->name),1350 buf_ptr(&enum_type->name),
1351 buf_ptr(field_name),1351 buf_ptr(field_name),
1352 buf_ptr(&type_enum_field->type_entry->name)));1352 buf_ptr(&type_enum_field->type_entry->name)));
1353 } else {
1354 Expr *expr = get_resolved_expr(field_access_node);
1355 expr->const_val.ok = true;
1356 expr->const_val.data.x_enum.tag = type_enum_field->value;
1357 expr->const_val.data.x_enum.payload = nullptr;
1353 }1358 }
1354 } else {1359 } else {
1355 add_node_error(g, field_access_node,1360 add_node_error(g, field_access_node,
...@@ -1945,6 +1950,25 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im...@@ -1945,6 +1950,25 @@ static TypeTableEntry *analyze_bool_bin_op_expr(CodeGen *g, ImportTableEntry *im
1945 }1950 }
1946 } else if (resolved_type->id == TypeTableEntryIdFloat) {1951 } else if (resolved_type->id == TypeTableEntryIdFloat) {
1947 answer = eval_bool_bin_op_float(op1_val->data.x_float, bin_op_type, op2_val->data.x_float);1952 answer = eval_bool_bin_op_float(op1_val->data.x_float, bin_op_type, op2_val->data.x_float);
1953 } else if (resolved_type->id == TypeTableEntryIdEnum) {
1954 ConstEnumValue *enum1 = &op1_val->data.x_enum;
1955 ConstEnumValue *enum2 = &op2_val->data.x_enum;
1956 bool are_equal = false;
1957 if (enum1->tag == enum2->tag) {
1958 TypeEnumField *enum_field = &op1_type->data.enumeration.fields[enum1->tag];
1959 if (enum_field->type_entry->size_in_bits > 0) {
1960 zig_panic("TODO const expr analyze enum special value for equality");
1961 } else {
1962 are_equal = true;
1963 }
1964 }
1965 if (bin_op_type == BinOpTypeCmpEq) {
1966 answer = are_equal;
1967 } else if (bin_op_type == BinOpTypeCmpNotEq) {
1968 answer = !are_equal;
1969 } else {
1970 zig_unreachable();
1971 }
1948 } else {1972 } else {
1949 zig_unreachable();1973 zig_unreachable();
1950 }1974 }
...@@ -3017,7 +3041,62 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo...@@ -3017,7 +3041,62 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo
3017static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,3041static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
3018 TypeTableEntry *expected_type, AstNode *node)3042 TypeTableEntry *expected_type, AstNode *node)
3019{3043{
3020 zig_panic("TODO analyze_switch_expr");3044 AstNode *expr_node = node->data.switch_expr.expr;
3045 TypeTableEntry *expr_type = analyze_expression(g, import, context, nullptr, expr_node);
3046
3047 if (expected_type == nullptr) {
3048 zig_panic("TODO resolve peer compatibility of switch prongs");
3049 }
3050
3051 if (expr_type->id == TypeTableEntryIdInvalid) {
3052 return expr_type;
3053 } else if (expr_type->id == TypeTableEntryIdUnreachable) {
3054 add_node_error(g, first_executing_node(expr_node),
3055 buf_sprintf("switch on unreachable expression not allowed"));
3056 return g->builtin_types.entry_invalid;
3057 } else {
3058 AstNode *else_prong = nullptr;
3059 for (int prong_i = 0; prong_i < node->data.switch_expr.prongs.length; prong_i += 1) {
3060 AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
3061
3062 TypeTableEntry *var_type;
3063 if (prong_node->data.switch_prong.items.length == 0) {
3064 if (else_prong) {
3065 add_node_error(g, prong_node, buf_sprintf("multiple else prongs in switch expression"));
3066 } else {
3067 else_prong = prong_node;
3068 }
3069 var_type = expr_type;
3070 } else {
3071 for (int item_i = 0; item_i < prong_node->data.switch_prong.items.length; item_i += 1) {
3072 AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
3073 if (item_node->type == NodeTypeSwitchRange) {
3074 zig_panic("TODO range in switch statement");
3075 }
3076 analyze_expression(g, import, context, expr_type, item_node);
3077 ConstExprValue *const_val = &get_resolved_expr(item_node)->const_val;
3078 if (!const_val->ok) {
3079 add_node_error(g, item_node, buf_sprintf("unable to resolve constant expression"));
3080 }
3081 }
3082 var_type = expr_type;
3083 }
3084
3085 BlockContext *child_context = new_block_context(node, context);
3086 prong_node->data.switch_prong.block_context = child_context;
3087 AstNode *var_node = prong_node->data.switch_prong.var_symbol;
3088 if (var_node) {
3089 assert(var_node->type == NodeTypeSymbol);
3090 Buf *var_name = &var_node->data.symbol_expr.symbol;
3091 prong_node->data.switch_prong.var = add_local_var(g, var_node, child_context, var_name,
3092 var_type, true);
3093 }
3094
3095 analyze_expression(g, import, child_context, expected_type,
3096 prong_node->data.switch_prong.expr);
3097 }
3098 }
3099 return expected_type;
3021}3100}
30223101
3023static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,3102static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
src/codegen.cpp+63-1
...@@ -1968,7 +1968,69 @@ static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) {...@@ -1968,7 +1968,69 @@ static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) {
1968static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {1968static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
1969 assert(node->type == NodeTypeSwitchExpr);1969 assert(node->type == NodeTypeSwitchExpr);
19701970
1971 zig_panic("TODO gen_switch_expr");1971 LLVMValueRef target_value = gen_expr(g, node->data.switch_expr.expr);
1972
1973 bool end_unreachable = (get_expr_type(node)->id == TypeTableEntryIdUnreachable);
1974
1975 LLVMBasicBlockRef end_block = end_unreachable ?
1976 nullptr : LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchEnd");
1977 LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchElse");
1978 int prong_count = node->data.switch_expr.prongs.length;
1979
1980 add_debug_source_node(g, node);
1981 LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, target_value, else_block, prong_count);
1982
1983 ZigList<LLVMValueRef> incoming_values = {0};
1984 ZigList<LLVMBasicBlockRef> incoming_blocks = {0};
1985
1986 AstNode *else_prong = nullptr;
1987 for (int prong_i = 0; prong_i < prong_count; prong_i += 1) {
1988 AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i);
1989 LLVMBasicBlockRef prong_block;
1990 if (prong_node->data.switch_prong.items.length == 0) {
1991 assert(!else_prong);
1992 else_prong = prong_node;
1993 prong_block = else_block;
1994 } else {
1995 prong_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "SwitchProng");
1996 for (int item_i = 0; item_i < prong_node->data.switch_prong.items.length; item_i += 1) {
1997 AstNode *item_node = prong_node->data.switch_prong.items.at(item_i);
1998 assert(item_node->type != NodeTypeSwitchRange);
1999 assert(get_resolved_expr(item_node)->const_val.ok);
2000 LLVMValueRef val = gen_expr(g, item_node);
2001 LLVMAddCase(switch_instr, val, prong_block);
2002 }
2003 }
2004 assert(!prong_node->data.switch_prong.var_symbol);
2005 LLVMPositionBuilderAtEnd(g->builder, prong_block);
2006 AstNode *prong_expr = prong_node->data.switch_prong.expr;
2007 LLVMValueRef prong_val = gen_expr(g, prong_expr);
2008
2009 if (get_expr_type(prong_expr)->id != TypeTableEntryIdUnreachable) {
2010 add_debug_source_node(g, prong_expr);
2011 LLVMBuildBr(g->builder, end_block);
2012 incoming_values.append(prong_val);
2013 incoming_blocks.append(prong_block);
2014 }
2015 }
2016
2017 if (!else_prong) {
2018 LLVMPositionBuilderAtEnd(g->builder, else_block);
2019 add_debug_source_node(g, node);
2020 LLVMBuildUnreachable(g->builder);
2021 }
2022
2023 if (end_unreachable) {
2024 return nullptr;
2025 }
2026
2027 LLVMPositionBuilderAtEnd(g->builder, end_block);
2028
2029 add_debug_source_node(g, node);
2030 LLVMValueRef phi = LLVMBuildPhi(g->builder, get_expr_type(node)->type_ref, "");
2031 LLVMAddIncoming(phi, incoming_values.items, incoming_blocks.items, incoming_values.length);
2032
2033 return phi;
1972}2034}
19732035
1974static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {2036static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) {
src/parser.cpp+2-2
...@@ -2255,8 +2255,8 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, int *token_index, bool m...@@ -2255,8 +2255,8 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, int *token_index, bool m
2255 break;2255 break;
2256 }2256 }
22572257
2258 Token *arrow_or_comma = &pc->tokens->at(*token_index);2258 Token *arrow_or_colon = &pc->tokens->at(*token_index);
2259 if (arrow_or_comma->id == TokenIdComma) {2259 if (arrow_or_colon->id == TokenIdColon) {
2260 *token_index += 1;2260 *token_index += 1;
2261 ast_eat_token(pc, token_index, TokenIdLParen);2261 ast_eat_token(pc, token_index, TokenIdLParen);
2262 prong_node->data.switch_prong.var_symbol = ast_parse_symbol(pc, token_index);2262 prong_node->data.switch_prong.var_symbol = ast_parse_symbol(pc, token_index);
test/run_tests.cpp+37
...@@ -1180,6 +1180,33 @@ fn fn2() u32 => {6}...@@ -1180,6 +1180,33 @@ fn fn2() u32 => {6}
1180fn fn3() u32 => {7}1180fn fn3() u32 => {7}
1181fn fn4() u32 => {8}1181fn fn4() u32 => {8}
1182 )SOURCE", "5\n6\n7\n8\n");1182 )SOURCE", "5\n6\n7\n8\n");
1183
1184 add_simple_case("switch statement", R"SOURCE(
1185import "std.zig";
1186
1187enum Foo {
1188 A,
1189 B,
1190 C,
1191 D,
1192}
1193
1194pub fn main(args: [][]u8) i32 => {
1195 const foo = Foo.C;
1196 const val: i32 = switch (foo) {
1197 Foo.A => 1,
1198 Foo.B => 2,
1199 Foo.C => 3,
1200 Foo.D => 4,
1201 };
1202 if (val != 3) {
1203 print_str("BAD\n");
1204 }
1205
1206 print_str("OK\n");
1207 return 0;
1208}
1209 )SOURCE", "OK\n");
1183}1210}
11841211
11851212
...@@ -1511,6 +1538,16 @@ fn f(Foo: i32) => {...@@ -1511,6 +1538,16 @@ fn f(Foo: i32) => {
1511}1538}
1512 )SOURCE", 2, ".tmp_source.zig:5:6: error: variable shadows type 'Foo'",1539 )SOURCE", 2, ".tmp_source.zig:5:6: error: variable shadows type 'Foo'",
1513 ".tmp_source.zig:6:5: error: variable shadows type 'Bar'");1540 ".tmp_source.zig:6:5: error: variable shadows type 'Bar'");
1541
1542 add_compile_fail_case("multiple else prongs in a switch", R"SOURCE(
1543fn f() => {
1544 const value: bool = switch (u32(111)) {
1545 1234 => false,
1546 else => true,
1547 else => true,
1548 };
1549}
1550 )SOURCE", 1, ".tmp_source.zig:6:9: error: multiple else prongs in switch expression");
1514}1551}
15151552
1516static void print_compiler_invocation(TestCase *test_case) {1553static void print_compiler_invocation(TestCase *test_case) {