| author | |
| committer | |
| log | e411467e1dd4557bb11698f2a5d979dfcbaea444 |
| tree | 8b9bf14a1dee5310a8e18099fc354e1b9b1d2152 |
| parent | 3d8eb10897a86b2616c6a1aa843b7ebe4134ac51 |
it gets implicitly casted to whatever is needed.
closes #2417 files changed, 892 insertions(+), 235 deletions(-)
doc/langref.md+8-7| ... | @@ -200,10 +200,11 @@ as | ... | @@ -200,10 +200,11 @@ as |
| 200 | 200 | ||
| 201 | ### Numbers | 201 | ### Numbers |
| 202 | 202 | ||
| 203 | Number literals | Example | Exponentiation | 203 | Number literals | Example | Exponentiation |
| 204 | ------------------------------------------- | 204 | -------------------------------------------------- |
| 205 | Decimal integer | 98222 | N/A | 205 | Decimal integer | 98222 | N/A |
| 206 | Hex integer | 0xff | N/A | 206 | Hex integer | 0xff | N/A |
| 207 | Octal integer | 0o77 | N/A | 207 | Octal integer | 0o77 | N/A |
| 208 | Binary integer | 0b11110000 | N/A | 208 | Binary integer | 0b11110000 | N/A |
| 209 | Floating-point | 123.0E+77 | Optional | 209 | Floating-point | 123.0E+77 | Optional |
| 210 | Hex floating point | TODO | TODO |
example/arrays/arrays.zig+8-11| ... | @@ -1,15 +1,11 @@ | ... | @@ -1,15 +1,11 @@ |
| 1 | export executable "arrays"; | 1 | export executable "arrays"; |
| 2 | 2 | ||
| 3 | #link("c") | 3 | use "std.zig"; |
| 4 | extern { | ||
| 5 | fn puts(s: *const u8) -> i32; | ||
| 6 | fn exit(code: i32) -> unreachable; | ||
| 7 | } | ||
| 8 | 4 | ||
| 9 | export fn _start() -> unreachable { | 5 | export fn main(argc: isize, argv: *mut *mut u8, env: *mut *mut u8) -> i32 { |
| 10 | let mut array : [i32; 5]; | 6 | let mut array : [i32; 5]; |
| 11 | 7 | ||
| 12 | let mut i = 0; | 8 | let mut i : i32 = 0; |
| 13 | loop_start: | 9 | loop_start: |
| 14 | if i == 5 { | 10 | if i == 5 { |
| 15 | goto loop_end; | 11 | goto loop_end; |
| ... | @@ -21,21 +17,22 @@ loop_start: | ... | @@ -21,21 +17,22 @@ loop_start: |
| 21 | loop_end: | 17 | loop_end: |
| 22 | 18 | ||
| 23 | i = 0; | 19 | i = 0; |
| 24 | let mut accumulator = 0; | 20 | let mut accumulator : i32 = 0; |
| 25 | loop_2_start: | 21 | loop_2_start: |
| 26 | if i == 5 { | 22 | if i == 5 { |
| 27 | goto loop_2_end; | 23 | goto loop_2_end; |
| 28 | } | 24 | } |
| 29 | 25 | ||
| 30 | accumulator = accumulator + array[i]; | 26 | accumulator += array[i]; |
| 31 | 27 | ||
| 32 | i = i + 1; | 28 | i = i + 1; |
| 33 | goto loop_2_start; | 29 | goto loop_2_start; |
| 34 | loop_2_end: | 30 | loop_2_end: |
| 35 | 31 | ||
| 36 | if accumulator == 15 { | 32 | if accumulator == 15 { |
| 37 | puts("OK"); | 33 | print_str("OK" as string); |
| 38 | } | 34 | } |
| 39 | 35 | ||
| 40 | exit(0); | 36 | |
| 37 | return 0; | ||
| 41 | } | 38 | } |
example/expressions/expressions.zig+7-7| ... | @@ -14,19 +14,19 @@ fn other_exit() -> unreachable { | ... | @@ -14,19 +14,19 @@ fn other_exit() -> unreachable { |
| 14 | 14 | ||
| 15 | export fn _start() -> unreachable { | 15 | export fn _start() -> unreachable { |
| 16 | let a : i32 = 1; | 16 | let a : i32 = 1; |
| 17 | let b = 2; | 17 | let b = 2 as i32; |
| 18 | // let c : i32; // not yet support for const variables | 18 | // let c : i32; // not yet support for const variables |
| 19 | // let d; // parse error | 19 | // let d; // parse error |
| 20 | if (a + b == 3) { | 20 | if (a + b == 3) { |
| 21 | let no_conflict = 5; | 21 | let no_conflict : i32 = 5; |
| 22 | if (no_conflict == 5) { puts("OK 1"); } | 22 | if (no_conflict == 5) { puts(c"OK 1"); } |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | let c = { | 25 | let c = { |
| 26 | let no_conflict = 10; | 26 | let no_conflict : i32 = 10; |
| 27 | no_conflict | 27 | no_conflict |
| 28 | }; | 28 | }; |
| 29 | if (c == 10) { puts("OK 2"); } | 29 | if (c == 10) { puts(c"OK 2"); } |
| 30 | 30 | ||
| 31 | void_fun(1, void, 2); | 31 | void_fun(1, void, 2); |
| 32 | 32 | ||
| ... | @@ -44,12 +44,12 @@ fn void_fun(a : i32, b : void, c : i32) -> void { | ... | @@ -44,12 +44,12 @@ fn void_fun(a : i32, b : void, c : i32) -> void { |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | fn test_mutable_vars() { | 46 | fn test_mutable_vars() { |
| 47 | let mut i = 0; | 47 | let mut i : i32 = 0; |
| 48 | loop_start: | 48 | loop_start: |
| 49 | if i == 3 { | 49 | if i == 3 { |
| 50 | goto done; | 50 | goto done; |
| 51 | } | 51 | } |
| 52 | puts("loop"); | 52 | puts(c"loop"); |
| 53 | i = i + 1; | 53 | i = i + 1; |
| 54 | goto loop_start; | 54 | goto loop_start; |
| 55 | done: | 55 | done: |
example/hello_world/hello_libc.zig+1-1| ... | @@ -7,6 +7,6 @@ extern { | ... | @@ -7,6 +7,6 @@ extern { |
| 7 | } | 7 | } |
| 8 | 8 | ||
| 9 | export fn _start() -> unreachable { | 9 | export fn _start() -> unreachable { |
| 10 | printf("Hello, world!\n"); | 10 | printf(c"Hello, world!\n"); |
| 11 | exit(0); | 11 | exit(0); |
| 12 | } | 12 | } |
example/multiple_files/foo.zig+1-1| ... | @@ -3,7 +3,7 @@ use "libc.zig"; | ... | @@ -3,7 +3,7 @@ use "libc.zig"; |
| 3 | // purposefully conflicting function with main.zig | 3 | // purposefully conflicting function with main.zig |
| 4 | // but it's private so it should be OK | 4 | // but it's private so it should be OK |
| 5 | fn private_function() { | 5 | fn private_function() { |
| 6 | puts("it works!"); | 6 | puts(c"it works!"); |
| 7 | } | 7 | } |
| 8 | 8 | ||
| 9 | pub fn print_text() { | 9 | pub fn print_text() { |
example/multiple_files/libc.zig+1-1| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | #link("c") | 1 | #link("c") |
| 2 | extern { | 2 | extern { |
| 3 | pub fn puts(s: *mut u8) -> i32; | 3 | pub fn puts(s: *const u8) -> i32; |
| 4 | pub fn exit(code: i32) -> unreachable; | 4 | pub fn exit(code: i32) -> unreachable; |
| 5 | } | 5 | } |
example/structs/structs.zig+1-1| ... | @@ -22,6 +22,6 @@ struct Foo { | ... | @@ -22,6 +22,6 @@ struct Foo { |
| 22 | 22 | ||
| 23 | fn test_foo(foo : Foo) { | 23 | fn test_foo(foo : Foo) { |
| 24 | if foo.b { | 24 | if foo.b { |
| 25 | print_str("OK" as string); | 25 | print_str("OK\n" as string); |
| 26 | } | 26 | } |
| 27 | } | 27 | } |
src/analyze.cpp+370-177| ... | @@ -122,7 +122,7 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool | ... | @@ -122,7 +122,7 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool |
| 122 | } | 122 | } |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, int array_size) { | 125 | static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size) { |
| 126 | auto existing_entry = child_type->arrays_by_size.maybe_get(array_size); | 126 | auto existing_entry = child_type->arrays_by_size.maybe_get(array_size); |
| 127 | if (existing_entry) { | 127 | if (existing_entry) { |
| 128 | return existing_entry->value; | 128 | return existing_entry->value; |
| ... | @@ -130,7 +130,7 @@ static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, in | ... | @@ -130,7 +130,7 @@ static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, in |
| 130 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray); | 130 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray); |
| 131 | entry->type_ref = LLVMArrayType(child_type->type_ref, array_size); | 131 | entry->type_ref = LLVMArrayType(child_type->type_ref, array_size); |
| 132 | buf_resize(&entry->name, 0); | 132 | buf_resize(&entry->name, 0); |
| 133 | buf_appendf(&entry->name, "[%s; %d]", buf_ptr(&child_type->name), array_size); | 133 | buf_appendf(&entry->name, "[%s; %" PRIu64 "]", buf_ptr(&child_type->name), array_size); |
| 134 | 134 | ||
| 135 | entry->size_in_bits = child_type->size_in_bits * array_size; | 135 | entry->size_in_bits = child_type->size_in_bits * array_size; |
| 136 | entry->align_in_bits = child_type->align_in_bits; | 136 | entry->align_in_bits = child_type->align_in_bits; |
| ... | @@ -145,11 +145,6 @@ static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, in | ... | @@ -145,11 +145,6 @@ static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, in |
| 145 | } | 145 | } |
| 146 | } | 146 | } |
| 147 | 147 | ||
| 148 | static int parse_int(Buf *number) { | ||
| 149 | // TODO: think about integer size of array sizes | ||
| 150 | return atoi(buf_ptr(number)); | ||
| 151 | } | ||
| 152 | |||
| 153 | static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) { | 148 | static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) { |
| 154 | assert(node->type == NodeTypeType); | 149 | assert(node->type == NodeTypeType); |
| 155 | alloc_codegen_node(node); | 150 | alloc_codegen_node(node); |
| ... | @@ -192,16 +187,15 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) { | ... | @@ -192,16 +187,15 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) { |
| 192 | } | 187 | } |
| 193 | 188 | ||
| 194 | AstNode *size_node = node->data.type.array_size; | 189 | AstNode *size_node = node->data.type.array_size; |
| 195 | int size; // TODO: think about integer size of array sizes | 190 | if (size_node->type == NodeTypeNumberLiteral && |
| 196 | if (size_node->type != NodeTypeNumberLiteral) { | 191 | is_num_lit_unsigned(size_node->data.number_literal.kind)) |
| 197 | add_node_error(g, size_node, | 192 | { |
| 198 | buf_create_from_str("array size must be literal number")); | 193 | type_node->entry = get_array_type(g, child_type, size_node->data.number_literal.data.x_uint); |
| 199 | size = -1; | ||
| 200 | } else { | 194 | } else { |
| 201 | size = parse_int(&size_node->data.number); | 195 | add_node_error(g, size_node, |
| 196 | buf_create_from_str("array size must be literal unsigned integer")); | ||
| 197 | type_node->entry = g->builtin_types.entry_invalid; | ||
| 202 | } | 198 | } |
| 203 | |||
| 204 | type_node->entry = get_array_type(g, child_type, size); | ||
| 205 | return type_node->entry; | 199 | return type_node->entry; |
| 206 | } | 200 | } |
| 207 | } | 201 | } |
| ... | @@ -625,6 +619,44 @@ static void get_struct_field(TypeTableEntry *struct_type, Buf *name, TypeStructF | ... | @@ -625,6 +619,44 @@ static void get_struct_field(TypeTableEntry *struct_type, Buf *name, TypeStructF |
| 625 | *out_i = -1; | 619 | *out_i = -1; |
| 626 | } | 620 | } |
| 627 | 621 | ||
| 622 | static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type, TypeTableEntry *other_type) { | ||
| 623 | NumLit num_lit = literal_type->data.num_lit.kind; | ||
| 624 | uint64_t lit_size_in_bits = num_lit_bit_count(num_lit); | ||
| 625 | |||
| 626 | switch (other_type->id) { | ||
| 627 | case TypeTableEntryIdInvalid: | ||
| 628 | case TypeTableEntryIdNumberLiteral: | ||
| 629 | zig_unreachable(); | ||
| 630 | case TypeTableEntryIdVoid: | ||
| 631 | case TypeTableEntryIdBool: | ||
| 632 | case TypeTableEntryIdUnreachable: | ||
| 633 | case TypeTableEntryIdPointer: | ||
| 634 | case TypeTableEntryIdArray: | ||
| 635 | case TypeTableEntryIdStruct: | ||
| 636 | return false; | ||
| 637 | case TypeTableEntryIdInt: | ||
| 638 | if (is_num_lit_signed(num_lit)) { | ||
| 639 | if (!other_type->data.integral.is_signed) { | ||
| 640 | return false; | ||
| 641 | } | ||
| 642 | |||
| 643 | return lit_size_in_bits <= other_type->size_in_bits; | ||
| 644 | } else if (is_num_lit_unsigned(num_lit)) { | ||
| 645 | |||
| 646 | return lit_size_in_bits <= other_type->size_in_bits; | ||
| 647 | } else { | ||
| 648 | return false; | ||
| 649 | } | ||
| 650 | case TypeTableEntryIdFloat: | ||
| 651 | if (is_num_lit_float(num_lit)) { | ||
| 652 | return lit_size_in_bits <= other_type->size_in_bits; | ||
| 653 | } else { | ||
| 654 | return false; | ||
| 655 | } | ||
| 656 | } | ||
| 657 | } | ||
| 658 | |||
| 659 | |||
| 628 | static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | 660 | static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 629 | AstNode *node) | 661 | AstNode *node) |
| 630 | { | 662 | { |
| ... | @@ -790,6 +822,16 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B | ... | @@ -790,6 +822,16 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B |
| 790 | cast_node->op = CastOpArrayToString; | 822 | cast_node->op = CastOpArrayToString; |
| 791 | context->cast_expr_alloca_list.append(node); | 823 | context->cast_expr_alloca_list.append(node); |
| 792 | return wanted_type; | 824 | return wanted_type; |
| 825 | } else if (actual_type->id == TypeTableEntryIdNumberLiteral && | ||
| 826 | num_lit_fits_in_other_type(g, actual_type, wanted_type)) | ||
| 827 | { | ||
| 828 | AstNode *literal_node = node->data.cast_expr.expr; | ||
| 829 | assert(literal_node->codegen_node); | ||
| 830 | NumberLiteralNode *codegen_num_lit = &literal_node->codegen_node->data.num_lit_node; | ||
| 831 | assert(!codegen_num_lit->resolved_type); | ||
| 832 | codegen_num_lit->resolved_type = wanted_type; | ||
| 833 | cast_node->op = CastOpNothing; | ||
| 834 | return wanted_type; | ||
| 793 | } else { | 835 | } else { |
| 794 | add_node_error(g, node, | 836 | add_node_error(g, node, |
| 795 | buf_sprintf("invalid cast from type '%s' to '%s'", | 837 | buf_sprintf("invalid cast from type '%s' to '%s'", |
| ... | @@ -799,6 +841,314 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B | ... | @@ -799,6 +841,314 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B |
| 799 | } | 841 | } |
| 800 | } | 842 | } |
| 801 | 843 | ||
| 844 | static TypeTableEntry * resolve_rhs_number_literal(CodeGen *g, AstNode *non_literal_node, | ||
| 845 | TypeTableEntry *non_literal_type, AstNode *literal_node, TypeTableEntry *literal_type) | ||
| 846 | { | ||
| 847 | assert(literal_node->codegen_node); | ||
| 848 | NumberLiteralNode *codegen_num_lit = &literal_node->codegen_node->data.num_lit_node; | ||
| 849 | |||
| 850 | if (num_lit_fits_in_other_type(g, literal_type, non_literal_type)) { | ||
| 851 | assert(!codegen_num_lit->resolved_type); | ||
| 852 | codegen_num_lit->resolved_type = non_literal_type; | ||
| 853 | return non_literal_type; | ||
| 854 | } else { | ||
| 855 | return nullptr; | ||
| 856 | } | ||
| 857 | } | ||
| 858 | |||
| 859 | static TypeTableEntry * resolve_number_literals(CodeGen *g, AstNode *node1, AstNode *node2) { | ||
| 860 | TypeTableEntry *type1 = node1->codegen_node->expr_node.type_entry; | ||
| 861 | TypeTableEntry *type2 = node2->codegen_node->expr_node.type_entry; | ||
| 862 | |||
| 863 | if (type1->id == TypeTableEntryIdNumberLiteral && | ||
| 864 | type2->id == TypeTableEntryIdNumberLiteral) | ||
| 865 | { | ||
| 866 | assert(node1->codegen_node); | ||
| 867 | assert(node2->codegen_node); | ||
| 868 | |||
| 869 | NumberLiteralNode *codegen_num_lit_1 = &node1->codegen_node->data.num_lit_node; | ||
| 870 | NumberLiteralNode *codegen_num_lit_2 = &node2->codegen_node->data.num_lit_node; | ||
| 871 | |||
| 872 | assert(!codegen_num_lit_1->resolved_type); | ||
| 873 | assert(!codegen_num_lit_2->resolved_type); | ||
| 874 | |||
| 875 | if (is_num_lit_float(type1->data.num_lit.kind) && | ||
| 876 | is_num_lit_float(type2->data.num_lit.kind)) | ||
| 877 | { | ||
| 878 | codegen_num_lit_1->resolved_type = g->builtin_types.entry_f64; | ||
| 879 | codegen_num_lit_2->resolved_type = g->builtin_types.entry_f64; | ||
| 880 | return g->builtin_types.entry_f64; | ||
| 881 | } else if (is_num_lit_signed(type1->data.num_lit.kind) && | ||
| 882 | is_num_lit_signed(type2->data.num_lit.kind)) | ||
| 883 | { | ||
| 884 | codegen_num_lit_1->resolved_type = g->builtin_types.entry_i64; | ||
| 885 | codegen_num_lit_2->resolved_type = g->builtin_types.entry_i64; | ||
| 886 | return g->builtin_types.entry_i64; | ||
| 887 | } else if (is_num_lit_unsigned(type1->data.num_lit.kind) && | ||
| 888 | is_num_lit_unsigned(type2->data.num_lit.kind)) | ||
| 889 | { | ||
| 890 | codegen_num_lit_1->resolved_type = g->builtin_types.entry_u64; | ||
| 891 | codegen_num_lit_2->resolved_type = g->builtin_types.entry_u64; | ||
| 892 | return g->builtin_types.entry_u64; | ||
| 893 | } else { | ||
| 894 | return nullptr; | ||
| 895 | } | ||
| 896 | } else if (type1->id == TypeTableEntryIdNumberLiteral) { | ||
| 897 | return resolve_rhs_number_literal(g, node2, type2, node1, type1); | ||
| 898 | } else { | ||
| 899 | assert(type2->id == TypeTableEntryIdNumberLiteral); | ||
| 900 | return resolve_rhs_number_literal(g, node1, type1, node2, type2); | ||
| 901 | } | ||
| 902 | } | ||
| 903 | |||
| 904 | static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | ||
| 905 | TypeTableEntry *expected_type, AstNode *node) | ||
| 906 | { | ||
| 907 | switch (node->data.bin_op_expr.bin_op) { | ||
| 908 | case BinOpTypeAssign: | ||
| 909 | case BinOpTypeAssignTimes: | ||
| 910 | case BinOpTypeAssignDiv: | ||
| 911 | case BinOpTypeAssignMod: | ||
| 912 | case BinOpTypeAssignPlus: | ||
| 913 | case BinOpTypeAssignMinus: | ||
| 914 | case BinOpTypeAssignBitShiftLeft: | ||
| 915 | case BinOpTypeAssignBitShiftRight: | ||
| 916 | case BinOpTypeAssignBitAnd: | ||
| 917 | case BinOpTypeAssignBitXor: | ||
| 918 | case BinOpTypeAssignBitOr: | ||
| 919 | case BinOpTypeAssignBoolAnd: | ||
| 920 | case BinOpTypeAssignBoolOr: | ||
| 921 | { | ||
| 922 | AstNode *lhs_node = node->data.bin_op_expr.op1; | ||
| 923 | TypeTableEntry *expected_rhs_type = nullptr; | ||
| 924 | if (lhs_node->type == NodeTypeSymbol) { | ||
| 925 | Buf *name = &lhs_node->data.symbol; | ||
| 926 | LocalVariableTableEntry *var = find_local_variable(context, name); | ||
| 927 | if (var) { | ||
| 928 | if (var->is_const) { | ||
| 929 | add_node_error(g, lhs_node, | ||
| 930 | buf_sprintf("cannot assign to constant variable")); | ||
| 931 | } else { | ||
| 932 | if (!is_op_allowed(var->type, node->data.bin_op_expr.bin_op)) { | ||
| 933 | if (var->type->id != TypeTableEntryIdInvalid) { | ||
| 934 | add_node_error(g, lhs_node, | ||
| 935 | buf_sprintf("operator not allowed for type '%s'", | ||
| 936 | buf_ptr(&var->type->name))); | ||
| 937 | } | ||
| 938 | } else { | ||
| 939 | expected_rhs_type = var->type; | ||
| 940 | } | ||
| 941 | } | ||
| 942 | } else { | ||
| 943 | add_node_error(g, lhs_node, | ||
| 944 | buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name))); | ||
| 945 | } | ||
| 946 | } else if (lhs_node->type == NodeTypeArrayAccessExpr) { | ||
| 947 | expected_rhs_type = analyze_array_access_expr(g, import, context, lhs_node); | ||
| 948 | } else if (lhs_node->type == NodeTypeFieldAccessExpr) { | ||
| 949 | alloc_codegen_node(lhs_node); | ||
| 950 | expected_rhs_type = analyze_field_access_expr(g, import, context, lhs_node); | ||
| 951 | } else { | ||
| 952 | add_node_error(g, lhs_node, | ||
| 953 | buf_sprintf("assignment target must be variable, field, or array element")); | ||
| 954 | } | ||
| 955 | analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2); | ||
| 956 | return g->builtin_types.entry_void; | ||
| 957 | } | ||
| 958 | case BinOpTypeBoolOr: | ||
| 959 | case BinOpTypeBoolAnd: | ||
| 960 | analyze_expression(g, import, context, g->builtin_types.entry_bool, | ||
| 961 | node->data.bin_op_expr.op1); | ||
| 962 | analyze_expression(g, import, context, g->builtin_types.entry_bool, | ||
| 963 | node->data.bin_op_expr.op2); | ||
| 964 | return g->builtin_types.entry_bool; | ||
| 965 | case BinOpTypeCmpEq: | ||
| 966 | case BinOpTypeCmpNotEq: | ||
| 967 | case BinOpTypeCmpLessThan: | ||
| 968 | case BinOpTypeCmpGreaterThan: | ||
| 969 | case BinOpTypeCmpLessOrEq: | ||
| 970 | case BinOpTypeCmpGreaterOrEq: | ||
| 971 | { | ||
| 972 | AstNode *op1 = node->data.bin_op_expr.op1; | ||
| 973 | AstNode *op2 = node->data.bin_op_expr.op2; | ||
| 974 | TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, op1); | ||
| 975 | TypeTableEntry *rhs_type = analyze_expression(g, import, context, nullptr, op2); | ||
| 976 | bool cmp_ok = false; | ||
| 977 | if (lhs_type->id == TypeTableEntryIdInvalid || rhs_type->id == TypeTableEntryIdInvalid) { | ||
| 978 | cmp_ok = true; | ||
| 979 | } else if (lhs_type->id == TypeTableEntryIdNumberLiteral || | ||
| 980 | rhs_type->id == TypeTableEntryIdNumberLiteral) | ||
| 981 | { | ||
| 982 | cmp_ok = resolve_number_literals(g, op1, op2); | ||
| 983 | } else if (lhs_type->id == TypeTableEntryIdInt) { | ||
| 984 | if (rhs_type->id == TypeTableEntryIdInt && | ||
| 985 | lhs_type->data.integral.is_signed == rhs_type->data.integral.is_signed && | ||
| 986 | lhs_type->size_in_bits == rhs_type->size_in_bits) | ||
| 987 | { | ||
| 988 | cmp_ok = true; | ||
| 989 | } | ||
| 990 | } else if (lhs_type->id == TypeTableEntryIdFloat) { | ||
| 991 | if (rhs_type->id == TypeTableEntryIdFloat && | ||
| 992 | lhs_type->size_in_bits == rhs_type->size_in_bits) | ||
| 993 | { | ||
| 994 | cmp_ok = true; | ||
| 995 | } | ||
| 996 | } | ||
| 997 | if (!cmp_ok) { | ||
| 998 | add_node_error(g, node, buf_sprintf("unable to compare '%s' with '%s'", | ||
| 999 | buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name))); | ||
| 1000 | } | ||
| 1001 | return g->builtin_types.entry_bool; | ||
| 1002 | } | ||
| 1003 | case BinOpTypeBinOr: | ||
| 1004 | case BinOpTypeBinXor: | ||
| 1005 | case BinOpTypeBinAnd: | ||
| 1006 | { | ||
| 1007 | // TODO: don't require i32 | ||
| 1008 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1); | ||
| 1009 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2); | ||
| 1010 | return g->builtin_types.entry_i32; | ||
| 1011 | } | ||
| 1012 | case BinOpTypeBitShiftLeft: | ||
| 1013 | case BinOpTypeBitShiftRight: | ||
| 1014 | { | ||
| 1015 | // TODO: don't require i32 | ||
| 1016 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1); | ||
| 1017 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2); | ||
| 1018 | return g->builtin_types.entry_i32; | ||
| 1019 | } | ||
| 1020 | case BinOpTypeAdd: | ||
| 1021 | case BinOpTypeSub: | ||
| 1022 | { | ||
| 1023 | AstNode *op1 = node->data.bin_op_expr.op1; | ||
| 1024 | AstNode *op2 = node->data.bin_op_expr.op2; | ||
| 1025 | TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, op1); | ||
| 1026 | TypeTableEntry *rhs_type = analyze_expression(g, import, context, nullptr, op2); | ||
| 1027 | |||
| 1028 | TypeTableEntry *return_type = nullptr; | ||
| 1029 | |||
| 1030 | if (lhs_type->id == TypeTableEntryIdInvalid || rhs_type->id == TypeTableEntryIdInvalid) { | ||
| 1031 | return_type = g->builtin_types.entry_invalid; | ||
| 1032 | } else if (lhs_type->id == TypeTableEntryIdNumberLiteral || | ||
| 1033 | rhs_type->id == TypeTableEntryIdNumberLiteral) | ||
| 1034 | { | ||
| 1035 | return_type = resolve_number_literals(g, op1, op2); | ||
| 1036 | } else if (lhs_type->id == TypeTableEntryIdInt && | ||
| 1037 | lhs_type == rhs_type) | ||
| 1038 | { | ||
| 1039 | return_type = lhs_type; | ||
| 1040 | } else if (lhs_type->id == TypeTableEntryIdFloat && | ||
| 1041 | lhs_type == rhs_type) | ||
| 1042 | { | ||
| 1043 | return_type = lhs_type; | ||
| 1044 | } | ||
| 1045 | if (!return_type) { | ||
| 1046 | if (node->data.bin_op_expr.bin_op == BinOpTypeAdd) { | ||
| 1047 | add_node_error(g, node, buf_sprintf("unable to add '%s' and '%s'", | ||
| 1048 | buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name))); | ||
| 1049 | } else { | ||
| 1050 | add_node_error(g, node, buf_sprintf("unable to subtract '%s' and '%s'", | ||
| 1051 | buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name))); | ||
| 1052 | } | ||
| 1053 | return g->builtin_types.entry_invalid; | ||
| 1054 | } | ||
| 1055 | return return_type; | ||
| 1056 | } | ||
| 1057 | case BinOpTypeMult: | ||
| 1058 | case BinOpTypeDiv: | ||
| 1059 | case BinOpTypeMod: | ||
| 1060 | { | ||
| 1061 | // TODO: don't require i32 | ||
| 1062 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1); | ||
| 1063 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2); | ||
| 1064 | return g->builtin_types.entry_i32; | ||
| 1065 | } | ||
| 1066 | case BinOpTypeInvalid: | ||
| 1067 | zig_unreachable(); | ||
| 1068 | } | ||
| 1069 | zig_unreachable(); | ||
| 1070 | } | ||
| 1071 | |||
| 1072 | static TypeTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import, BlockContext *context, | ||
| 1073 | TypeTableEntry *expected_type, AstNode *node) | ||
| 1074 | { | ||
| 1075 | AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration; | ||
| 1076 | |||
| 1077 | TypeTableEntry *explicit_type = nullptr; | ||
| 1078 | if (variable_declaration->type != nullptr) { | ||
| 1079 | explicit_type = resolve_type(g, variable_declaration->type); | ||
| 1080 | if (explicit_type->id == TypeTableEntryIdUnreachable) { | ||
| 1081 | add_node_error(g, variable_declaration->type, | ||
| 1082 | buf_sprintf("variable of type 'unreachable' not allowed")); | ||
| 1083 | explicit_type = g->builtin_types.entry_invalid; | ||
| 1084 | } | ||
| 1085 | } | ||
| 1086 | |||
| 1087 | TypeTableEntry *implicit_type = nullptr; | ||
| 1088 | if (variable_declaration->expr != nullptr) { | ||
| 1089 | implicit_type = analyze_expression(g, import, context, explicit_type, variable_declaration->expr); | ||
| 1090 | if (implicit_type->id == TypeTableEntryIdUnreachable) { | ||
| 1091 | add_node_error(g, node, | ||
| 1092 | buf_sprintf("variable initialization is unreachable")); | ||
| 1093 | implicit_type = g->builtin_types.entry_invalid; | ||
| 1094 | } else if (implicit_type->id == TypeTableEntryIdNumberLiteral) { | ||
| 1095 | add_node_error(g, node, | ||
| 1096 | buf_sprintf("unable to infer variable type")); | ||
| 1097 | implicit_type = g->builtin_types.entry_invalid; | ||
| 1098 | } | ||
| 1099 | } | ||
| 1100 | |||
| 1101 | if (implicit_type == nullptr && variable_declaration->is_const) { | ||
| 1102 | add_node_error(g, node, buf_sprintf("variables must have initial values or be declared 'mut'.")); | ||
| 1103 | implicit_type = g->builtin_types.entry_invalid; | ||
| 1104 | } | ||
| 1105 | |||
| 1106 | TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type; | ||
| 1107 | assert(type != nullptr); // should have been caught by the parser | ||
| 1108 | |||
| 1109 | LocalVariableTableEntry *existing_variable = find_local_variable(context, &variable_declaration->symbol); | ||
| 1110 | if (existing_variable) { | ||
| 1111 | add_node_error(g, node, | ||
| 1112 | buf_sprintf("redeclaration of variable '%s'", buf_ptr(&variable_declaration->symbol))); | ||
| 1113 | } else { | ||
| 1114 | LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1); | ||
| 1115 | buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol); | ||
| 1116 | variable_entry->type = type; | ||
| 1117 | variable_entry->is_const = variable_declaration->is_const; | ||
| 1118 | variable_entry->is_ptr = true; | ||
| 1119 | variable_entry->decl_node = node; | ||
| 1120 | context->variable_table.put(&variable_entry->name, variable_entry); | ||
| 1121 | } | ||
| 1122 | return g->builtin_types.entry_void; | ||
| 1123 | } | ||
| 1124 | |||
| 1125 | static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | ||
| 1126 | TypeTableEntry *expected_type, AstNode *node) | ||
| 1127 | { | ||
| 1128 | TypeTableEntry *num_lit_type = g->num_lit_types[node->data.number_literal.kind]; | ||
| 1129 | if (node->data.number_literal.overflow) { | ||
| 1130 | add_node_error(g, node, | ||
| 1131 | buf_sprintf("number literal too large to be represented in any type")); | ||
| 1132 | return g->builtin_types.entry_invalid; | ||
| 1133 | } else if (expected_type) { | ||
| 1134 | if (expected_type->id == TypeTableEntryIdInvalid) { | ||
| 1135 | return g->builtin_types.entry_invalid; | ||
| 1136 | } else if (num_lit_fits_in_other_type(g, num_lit_type, expected_type)) { | ||
| 1137 | NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node; | ||
| 1138 | assert(!codegen_num_lit->resolved_type); | ||
| 1139 | codegen_num_lit->resolved_type = expected_type; | ||
| 1140 | |||
| 1141 | return expected_type; | ||
| 1142 | } else { | ||
| 1143 | add_node_error(g, node, buf_sprintf("expected type '%s', got '%s'", | ||
| 1144 | buf_ptr(&expected_type->name), buf_ptr(&num_lit_type->name))); | ||
| 1145 | return g->builtin_types.entry_invalid; | ||
| 1146 | } | ||
| 1147 | } else { | ||
| 1148 | return num_lit_type; | ||
| 1149 | } | ||
| 1150 | } | ||
| 1151 | |||
| 802 | static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, | 1152 | static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 803 | TypeTableEntry *expected_type, AstNode *node) | 1153 | TypeTableEntry *expected_type, AstNode *node) |
| 804 | { | 1154 | { |
| ... | @@ -854,51 +1204,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -854,51 +1204,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 854 | break; | 1204 | break; |
| 855 | } | 1205 | } |
| 856 | case NodeTypeVariableDeclaration: | 1206 | case NodeTypeVariableDeclaration: |
| 857 | { | 1207 | return_type = analyze_variable_declaration(g, import, context, expected_type, node); |
| 858 | AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;; | 1208 | break; |
| 859 | |||
| 860 | TypeTableEntry *explicit_type = nullptr; | ||
| 861 | if (variable_declaration->type != nullptr) { | ||
| 862 | explicit_type = resolve_type(g, variable_declaration->type); | ||
| 863 | if (explicit_type->id == TypeTableEntryIdUnreachable) { | ||
| 864 | add_node_error(g, variable_declaration->type, | ||
| 865 | buf_sprintf("variable of type 'unreachable' not allowed")); | ||
| 866 | } | ||
| 867 | } | ||
| 868 | |||
| 869 | TypeTableEntry *implicit_type = nullptr; | ||
| 870 | if (variable_declaration->expr != nullptr) { | ||
| 871 | implicit_type = analyze_expression(g, import, context, explicit_type, variable_declaration->expr); | ||
| 872 | if (implicit_type->id == TypeTableEntryIdUnreachable) { | ||
| 873 | add_node_error(g, node, | ||
| 874 | buf_sprintf("variable initialization is unreachable")); | ||
| 875 | } | ||
| 876 | } | ||
| 877 | |||
| 878 | if (implicit_type == nullptr && variable_declaration->is_const) { | ||
| 879 | add_node_error(g, node, buf_sprintf("variables must have initial values or be declared 'mut'.")); | ||
| 880 | } | ||
| 881 | |||
| 882 | TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type; | ||
| 883 | assert(type != nullptr); // should have been caught by the parser | ||
| 884 | |||
| 885 | LocalVariableTableEntry *existing_variable = find_local_variable(context, &variable_declaration->symbol); | ||
| 886 | if (existing_variable) { | ||
| 887 | add_node_error(g, node, | ||
| 888 | buf_sprintf("redeclaration of variable '%s'", buf_ptr(&variable_declaration->symbol))); | ||
| 889 | } else { | ||
| 890 | LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1); | ||
| 891 | buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol); | ||
| 892 | variable_entry->type = type; | ||
| 893 | variable_entry->is_const = variable_declaration->is_const; | ||
| 894 | variable_entry->is_ptr = true; | ||
| 895 | variable_entry->decl_node = node; | ||
| 896 | context->variable_table.put(&variable_entry->name, variable_entry); | ||
| 897 | } | ||
| 898 | return_type = g->builtin_types.entry_void; | ||
| 899 | break; | ||
| 900 | } | ||
| 901 | |||
| 902 | case NodeTypeGoto: | 1209 | case NodeTypeGoto: |
| 903 | { | 1210 | { |
| 904 | FnTableEntry *fn_table_entry = get_context_fn_entry(context); | 1211 | FnTableEntry *fn_table_entry = get_context_fn_entry(context); |
| ... | @@ -928,120 +1235,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -928,120 +1235,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 928 | break; | 1235 | break; |
| 929 | } | 1236 | } |
| 930 | case NodeTypeBinOpExpr: | 1237 | case NodeTypeBinOpExpr: |
| 931 | { | 1238 | return_type = analyze_bin_op_expr(g, import, context, expected_type, node); |
| 932 | switch (node->data.bin_op_expr.bin_op) { | 1239 | break; |
| 933 | case BinOpTypeAssign: | ||
| 934 | case BinOpTypeAssignTimes: | ||
| 935 | case BinOpTypeAssignDiv: | ||
| 936 | case BinOpTypeAssignMod: | ||
| 937 | case BinOpTypeAssignPlus: | ||
| 938 | case BinOpTypeAssignMinus: | ||
| 939 | case BinOpTypeAssignBitShiftLeft: | ||
| 940 | case BinOpTypeAssignBitShiftRight: | ||
| 941 | case BinOpTypeAssignBitAnd: | ||
| 942 | case BinOpTypeAssignBitXor: | ||
| 943 | case BinOpTypeAssignBitOr: | ||
| 944 | case BinOpTypeAssignBoolAnd: | ||
| 945 | case BinOpTypeAssignBoolOr: | ||
| 946 | { | ||
| 947 | AstNode *lhs_node = node->data.bin_op_expr.op1; | ||
| 948 | TypeTableEntry *expected_rhs_type = nullptr; | ||
| 949 | if (lhs_node->type == NodeTypeSymbol) { | ||
| 950 | Buf *name = &lhs_node->data.symbol; | ||
| 951 | LocalVariableTableEntry *var = find_local_variable(context, name); | ||
| 952 | if (var) { | ||
| 953 | if (var->is_const) { | ||
| 954 | add_node_error(g, lhs_node, | ||
| 955 | buf_sprintf("cannot assign to constant variable")); | ||
| 956 | } else { | ||
| 957 | if (!is_op_allowed(var->type, node->data.bin_op_expr.bin_op)) { | ||
| 958 | add_node_error(g, lhs_node, | ||
| 959 | buf_sprintf("operator not allowed for type '%s'", buf_ptr(&var->type->name))); | ||
| 960 | } else { | ||
| 961 | expected_rhs_type = var->type; | ||
| 962 | } | ||
| 963 | } | ||
| 964 | } else { | ||
| 965 | add_node_error(g, lhs_node, | ||
| 966 | buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name))); | ||
| 967 | } | ||
| 968 | } else if (lhs_node->type == NodeTypeArrayAccessExpr) { | ||
| 969 | expected_rhs_type = analyze_array_access_expr(g, import, context, lhs_node); | ||
| 970 | } else if (lhs_node->type == NodeTypeFieldAccessExpr) { | ||
| 971 | alloc_codegen_node(lhs_node); | ||
| 972 | expected_rhs_type = analyze_field_access_expr(g, import, context, lhs_node); | ||
| 973 | } else { | ||
| 974 | add_node_error(g, lhs_node, | ||
| 975 | buf_sprintf("assignment target must be variable, field, or array element")); | ||
| 976 | } | ||
| 977 | analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2); | ||
| 978 | return_type = g->builtin_types.entry_void; | ||
| 979 | break; | ||
| 980 | } | ||
| 981 | case BinOpTypeBoolOr: | ||
| 982 | case BinOpTypeBoolAnd: | ||
| 983 | analyze_expression(g, import, context, g->builtin_types.entry_bool, | ||
| 984 | node->data.bin_op_expr.op1); | ||
| 985 | analyze_expression(g, import, context, g->builtin_types.entry_bool, | ||
| 986 | node->data.bin_op_expr.op2); | ||
| 987 | return_type = g->builtin_types.entry_bool; | ||
| 988 | break; | ||
| 989 | case BinOpTypeCmpEq: | ||
| 990 | case BinOpTypeCmpNotEq: | ||
| 991 | case BinOpTypeCmpLessThan: | ||
| 992 | case BinOpTypeCmpGreaterThan: | ||
| 993 | case BinOpTypeCmpLessOrEq: | ||
| 994 | case BinOpTypeCmpGreaterOrEq: | ||
| 995 | // TODO think how should type checking for these work? | ||
| 996 | analyze_expression(g, import, context, g->builtin_types.entry_i32, | ||
| 997 | node->data.bin_op_expr.op1); | ||
| 998 | analyze_expression(g, import, context, g->builtin_types.entry_i32, | ||
| 999 | node->data.bin_op_expr.op2); | ||
| 1000 | return_type = g->builtin_types.entry_bool; | ||
| 1001 | break; | ||
| 1002 | case BinOpTypeBinOr: | ||
| 1003 | case BinOpTypeBinXor: | ||
| 1004 | case BinOpTypeBinAnd: | ||
| 1005 | { | ||
| 1006 | // TODO: don't require i32 | ||
| 1007 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1); | ||
| 1008 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2); | ||
| 1009 | return_type = g->builtin_types.entry_i32; | ||
| 1010 | break; | ||
| 1011 | } | ||
| 1012 | case BinOpTypeBitShiftLeft: | ||
| 1013 | case BinOpTypeBitShiftRight: | ||
| 1014 | { | ||
| 1015 | // TODO: don't require i32 | ||
| 1016 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1); | ||
| 1017 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2); | ||
| 1018 | return_type = g->builtin_types.entry_i32; | ||
| 1019 | break; | ||
| 1020 | } | ||
| 1021 | case BinOpTypeAdd: | ||
| 1022 | case BinOpTypeSub: | ||
| 1023 | // TODO think how should type checking for these work? | ||
| 1024 | analyze_expression(g, import, context, g->builtin_types.entry_i32, | ||
| 1025 | node->data.bin_op_expr.op1); | ||
| 1026 | analyze_expression(g, import, context, g->builtin_types.entry_i32, | ||
| 1027 | node->data.bin_op_expr.op2); | ||
| 1028 | return_type = g->builtin_types.entry_i32; | ||
| 1029 | break; | ||
| 1030 | case BinOpTypeMult: | ||
| 1031 | case BinOpTypeDiv: | ||
| 1032 | case BinOpTypeMod: | ||
| 1033 | { | ||
| 1034 | // TODO: don't require i32 | ||
| 1035 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1); | ||
| 1036 | analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2); | ||
| 1037 | return_type = g->builtin_types.entry_i32; | ||
| 1038 | break; | ||
| 1039 | } | ||
| 1040 | case BinOpTypeInvalid: | ||
| 1041 | zig_unreachable(); | ||
| 1042 | } | ||
| 1043 | break; | ||
| 1044 | } | ||
| 1045 | 1240 | ||
| 1046 | case NodeTypeFnCallExpr: | 1241 | case NodeTypeFnCallExpr: |
| 1047 | { | 1242 | { |
| ... | @@ -1116,10 +1311,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, | ... | @@ -1116,10 +1311,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 1116 | return_type = analyze_field_access_expr(g, import, context, node); | 1311 | return_type = analyze_field_access_expr(g, import, context, node); |
| 1117 | break; | 1312 | break; |
| 1118 | case NodeTypeNumberLiteral: | 1313 | case NodeTypeNumberLiteral: |
| 1119 | // TODO: generic literal int type | 1314 | return_type = analyze_number_literal_expr(g, import, context, expected_type, node); |
| 1120 | return_type = g->builtin_types.entry_i32; | ||
| 1121 | break; | 1315 | break; |
| 1122 | |||
| 1123 | case NodeTypeStringLiteral: | 1316 | case NodeTypeStringLiteral: |
| 1124 | if (node->data.string_literal.c) { | 1317 | if (node->data.string_literal.c) { |
| 1125 | return_type = g->builtin_types.entry_c_string_literal; | 1318 | return_type = g->builtin_types.entry_c_string_literal; |
src/analyze.hpp+18-1| ... | @@ -42,6 +42,10 @@ struct TypeTableEntryStruct { | ... | @@ -42,6 +42,10 @@ struct TypeTableEntryStruct { |
| 42 | TypeStructField *fields; | 42 | TypeStructField *fields; |
| 43 | }; | 43 | }; |
| 44 | 44 | ||
| 45 | struct TypeTableEntryNumLit { | ||
| 46 | NumLit kind; | ||
| 47 | }; | ||
| 48 | |||
| 45 | enum TypeTableEntryId { | 49 | enum TypeTableEntryId { |
| 46 | TypeTableEntryIdInvalid, | 50 | TypeTableEntryIdInvalid, |
| 47 | TypeTableEntryIdVoid, | 51 | TypeTableEntryIdVoid, |
| ... | @@ -52,6 +56,7 @@ enum TypeTableEntryId { | ... | @@ -52,6 +56,7 @@ enum TypeTableEntryId { |
| 52 | TypeTableEntryIdPointer, | 56 | TypeTableEntryIdPointer, |
| 53 | TypeTableEntryIdArray, | 57 | TypeTableEntryIdArray, |
| 54 | TypeTableEntryIdStruct, | 58 | TypeTableEntryIdStruct, |
| 59 | TypeTableEntryIdNumberLiteral, | ||
| 55 | }; | 60 | }; |
| 56 | 61 | ||
| 57 | struct TypeTableEntry { | 62 | struct TypeTableEntry { |
| ... | @@ -69,12 +74,13 @@ struct TypeTableEntry { | ... | @@ -69,12 +74,13 @@ struct TypeTableEntry { |
| 69 | TypeTableEntryInt integral; | 74 | TypeTableEntryInt integral; |
| 70 | TypeTableEntryArray array; | 75 | TypeTableEntryArray array; |
| 71 | TypeTableEntryStruct structure; | 76 | TypeTableEntryStruct structure; |
| 77 | TypeTableEntryNumLit num_lit; | ||
| 72 | } data; | 78 | } data; |
| 73 | 79 | ||
| 74 | // use these fields to make sure we don't duplicate type table entries for the same type | 80 | // use these fields to make sure we don't duplicate type table entries for the same type |
| 75 | TypeTableEntry *pointer_const_parent; | 81 | TypeTableEntry *pointer_const_parent; |
| 76 | TypeTableEntry *pointer_mut_parent; | 82 | TypeTableEntry *pointer_mut_parent; |
| 77 | HashMap<int, TypeTableEntry *, int_hash, int_eq> arrays_by_size; | 83 | HashMap<uint64_t, TypeTableEntry *, uint64_hash, uint64_eq> arrays_by_size; |
| 78 | 84 | ||
| 79 | }; | 85 | }; |
| 80 | 86 | ||
| ... | @@ -134,10 +140,13 @@ struct CodeGen { | ... | @@ -134,10 +140,13 @@ struct CodeGen { |
| 134 | struct { | 140 | struct { |
| 135 | TypeTableEntry *entry_bool; | 141 | TypeTableEntry *entry_bool; |
| 136 | TypeTableEntry *entry_u8; | 142 | TypeTableEntry *entry_u8; |
| 143 | TypeTableEntry *entry_u64; | ||
| 137 | TypeTableEntry *entry_i32; | 144 | TypeTableEntry *entry_i32; |
| 145 | TypeTableEntry *entry_i64; | ||
| 138 | TypeTableEntry *entry_isize; | 146 | TypeTableEntry *entry_isize; |
| 139 | TypeTableEntry *entry_usize; | 147 | TypeTableEntry *entry_usize; |
| 140 | TypeTableEntry *entry_f32; | 148 | TypeTableEntry *entry_f32; |
| 149 | TypeTableEntry *entry_f64; | ||
| 141 | TypeTableEntry *entry_c_string_literal; | 150 | TypeTableEntry *entry_c_string_literal; |
| 142 | TypeTableEntry *entry_string; | 151 | TypeTableEntry *entry_string; |
| 143 | TypeTableEntry *entry_void; | 152 | TypeTableEntry *entry_void; |
| ... | @@ -145,6 +154,8 @@ struct CodeGen { | ... | @@ -145,6 +154,8 @@ struct CodeGen { |
| 145 | TypeTableEntry *entry_invalid; | 154 | TypeTableEntry *entry_invalid; |
| 146 | } builtin_types; | 155 | } builtin_types; |
| 147 | 156 | ||
| 157 | TypeTableEntry *num_lit_types[NumLitCount]; | ||
| 158 | |||
| 148 | LLVMTargetDataRef target_data_ref; | 159 | LLVMTargetDataRef target_data_ref; |
| 149 | unsigned pointer_size_bytes; | 160 | unsigned pointer_size_bytes; |
| 150 | bool is_static; | 161 | bool is_static; |
| ... | @@ -242,6 +253,7 @@ enum CastOp { | ... | @@ -242,6 +253,7 @@ enum CastOp { |
| 242 | CastOpPtrToInt, | 253 | CastOpPtrToInt, |
| 243 | CastOpIntWidenOrShorten, | 254 | CastOpIntWidenOrShorten, |
| 244 | CastOpArrayToString, | 255 | CastOpArrayToString, |
| 256 | CastOpNothing, | ||
| 245 | }; | 257 | }; |
| 246 | 258 | ||
| 247 | struct CastNode { | 259 | struct CastNode { |
| ... | @@ -251,6 +263,10 @@ struct CastNode { | ... | @@ -251,6 +263,10 @@ struct CastNode { |
| 251 | LLVMValueRef ptr; | 263 | LLVMValueRef ptr; |
| 252 | }; | 264 | }; |
| 253 | 265 | ||
| 266 | struct NumberLiteralNode { | ||
| 267 | TypeTableEntry *resolved_type; | ||
| 268 | }; | ||
| 269 | |||
| 254 | struct CodeGenNode { | 270 | struct CodeGenNode { |
| 255 | union { | 271 | union { |
| 256 | TypeNode type_node; // for NodeTypeType | 272 | TypeNode type_node; // for NodeTypeType |
| ... | @@ -262,6 +278,7 @@ struct CodeGenNode { | ... | @@ -262,6 +278,7 @@ struct CodeGenNode { |
| 262 | StructDeclNode struct_decl_node; // for NodeTypeStructDecl | 278 | StructDeclNode struct_decl_node; // for NodeTypeStructDecl |
| 263 | FieldAccessNode field_access_node; // for NodeTypeFieldAccessExpr | 279 | FieldAccessNode field_access_node; // for NodeTypeFieldAccessExpr |
| 264 | CastNode cast_node; // for NodeTypeCastExpr | 280 | CastNode cast_node; // for NodeTypeCastExpr |
| 281 | NumberLiteralNode num_lit_node; // for NodeTypeNumberLiteral | ||
| 265 | } data; | 282 | } data; |
| 266 | ExprNode expr_node; // for all the expression nodes | 283 | ExprNode expr_node; // for all the expression nodes |
| 267 | }; | 284 | }; |
src/codegen.cpp+90-5| ... | @@ -279,6 +279,8 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { | ... | @@ -279,6 +279,8 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { |
| 279 | CastNode *cast_node = &node->codegen_node->data.cast_node; | 279 | CastNode *cast_node = &node->codegen_node->data.cast_node; |
| 280 | 280 | ||
| 281 | switch (cast_node->op) { | 281 | switch (cast_node->op) { |
| 282 | case CastOpNothing: | ||
| 283 | return expr_val; | ||
| 282 | case CastOpPtrToInt: | 284 | case CastOpPtrToInt: |
| 283 | return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); | 285 | return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); |
| 284 | case CastOpIntWidenOrShorten: | 286 | case CastOpIntWidenOrShorten: |
| ... | @@ -901,11 +903,29 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { | ... | @@ -901,11 +903,29 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { |
| 901 | return gen_asm_expr(g, node); | 903 | return gen_asm_expr(g, node); |
| 902 | case NodeTypeNumberLiteral: | 904 | case NodeTypeNumberLiteral: |
| 903 | { | 905 | { |
| 904 | Buf *number_str = &node->data.number; | 906 | NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node; |
| 905 | LLVMTypeRef number_type = LLVMInt32Type(); | 907 | assert(codegen_num_lit); |
| 906 | LLVMValueRef number_val = LLVMConstIntOfStringAndSize(number_type, | 908 | TypeTableEntry *type_entry = codegen_num_lit->resolved_type; |
| 907 | buf_ptr(number_str), buf_len(number_str), 10); | 909 | assert(type_entry); |
| 908 | return number_val; | 910 | |
| 911 | // TODO this is kinda iffy. make sure josh is on board with this | ||
| 912 | node->codegen_node->expr_node.type_entry = type_entry; | ||
| 913 | |||
| 914 | if (type_entry->id == TypeTableEntryIdInt) { | ||
| 915 | // here the union has int64_t and uint64_t and we purposefully read | ||
| 916 | // the uint64_t value in either case, because we want the twos | ||
| 917 | // complement representation | ||
| 918 | |||
| 919 | return LLVMConstInt(type_entry->type_ref, | ||
| 920 | node->data.number_literal.data.x_uint, | ||
| 921 | type_entry->data.integral.is_signed); | ||
| 922 | } else if (type_entry->id == TypeTableEntryIdFloat) { | ||
| 923 | |||
| 924 | return LLVMConstReal(type_entry->type_ref, | ||
| 925 | node->data.number_literal.data.x_float); | ||
| 926 | } else { | ||
| 927 | zig_panic("bad number literal type"); | ||
| 928 | } | ||
| 909 | } | 929 | } |
| 910 | case NodeTypeStringLiteral: | 930 | case NodeTypeStringLiteral: |
| 911 | { | 931 | { |
| ... | @@ -1186,6 +1206,20 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -1186,6 +1206,20 @@ static void do_code_gen(CodeGen *g) { |
| 1186 | #endif | 1206 | #endif |
| 1187 | } | 1207 | } |
| 1188 | 1208 | ||
| 1209 | static const NumLit num_lit_kinds[] = { | ||
| 1210 | NumLitF32, | ||
| 1211 | NumLitF64, | ||
| 1212 | NumLitF128, | ||
| 1213 | NumLitI8, | ||
| 1214 | NumLitU8, | ||
| 1215 | NumLitI16, | ||
| 1216 | NumLitU16, | ||
| 1217 | NumLitI32, | ||
| 1218 | NumLitU32, | ||
| 1219 | NumLitI64, | ||
| 1220 | NumLitU64, | ||
| 1221 | }; | ||
| 1222 | |||
| 1189 | static void define_builtin_types(CodeGen *g) { | 1223 | static void define_builtin_types(CodeGen *g) { |
| 1190 | { | 1224 | { |
| 1191 | // if this type is anywhere in the AST, we should never hit codegen. | 1225 | // if this type is anywhere in the AST, we should never hit codegen. |
| ... | @@ -1193,6 +1227,19 @@ static void define_builtin_types(CodeGen *g) { | ... | @@ -1193,6 +1227,19 @@ static void define_builtin_types(CodeGen *g) { |
| 1193 | buf_init_from_str(&entry->name, "(invalid)"); | 1227 | buf_init_from_str(&entry->name, "(invalid)"); |
| 1194 | g->builtin_types.entry_invalid = entry; | 1228 | g->builtin_types.entry_invalid = entry; |
| 1195 | } | 1229 | } |
| 1230 | |||
| 1231 | assert(NumLitCount == array_length(num_lit_kinds)); | ||
| 1232 | for (int i = 0; i < NumLitCount; i += 1) { | ||
| 1233 | NumLit num_lit_kind = num_lit_kinds[i]; | ||
| 1234 | // This type should just create a constant with whatever actual number | ||
| 1235 | // type is expected at the time. | ||
| 1236 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumberLiteral); | ||
| 1237 | buf_resize(&entry->name, 0); | ||
| 1238 | buf_appendf(&entry->name, "(%s literal)", num_lit_str(num_lit_kind)); | ||
| 1239 | entry->data.num_lit.kind = num_lit_kind; | ||
| 1240 | g->num_lit_types[i] = entry; | ||
| 1241 | } | ||
| 1242 | |||
| 1196 | { | 1243 | { |
| 1197 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBool); | 1244 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBool); |
| 1198 | entry->type_ref = LLVMInt1Type(); | 1245 | entry->type_ref = LLVMInt1Type(); |
| ... | @@ -1217,6 +1264,19 @@ static void define_builtin_types(CodeGen *g) { | ... | @@ -1217,6 +1264,19 @@ static void define_builtin_types(CodeGen *g) { |
| 1217 | g->type_table.put(&entry->name, entry); | 1264 | g->type_table.put(&entry->name, entry); |
| 1218 | g->builtin_types.entry_u8 = entry; | 1265 | g->builtin_types.entry_u8 = entry; |
| 1219 | } | 1266 | } |
| 1267 | { | ||
| 1268 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); | ||
| 1269 | entry->type_ref = LLVMInt64Type(); | ||
| 1270 | buf_init_from_str(&entry->name, "u64"); | ||
| 1271 | entry->size_in_bits = 64; | ||
| 1272 | entry->align_in_bits = 64; | ||
| 1273 | entry->data.integral.is_signed = false; | ||
| 1274 | entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), | ||
| 1275 | entry->size_in_bits, entry->align_in_bits, | ||
| 1276 | LLVMZigEncoding_DW_ATE_unsigned()); | ||
| 1277 | g->type_table.put(&entry->name, entry); | ||
| 1278 | g->builtin_types.entry_u64 = entry; | ||
| 1279 | } | ||
| 1220 | g->builtin_types.entry_c_string_literal = get_pointer_to_type(g, g->builtin_types.entry_u8, true); | 1280 | g->builtin_types.entry_c_string_literal = get_pointer_to_type(g, g->builtin_types.entry_u8, true); |
| 1221 | { | 1281 | { |
| 1222 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); | 1282 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); |
| ... | @@ -1231,6 +1291,19 @@ static void define_builtin_types(CodeGen *g) { | ... | @@ -1231,6 +1291,19 @@ static void define_builtin_types(CodeGen *g) { |
| 1231 | g->type_table.put(&entry->name, entry); | 1291 | g->type_table.put(&entry->name, entry); |
| 1232 | g->builtin_types.entry_i32 = entry; | 1292 | g->builtin_types.entry_i32 = entry; |
| 1233 | } | 1293 | } |
| 1294 | { | ||
| 1295 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); | ||
| 1296 | entry->type_ref = LLVMInt64Type(); | ||
| 1297 | buf_init_from_str(&entry->name, "i64"); | ||
| 1298 | entry->size_in_bits = 64; | ||
| 1299 | entry->align_in_bits = 64; | ||
| 1300 | entry->data.integral.is_signed = true; | ||
| 1301 | entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), | ||
| 1302 | entry->size_in_bits, entry->align_in_bits, | ||
| 1303 | LLVMZigEncoding_DW_ATE_signed()); | ||
| 1304 | g->type_table.put(&entry->name, entry); | ||
| 1305 | g->builtin_types.entry_i64 = entry; | ||
| 1306 | } | ||
| 1234 | { | 1307 | { |
| 1235 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); | 1308 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); |
| 1236 | entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8); | 1309 | entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8); |
| ... | @@ -1269,6 +1342,18 @@ static void define_builtin_types(CodeGen *g) { | ... | @@ -1269,6 +1342,18 @@ static void define_builtin_types(CodeGen *g) { |
| 1269 | g->type_table.put(&entry->name, entry); | 1342 | g->type_table.put(&entry->name, entry); |
| 1270 | g->builtin_types.entry_f32 = entry; | 1343 | g->builtin_types.entry_f32 = entry; |
| 1271 | } | 1344 | } |
| 1345 | { | ||
| 1346 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat); | ||
| 1347 | entry->type_ref = LLVMFloatType(); | ||
| 1348 | buf_init_from_str(&entry->name, "f64"); | ||
| 1349 | entry->size_in_bits = 64; | ||
| 1350 | entry->align_in_bits = 64; | ||
| 1351 | entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), | ||
| 1352 | entry->size_in_bits, entry->align_in_bits, | ||
| 1353 | LLVMZigEncoding_DW_ATE_float()); | ||
| 1354 | g->type_table.put(&entry->name, entry); | ||
| 1355 | g->builtin_types.entry_f64 = entry; | ||
| 1356 | } | ||
| 1272 | { | 1357 | { |
| 1273 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVoid); | 1358 | TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVoid); |
| 1274 | entry->type_ref = LLVMVoidType(); | 1359 | entry->type_ref = LLVMVoidType(); |
src/parseh.cpp+1-1| ... | @@ -544,7 +544,7 @@ void parse_h_file(const char *target_path, ZigList<const char *> *clang_argv, FI | ... | @@ -544,7 +544,7 @@ void parse_h_file(const char *target_path, ZigList<const char *> *clang_argv, FI |
| 544 | 544 | ||
| 545 | char *ZIG_PARSEH_CFLAGS = getenv("ZIG_PARSEH_CFLAGS"); | 545 | char *ZIG_PARSEH_CFLAGS = getenv("ZIG_PARSEH_CFLAGS"); |
| 546 | if (ZIG_PARSEH_CFLAGS) { | 546 | if (ZIG_PARSEH_CFLAGS) { |
| 547 | Buf tmp_buf = {0}; | 547 | Buf tmp_buf = BUF_INIT; |
| 548 | char *start = ZIG_PARSEH_CFLAGS; | 548 | char *start = ZIG_PARSEH_CFLAGS; |
| 549 | char *space = strstr(start, " "); | 549 | char *space = strstr(start, " "); |
| 550 | while (space) { | 550 | while (space) { |
src/parser.cpp+313-4| ... | @@ -12,6 +12,7 @@ | ... | @@ -12,6 +12,7 @@ |
| 12 | #include <stdarg.h> | 12 | #include <stdarg.h> |
| 13 | #include <stdio.h> | 13 | #include <stdio.h> |
| 14 | 14 | ||
| 15 | |||
| 15 | static const char *bin_op_str(BinOpType bin_op) { | 16 | static const char *bin_op_str(BinOpType bin_op) { |
| 16 | switch (bin_op) { | 17 | switch (bin_op) { |
| 17 | case BinOpTypeInvalid: return "(invalid)"; | 18 | case BinOpTypeInvalid: return "(invalid)"; |
| ... | @@ -273,9 +274,19 @@ void ast_print(AstNode *node, int indent) { | ... | @@ -273,9 +274,19 @@ void ast_print(AstNode *node, int indent) { |
| 273 | ast_print(node->data.prefix_op_expr.primary_expr, indent + 2); | 274 | ast_print(node->data.prefix_op_expr.primary_expr, indent + 2); |
| 274 | break; | 275 | break; |
| 275 | case NodeTypeNumberLiteral: | 276 | case NodeTypeNumberLiteral: |
| 276 | fprintf(stderr, "NumberLiteral %s\n", | 277 | { |
| 277 | buf_ptr(&node->data.number)); | 278 | NumLit num_lit = node->data.number_literal.kind; |
| 278 | break; | 279 | const char *name = node_type_str(node->type); |
| 280 | const char *kind_str = num_lit_str(num_lit); | ||
| 281 | if (is_num_lit_signed(num_lit)) { | ||
| 282 | fprintf(stderr, "%s %s %" PRId64 "\n", name, kind_str, node->data.number_literal.data.x_int); | ||
| 283 | } else if (is_num_lit_unsigned(num_lit)) { | ||
| 284 | fprintf(stderr, "%s %s %" PRIu64 "\n", name, kind_str, node->data.number_literal.data.x_uint); | ||
| 285 | } else { | ||
| 286 | fprintf(stderr, "%s %s %f\n", name, kind_str, node->data.number_literal.data.x_float); | ||
| 287 | } | ||
| 288 | break; | ||
| 289 | } | ||
| 279 | case NodeTypeStringLiteral: | 290 | case NodeTypeStringLiteral: |
| 280 | { | 291 | { |
| 281 | const char *c = node->data.string_literal.c ? "c" : ""; | 292 | const char *c = node->data.string_literal.c ? "c" : ""; |
| ... | @@ -574,6 +585,186 @@ static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf, bool | ... | @@ -574,6 +585,186 @@ static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf, bool |
| 574 | if (offset_map) offset_map->append(pos); | 585 | if (offset_map) offset_map->append(pos); |
| 575 | } | 586 | } |
| 576 | 587 | ||
| 588 | enum ParseNumLitState { | ||
| 589 | ParseNumLitStateStart, | ||
| 590 | ParseNumLitStateBase, | ||
| 591 | ParseNumLitStateDigits, | ||
| 592 | ParseNumLitStateExpectFirstDigit, | ||
| 593 | ParseNumLitStateDecimal, | ||
| 594 | ParseNumLitStateESign, | ||
| 595 | ParseNumLitStateEDigit, | ||
| 596 | }; | ||
| 597 | |||
| 598 | static void parse_number_literal(ParseContext *pc, Token *token, AstNodeNumberLiteral *num_lit) { | ||
| 599 | ParseNumLitState state = ParseNumLitStateStart; | ||
| 600 | unsigned long long base = 10; | ||
| 601 | bool negative = false; | ||
| 602 | int digits_start; | ||
| 603 | int digits_end; | ||
| 604 | int decimal_start = -1; | ||
| 605 | int decimal_end; | ||
| 606 | bool e_present = false; | ||
| 607 | bool e_positive; | ||
| 608 | int e_digit_start; | ||
| 609 | int e_digit_end; | ||
| 610 | |||
| 611 | for (int i = token->start_pos; i < token->end_pos; i += 1) { | ||
| 612 | uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + i); | ||
| 613 | switch (state) { | ||
| 614 | case ParseNumLitStateStart: | ||
| 615 | if (c == '-') { | ||
| 616 | negative = true; | ||
| 617 | } else if (c == '0') { | ||
| 618 | state = ParseNumLitStateBase; | ||
| 619 | } else if (c >= '1' && c <= '9') { | ||
| 620 | digits_start = i; | ||
| 621 | state = ParseNumLitStateDigits; | ||
| 622 | } else { | ||
| 623 | zig_unreachable(); | ||
| 624 | } | ||
| 625 | break; | ||
| 626 | case ParseNumLitStateBase: | ||
| 627 | if (c == 'x') { | ||
| 628 | base = 16; | ||
| 629 | state = ParseNumLitStateExpectFirstDigit; | ||
| 630 | } else if (c == 'o') { | ||
| 631 | base = 8; | ||
| 632 | state = ParseNumLitStateExpectFirstDigit; | ||
| 633 | } else if (c == 'b') { | ||
| 634 | base = 2; | ||
| 635 | state = ParseNumLitStateExpectFirstDigit; | ||
| 636 | } else { | ||
| 637 | zig_unreachable(); | ||
| 638 | } | ||
| 639 | break; | ||
| 640 | |||
| 641 | case ParseNumLitStateExpectFirstDigit: | ||
| 642 | state = ParseNumLitStateDigits; | ||
| 643 | break; | ||
| 644 | |||
| 645 | case ParseNumLitStateDigits: | ||
| 646 | if (c == '.') { | ||
| 647 | assert(base == 10); | ||
| 648 | digits_end = i; | ||
| 649 | decimal_start = i + 1; | ||
| 650 | state = ParseNumLitStateDecimal; | ||
| 651 | } | ||
| 652 | break; | ||
| 653 | case ParseNumLitStateDecimal: | ||
| 654 | if (c == 'E') { | ||
| 655 | e_present = false; | ||
| 656 | decimal_end = i; | ||
| 657 | state = ParseNumLitStateESign; | ||
| 658 | } | ||
| 659 | break; | ||
| 660 | case ParseNumLitStateESign: | ||
| 661 | if (c == '+') { | ||
| 662 | e_positive = true; | ||
| 663 | e_digit_start = i + 1; | ||
| 664 | state = ParseNumLitStateEDigit; | ||
| 665 | } else if (c == '-') { | ||
| 666 | e_positive = false; | ||
| 667 | e_digit_start = i + 1; | ||
| 668 | state = ParseNumLitStateEDigit; | ||
| 669 | } else { | ||
| 670 | zig_unreachable(); | ||
| 671 | } | ||
| 672 | break; | ||
| 673 | case ParseNumLitStateEDigit: | ||
| 674 | assert(c >= '0' && c <= '9'); | ||
| 675 | break; | ||
| 676 | } | ||
| 677 | } | ||
| 678 | |||
| 679 | switch (state) { | ||
| 680 | case ParseNumLitStateDigits: | ||
| 681 | digits_end = token->end_pos; | ||
| 682 | break; | ||
| 683 | case ParseNumLitStateDecimal: | ||
| 684 | decimal_end = token->end_pos; | ||
| 685 | break; | ||
| 686 | case ParseNumLitStateEDigit: | ||
| 687 | e_digit_end = token->end_pos; | ||
| 688 | break; | ||
| 689 | case ParseNumLitStateBase: | ||
| 690 | num_lit->kind = NumLitU8; | ||
| 691 | num_lit->data.x_uint = 0; | ||
| 692 | return; | ||
| 693 | case ParseNumLitStateESign: | ||
| 694 | case ParseNumLitStateExpectFirstDigit: | ||
| 695 | case ParseNumLitStateStart: | ||
| 696 | zig_unreachable(); | ||
| 697 | } | ||
| 698 | |||
| 699 | if (decimal_start >= 0) { | ||
| 700 | // float | ||
| 701 | double x; | ||
| 702 | |||
| 703 | (void)x; | ||
| 704 | zig_panic("TODO parse float"); | ||
| 705 | } else { | ||
| 706 | // integer | ||
| 707 | unsigned long long x = 0; | ||
| 708 | |||
| 709 | unsigned long long mult = 1; | ||
| 710 | for (int i = digits_end - 1; ; i -= 1) { | ||
| 711 | uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + i); | ||
| 712 | unsigned long long digit = (c - '0'); | ||
| 713 | |||
| 714 | // digit *= mult | ||
| 715 | if (__builtin_umulll_overflow(digit, mult, &digit)) { | ||
| 716 | num_lit->overflow = true; | ||
| 717 | return; | ||
| 718 | } | ||
| 719 | |||
| 720 | // x += digit | ||
| 721 | if (__builtin_uaddll_overflow(x, digit, &x)) { | ||
| 722 | num_lit->overflow = true; | ||
| 723 | return; | ||
| 724 | } | ||
| 725 | |||
| 726 | if (i == digits_start) | ||
| 727 | break; | ||
| 728 | |||
| 729 | // mult *= base | ||
| 730 | if (__builtin_umulll_overflow(mult, base, &mult)) { | ||
| 731 | num_lit->overflow = true; | ||
| 732 | return; | ||
| 733 | } | ||
| 734 | } | ||
| 735 | |||
| 736 | if (negative) { | ||
| 737 | if (x <= 128ull) { | ||
| 738 | num_lit->kind = NumLitI8; | ||
| 739 | } else if (x <= 32768ull) { | ||
| 740 | num_lit->kind = NumLitI16; | ||
| 741 | } else if (x <= 2147483648ull) { | ||
| 742 | num_lit->kind = NumLitI32; | ||
| 743 | } else if (x <= 9223372036854775808ull) { | ||
| 744 | num_lit->kind = NumLitI64; | ||
| 745 | } else { | ||
| 746 | num_lit->overflow = true; | ||
| 747 | return; | ||
| 748 | } | ||
| 749 | |||
| 750 | num_lit->data.x_int = -((int64_t)x); | ||
| 751 | } else { | ||
| 752 | num_lit->data.x_uint = x; | ||
| 753 | |||
| 754 | if (x <= UINT8_MAX) { | ||
| 755 | num_lit->kind = NumLitU8; | ||
| 756 | } else if (x <= UINT16_MAX) { | ||
| 757 | num_lit->kind = NumLitU16; | ||
| 758 | } else if (x <= UINT32_MAX) { | ||
| 759 | num_lit->kind = NumLitU32; | ||
| 760 | } else { | ||
| 761 | num_lit->kind = NumLitU64; | ||
| 762 | } | ||
| 763 | } | ||
| 764 | } | ||
| 765 | } | ||
| 766 | |||
| 767 | |||
| 577 | __attribute__ ((noreturn)) | 768 | __attribute__ ((noreturn)) |
| 578 | static void ast_invalid_token_error(ParseContext *pc, Token *token) { | 769 | static void ast_invalid_token_error(ParseContext *pc, Token *token) { |
| 579 | Buf token_value = BUF_INIT; | 770 | Buf token_value = BUF_INIT; |
| ... | @@ -829,7 +1020,7 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool | ... | @@ -829,7 +1020,7 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool |
| 829 | 1020 | ||
| 830 | if (token->id == TokenIdNumberLiteral) { | 1021 | if (token->id == TokenIdNumberLiteral) { |
| 831 | AstNode *node = ast_create_node(pc, NodeTypeNumberLiteral, token); | 1022 | AstNode *node = ast_create_node(pc, NodeTypeNumberLiteral, token); |
| 832 | ast_buf_from_token(pc, token, &node->data.number); | 1023 | parse_number_literal(pc, token, &node->data.number_literal); |
| 833 | *token_index += 1; | 1024 | *token_index += 1; |
| 834 | return node; | 1025 | return node; |
| 835 | } else if (token->id == TokenIdStringLiteral) { | 1026 | } else if (token->id == TokenIdStringLiteral) { |
| ... | @@ -2152,3 +2343,121 @@ AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ImportTableEntry *owner, Er | ... | @@ -2152,3 +2343,121 @@ AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ImportTableEntry *owner, Er |
| 2152 | pc.root = ast_parse_root(&pc, &token_index); | 2343 | pc.root = ast_parse_root(&pc, &token_index); |
| 2153 | return pc.root; | 2344 | return pc.root; |
| 2154 | } | 2345 | } |
| 2346 | |||
| 2347 | const char *num_lit_str(NumLit num_lit) { | ||
| 2348 | switch (num_lit) { | ||
| 2349 | case NumLitF32: | ||
| 2350 | return "f32"; | ||
| 2351 | case NumLitF64: | ||
| 2352 | return "f64"; | ||
| 2353 | case NumLitF128: | ||
| 2354 | return "f128"; | ||
| 2355 | case NumLitI8: | ||
| 2356 | return "i8"; | ||
| 2357 | case NumLitI16: | ||
| 2358 | return "i16"; | ||
| 2359 | case NumLitI32: | ||
| 2360 | return "i32"; | ||
| 2361 | case NumLitI64: | ||
| 2362 | return "i64"; | ||
| 2363 | case NumLitU8: | ||
| 2364 | return "u8"; | ||
| 2365 | case NumLitU16: | ||
| 2366 | return "u16"; | ||
| 2367 | case NumLitU32: | ||
| 2368 | return "u32"; | ||
| 2369 | case NumLitU64: | ||
| 2370 | return "u64"; | ||
| 2371 | case NumLitCount: | ||
| 2372 | zig_unreachable(); | ||
| 2373 | } | ||
| 2374 | zig_unreachable(); | ||
| 2375 | } | ||
| 2376 | |||
| 2377 | bool is_num_lit_signed(NumLit num_lit) { | ||
| 2378 | switch (num_lit) { | ||
| 2379 | case NumLitI8: | ||
| 2380 | case NumLitI16: | ||
| 2381 | case NumLitI32: | ||
| 2382 | case NumLitI64: | ||
| 2383 | return true; | ||
| 2384 | |||
| 2385 | case NumLitF32: | ||
| 2386 | case NumLitF64: | ||
| 2387 | case NumLitF128: | ||
| 2388 | case NumLitU8: | ||
| 2389 | case NumLitU16: | ||
| 2390 | case NumLitU32: | ||
| 2391 | case NumLitU64: | ||
| 2392 | return false; | ||
| 2393 | case NumLitCount: | ||
| 2394 | zig_unreachable(); | ||
| 2395 | } | ||
| 2396 | zig_unreachable(); | ||
| 2397 | } | ||
| 2398 | |||
| 2399 | bool is_num_lit_unsigned(NumLit num_lit) { | ||
| 2400 | switch (num_lit) { | ||
| 2401 | case NumLitF32: | ||
| 2402 | case NumLitF64: | ||
| 2403 | case NumLitF128: | ||
| 2404 | case NumLitI8: | ||
| 2405 | case NumLitI16: | ||
| 2406 | case NumLitI32: | ||
| 2407 | case NumLitI64: | ||
| 2408 | return false; | ||
| 2409 | case NumLitU8: | ||
| 2410 | case NumLitU16: | ||
| 2411 | case NumLitU32: | ||
| 2412 | case NumLitU64: | ||
| 2413 | return true; | ||
| 2414 | case NumLitCount: | ||
| 2415 | zig_unreachable(); | ||
| 2416 | } | ||
| 2417 | zig_unreachable(); | ||
| 2418 | } | ||
| 2419 | |||
| 2420 | bool is_num_lit_float(NumLit num_lit) { | ||
| 2421 | switch (num_lit) { | ||
| 2422 | case NumLitF32: | ||
| 2423 | case NumLitF64: | ||
| 2424 | case NumLitF128: | ||
| 2425 | return true; | ||
| 2426 | case NumLitI8: | ||
| 2427 | case NumLitI16: | ||
| 2428 | case NumLitI32: | ||
| 2429 | case NumLitI64: | ||
| 2430 | case NumLitU8: | ||
| 2431 | case NumLitU16: | ||
| 2432 | case NumLitU32: | ||
| 2433 | case NumLitU64: | ||
| 2434 | return false; | ||
| 2435 | case NumLitCount: | ||
| 2436 | zig_unreachable(); | ||
| 2437 | } | ||
| 2438 | zig_unreachable(); | ||
| 2439 | } | ||
| 2440 | |||
| 2441 | uint64_t num_lit_bit_count(NumLit num_lit) { | ||
| 2442 | switch (num_lit) { | ||
| 2443 | case NumLitI8: | ||
| 2444 | case NumLitU8: | ||
| 2445 | return 8; | ||
| 2446 | case NumLitI16: | ||
| 2447 | case NumLitU16: | ||
| 2448 | return 16; | ||
| 2449 | case NumLitI32: | ||
| 2450 | case NumLitU32: | ||
| 2451 | case NumLitF32: | ||
| 2452 | return 32; | ||
| 2453 | case NumLitI64: | ||
| 2454 | case NumLitU64: | ||
| 2455 | case NumLitF64: | ||
| 2456 | return 64; | ||
| 2457 | case NumLitF128: | ||
| 2458 | return 128; | ||
| 2459 | case NumLitCount: | ||
| 2460 | zig_unreachable(); | ||
| 2461 | } | ||
| 2462 | zig_unreachable(); | ||
| 2463 | } |
src/parser.hpp+38-1| ... | @@ -267,6 +267,36 @@ struct AstNodeStringLiteral { | ... | @@ -267,6 +267,36 @@ struct AstNodeStringLiteral { |
| 267 | bool c; | 267 | bool c; |
| 268 | }; | 268 | }; |
| 269 | 269 | ||
| 270 | enum NumLit { | ||
| 271 | NumLitF32, | ||
| 272 | NumLitF64, | ||
| 273 | NumLitF128, | ||
| 274 | NumLitI8, | ||
| 275 | NumLitU8, | ||
| 276 | NumLitI16, | ||
| 277 | NumLitU16, | ||
| 278 | NumLitI32, | ||
| 279 | NumLitU32, | ||
| 280 | NumLitI64, | ||
| 281 | NumLitU64, | ||
| 282 | |||
| 283 | NumLitCount | ||
| 284 | }; | ||
| 285 | |||
| 286 | struct AstNodeNumberLiteral { | ||
| 287 | NumLit kind; | ||
| 288 | |||
| 289 | // overflow is true if when parsing the number, we discovered it would not | ||
| 290 | // fit without losing data in a uint64_t, int64_t, or double | ||
| 291 | bool overflow; | ||
| 292 | |||
| 293 | union { | ||
| 294 | uint64_t x_uint; | ||
| 295 | int64_t x_int; | ||
| 296 | double x_float; | ||
| 297 | } data; | ||
| 298 | }; | ||
| 299 | |||
| 270 | struct AstNode { | 300 | struct AstNode { |
| 271 | enum NodeType type; | 301 | enum NodeType type; |
| 272 | int line; | 302 | int line; |
| ... | @@ -300,7 +330,7 @@ struct AstNode { | ... | @@ -300,7 +330,7 @@ struct AstNode { |
| 300 | AstNodeStructDecl struct_decl; | 330 | AstNodeStructDecl struct_decl; |
| 301 | AstNodeStructField struct_field; | 331 | AstNodeStructField struct_field; |
| 302 | AstNodeStringLiteral string_literal; | 332 | AstNodeStringLiteral string_literal; |
| 303 | Buf number; | 333 | AstNodeNumberLiteral number_literal; |
| 304 | Buf symbol; | 334 | Buf symbol; |
| 305 | bool bool_literal; | 335 | bool bool_literal; |
| 306 | } data; | 336 | } data; |
| ... | @@ -329,4 +359,11 @@ const char *node_type_str(NodeType node_type); | ... | @@ -329,4 +359,11 @@ const char *node_type_str(NodeType node_type); |
| 329 | 359 | ||
| 330 | void ast_print(AstNode *node, int indent); | 360 | void ast_print(AstNode *node, int indent); |
| 331 | 361 | ||
| 362 | const char *num_lit_str(NumLit num_lit); | ||
| 363 | bool is_num_lit_signed(NumLit num_lit); | ||
| 364 | bool is_num_lit_unsigned(NumLit num_lit); | ||
| 365 | bool is_num_lit_float(NumLit num_lit); | ||
| 366 | uint64_t num_lit_bit_count(NumLit num_lit); | ||
| 367 | |||
| 368 | |||
| 332 | #endif | 369 | #endif |
src/util.cpp+9-1| ... | @@ -21,8 +21,16 @@ void zig_panic(const char *format, ...) { | ... | @@ -21,8 +21,16 @@ void zig_panic(const char *format, ...) { |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | uint32_t int_hash(int i) { | 23 | uint32_t int_hash(int i) { |
| 24 | return *reinterpret_cast<uint32_t*>(&i); | 24 | return (uint32_t)(i % UINT32_MAX); |
| 25 | } | 25 | } |
| 26 | bool int_eq(int a, int b) { | 26 | bool int_eq(int a, int b) { |
| 27 | return a == b; | 27 | return a == b; |
| 28 | } | 28 | } |
| 29 | |||
| 30 | uint32_t uint64_hash(uint64_t i) { | ||
| 31 | return (uint32_t)(i % UINT32_MAX); | ||
| 32 | } | ||
| 33 | |||
| 34 | bool uint64_eq(uint64_t a, uint64_t b) { | ||
| 35 | return a == b; | ||
| 36 | } |
src/util.hpp+2| ... | @@ -81,5 +81,7 @@ static inline bool mem_eql_str(const char *mem, size_t mem_len, const char *str) | ... | @@ -81,5 +81,7 @@ static inline bool mem_eql_str(const char *mem, size_t mem_len, const char *str) |
| 81 | 81 | ||
| 82 | uint32_t int_hash(int i); | 82 | uint32_t int_hash(int i); |
| 83 | bool int_eq(int a, int b); | 83 | bool int_eq(int a, int b); |
| 84 | uint32_t uint64_hash(uint64_t i); | ||
| 85 | bool uint64_eq(uint64_t a, uint64_t b); | ||
| 84 | 86 | ||
| 85 | #endif | 87 | #endif |
std/std.zig+3-4| ... | @@ -15,10 +15,9 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize { | ... | @@ -15,10 +15,9 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize { |
| 15 | 15 | ||
| 16 | // TODO error handling | 16 | // TODO error handling |
| 17 | // TODO handle buffering and flushing | 17 | // TODO handle buffering and flushing |
| 18 | // TODO non-i32 integer literals so we can remove the casts | ||
| 19 | // TODO constants for SYS_write and stdout_fileno | 18 | // TODO constants for SYS_write and stdout_fileno |
| 20 | pub fn print_str(str : string) -> isize { | 19 | pub fn print_str(str : string) -> isize { |
| 21 | let SYS_write = 1; | 20 | let SYS_write : isize = 1; |
| 22 | let stdout_fileno = 1; | 21 | let stdout_fileno : isize = 1; |
| 23 | return syscall3(SYS_write as isize, stdout_fileno as isize, str.ptr as isize, str.len as isize); | 22 | return syscall3(SYS_write, stdout_fileno, str.ptr as isize, str.len as isize); |
| 24 | } | 23 | } |
test/run_tests.cpp+21-12| ... | @@ -266,7 +266,7 @@ extern { | ... | @@ -266,7 +266,7 @@ extern { |
| 266 | 266 | ||
| 267 | export fn _start() -> unreachable { | 267 | export fn _start() -> unreachable { |
| 268 | let a : i32 = 1; | 268 | let a : i32 = 1; |
| 269 | let b = 2; | 269 | let b = 2 as i32; |
| 270 | if (a + b == 3) { | 270 | if (a + b == 3) { |
| 271 | puts(c"OK"); | 271 | puts(c"OK"); |
| 272 | } | 272 | } |
| ... | @@ -299,12 +299,12 @@ extern { | ... | @@ -299,12 +299,12 @@ extern { |
| 299 | 299 | ||
| 300 | export fn _start() -> unreachable { | 300 | export fn _start() -> unreachable { |
| 301 | if (true) { | 301 | if (true) { |
| 302 | let no_conflict = 5; | 302 | let no_conflict : i32 = 5; |
| 303 | if (no_conflict == 5) { puts(c"OK 1"); } | 303 | if (no_conflict == 5) { puts(c"OK 1"); } |
| 304 | } | 304 | } |
| 305 | 305 | ||
| 306 | let c = { | 306 | let c = { |
| 307 | let no_conflict = 10; | 307 | let no_conflict = 10 as i32; |
| 308 | no_conflict | 308 | no_conflict |
| 309 | }; | 309 | }; |
| 310 | if (c == 10) { puts(c"OK 2"); } | 310 | if (c == 10) { puts(c"OK 2"); } |
| ... | @@ -343,7 +343,7 @@ export fn _start() -> unreachable { | ... | @@ -343,7 +343,7 @@ export fn _start() -> unreachable { |
| 343 | let mut zero : i32; | 343 | let mut zero : i32; |
| 344 | if (zero == 0) { puts(c"zero"); } | 344 | if (zero == 0) { puts(c"zero"); } |
| 345 | 345 | ||
| 346 | let mut i = 0; | 346 | let mut i = 0 as i32; |
| 347 | loop_start: | 347 | loop_start: |
| 348 | if i == 3 { | 348 | if i == 3 { |
| 349 | goto done; | 349 | goto done; |
| ... | @@ -366,7 +366,7 @@ extern { | ... | @@ -366,7 +366,7 @@ extern { |
| 366 | export fn _start() -> unreachable { | 366 | export fn _start() -> unreachable { |
| 367 | let mut array : [i32; 5]; | 367 | let mut array : [i32; 5]; |
| 368 | 368 | ||
| 369 | let mut i = 0; | 369 | let mut i : i32 = 0; |
| 370 | loop_start: | 370 | loop_start: |
| 371 | if i == 5 { | 371 | if i == 5 { |
| 372 | goto loop_end; | 372 | goto loop_end; |
| ... | @@ -378,7 +378,7 @@ loop_start: | ... | @@ -378,7 +378,7 @@ loop_start: |
| 378 | loop_end: | 378 | loop_end: |
| 379 | 379 | ||
| 380 | i = 0; | 380 | i = 0; |
| 381 | let mut accumulator = 0; | 381 | let mut accumulator = 0 as i32; |
| 382 | loop_2_start: | 382 | loop_2_start: |
| 383 | if i == 5 { | 383 | if i == 5 { |
| 384 | goto loop_2_end; | 384 | goto loop_2_end; |
| ... | @@ -611,7 +611,7 @@ fn f() -> i32 { | ... | @@ -611,7 +611,7 @@ fn f() -> i32 { |
| 611 | fn f() { | 611 | fn f() { |
| 612 | if (0) {} | 612 | if (0) {} |
| 613 | } | 613 | } |
| 614 | )SOURCE", 1, ".tmp_source.zig:3:9: error: expected type 'bool', got 'i32'"); | 614 | )SOURCE", 1, ".tmp_source.zig:3:9: error: expected type 'bool', got '(u8 literal)'"); |
| 615 | 615 | ||
| 616 | add_compile_fail_case("assign unreachable", R"SOURCE( | 616 | add_compile_fail_case("assign unreachable", R"SOURCE( |
| 617 | fn f() { | 617 | fn f() { |
| ... | @@ -685,13 +685,12 @@ fn f(...) {} | ... | @@ -685,13 +685,12 @@ fn f(...) {} |
| 685 | 685 | ||
| 686 | } | 686 | } |
| 687 | 687 | ||
| 688 | static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) { | 688 | static void print_compiler_invocation(TestCase *test_case) { |
| 689 | printf("%s", zig_exe); | 689 | printf("%s", zig_exe); |
| 690 | for (int i = 0; i < test_case->compiler_args.length; i += 1) { | 690 | for (int i = 0; i < test_case->compiler_args.length; i += 1) { |
| 691 | printf(" %s", test_case->compiler_args.at(i)); | 691 | printf(" %s", test_case->compiler_args.at(i)); |
| 692 | } | 692 | } |
| 693 | printf("\n"); | 693 | printf("\n"); |
| 694 | printf("%s\n", buf_ptr(zig_stderr)); | ||
| 695 | } | 694 | } |
| 696 | 695 | ||
| 697 | static void run_test(TestCase *test_case) { | 696 | static void run_test(TestCase *test_case) { |
| ... | @@ -716,21 +715,24 @@ static void run_test(TestCase *test_case) { | ... | @@ -716,21 +715,24 @@ static void run_test(TestCase *test_case) { |
| 716 | printf("========= Expected this compile error: =========\n"); | 715 | printf("========= Expected this compile error: =========\n"); |
| 717 | printf("%s\n", err_text); | 716 | printf("%s\n", err_text); |
| 718 | printf("================================================\n"); | 717 | printf("================================================\n"); |
| 719 | print_compiler_invocation(test_case, &zig_stderr); | 718 | print_compiler_invocation(test_case); |
| 719 | printf("%s\n", buf_ptr(&zig_stderr)); | ||
| 720 | exit(1); | 720 | exit(1); |
| 721 | } | 721 | } |
| 722 | } | 722 | } |
| 723 | return; // success | 723 | return; // success |
| 724 | } else { | 724 | } else { |
| 725 | printf("\nCompile failed with return code 0 (Expected failure):\n"); | 725 | printf("\nCompile failed with return code 0 (Expected failure):\n"); |
| 726 | print_compiler_invocation(test_case, &zig_stderr); | 726 | print_compiler_invocation(test_case); |
| 727 | printf("%s\n", buf_ptr(&zig_stderr)); | ||
| 727 | exit(1); | 728 | exit(1); |
| 728 | } | 729 | } |
| 729 | } | 730 | } |
| 730 | 731 | ||
| 731 | if (return_code != 0) { | 732 | if (return_code != 0) { |
| 732 | printf("\nCompile failed with return code %d:\n", return_code); | 733 | printf("\nCompile failed with return code %d:\n", return_code); |
| 733 | print_compiler_invocation(test_case, &zig_stderr); | 734 | print_compiler_invocation(test_case); |
| 735 | printf("%s\n", buf_ptr(&zig_stderr)); | ||
| 734 | exit(1); | 736 | exit(1); |
| 735 | } | 737 | } |
| 736 | 738 | ||
| ... | @@ -740,6 +742,7 @@ static void run_test(TestCase *test_case) { | ... | @@ -740,6 +742,7 @@ static void run_test(TestCase *test_case) { |
| 740 | 742 | ||
| 741 | if (return_code != 0) { | 743 | if (return_code != 0) { |
| 742 | printf("\nProgram exited with return code %d:\n", return_code); | 744 | printf("\nProgram exited with return code %d:\n", return_code); |
| 745 | print_compiler_invocation(test_case); | ||
| 743 | printf("%s", tmp_exe_path); | 746 | printf("%s", tmp_exe_path); |
| 744 | for (int i = 0; i < test_case->program_args.length; i += 1) { | 747 | for (int i = 0; i < test_case->program_args.length; i += 1) { |
| 745 | printf(" %s", test_case->program_args.at(i)); | 748 | printf(" %s", test_case->program_args.at(i)); |
| ... | @@ -750,6 +753,12 @@ static void run_test(TestCase *test_case) { | ... | @@ -750,6 +753,12 @@ static void run_test(TestCase *test_case) { |
| 750 | } | 753 | } |
| 751 | 754 | ||
| 752 | if (!buf_eql_str(&program_stdout, test_case->output)) { | 755 | if (!buf_eql_str(&program_stdout, test_case->output)) { |
| 756 | printf("\n"); | ||
| 757 | print_compiler_invocation(test_case); | ||
| 758 | printf("%s", tmp_exe_path); | ||
| 759 | for (int i = 0; i < test_case->program_args.length; i += 1) { | ||
| 760 | printf(" %s", test_case->program_args.at(i)); | ||
| 761 | } | ||
| 753 | printf("\n"); | 762 | printf("\n"); |
| 754 | printf("==== Test failed. Expected output: ====\n"); | 763 | printf("==== Test failed. Expected output: ====\n"); |
| 755 | printf("%s\n", test_case->output); | 764 | printf("%s\n", test_case->output); |