| author | |
| committer | |
| log | 32d8686da80d282e8cd6d84a0e5c331d269a1f69 |
| tree | 889c1f634f4ff2e7b9d8cb078f11dbb41c4a1b2a |
| parent | 17cb85dfb837949cd3a559fe8e99dee1f72463a4 |
* comptime expression is a block expression as it should be
* fix var args when number of args passed is 0
* implement const value equality for structs
* fix indent when rendering container decl AST
* IR: prevent duplicate generation of code when it is partially
compile-time evaluated
* implement compile time struct field pointer evaluation
* fix compile time evaluation of slicing10 files changed, 84 insertions(+), 42 deletions(-)
doc/langref.md+4-4| ... | ... | @@ -69,7 +69,9 @@ AssignmentExpression = UnwrapExpression AssignmentOperator UnwrapExpression | Un |
| 69 | 69 | |
| 70 | 70 | AssignmentOperator = "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" | "&&=" | "||=" | "*%=" | "+%=" | "-%=" | "<<%=" |
| 71 | 71 | |
| 72 | BlockExpression = IfExpression | Block | WhileExpression | ForExpression | SwitchExpression | |
| 72 | BlockExpression = IfExpression | Block | WhileExpression | ForExpression | SwitchExpression | CompTimeExpression | |
| 73 | ||
| 74 | CompTimeExpression = option("comptime") Expression | |
| 73 | 75 | |
| 74 | 76 | SwitchExpression = option("inline") "switch" "(" Expression ")" "{" many(SwitchProng) "}" |
| 75 | 77 | |
| ... | ... | @@ -141,14 +143,12 @@ StructLiteralField = "." Symbol "=" Expression |
| 141 | 143 | |
| 142 | 144 | PrefixOp = "!" | "-" | "~" | "*" | ("&" option("const")) | "?" | "%" | "%%" | "??" | "-%" |
| 143 | 145 | |
| 144 | PrimaryExpression = Number | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | CompTimeExpression | BlockExpression | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl | |
| 146 | PrimaryExpression = Number | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl | |
| 145 | 147 | |
| 146 | 148 | ArrayType = "[" option(Expression) "]" option("const") TypeExpr |
| 147 | 149 | |
| 148 | 150 | GotoExpression = "goto" Symbol |
| 149 | 151 | |
| 150 | CompTimeExpression = option("comptime") Expression | |
| 151 | ||
| 152 | 152 | GroupedExpression = "(" Expression ")" |
| 153 | 153 | |
| 154 | 154 | KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" | "type" | "this" |
example/cat/main.zig+4-12| ... | ... | @@ -16,9 +16,7 @@ pub fn main(args: [][]u8) -> %void { |
| 16 | 16 | } else { |
| 17 | 17 | var is: io.InStream = undefined; |
| 18 | 18 | is.open(arg) %% |err| { |
| 19 | %%io.stderr.printf("Unable to open file: "); | |
| 20 | %%io.stderr.printf(@errorName(err)); | |
| 21 | %%io.stderr.printf("\n"); | |
| 19 | %%io.stderr.printf("Unable to open file: {}\n", @errorName(err)); | |
| 22 | 20 | return err; |
| 23 | 21 | }; |
| 24 | 22 | defer %%is.close(); |
| ... | ... | @@ -34,9 +32,7 @@ pub fn main(args: [][]u8) -> %void { |
| 34 | 32 | } |
| 35 | 33 | |
| 36 | 34 | fn usage(exe: []u8) -> %void { |
| 37 | %%io.stderr.printf("Usage: "); | |
| 38 | %%io.stderr.printf(exe); | |
| 39 | %%io.stderr.printf(" [FILE]...\n"); | |
| 35 | %%io.stderr.printf("Usage: {} [FILE]...\n", exe); | |
| 40 | 36 | return error.Invalid; |
| 41 | 37 | } |
| 42 | 38 | |
| ... | ... | @@ -45,9 +41,7 @@ fn cat_stream(is: &io.InStream) -> %void { |
| 45 | 41 | |
| 46 | 42 | while (true) { |
| 47 | 43 | const bytes_read = is.read(buf) %% |err| { |
| 48 | %%io.stderr.printf("Unable to read from stream: "); | |
| 49 | %%io.stderr.printf(@errorName(err)); | |
| 50 | %%io.stderr.printf("\n"); | |
| 44 | %%io.stderr.printf("Unable to read from stream: {}\n", @errorName(err)); | |
| 51 | 45 | return err; |
| 52 | 46 | }; |
| 53 | 47 | |
| ... | ... | @@ -56,9 +50,7 @@ fn cat_stream(is: &io.InStream) -> %void { |
| 56 | 50 | } |
| 57 | 51 | |
| 58 | 52 | io.stdout.write(buf[0...bytes_read]) %% |err| { |
| 59 | %%io.stderr.printf("Unable to write to stdout: "); | |
| 60 | %%io.stderr.printf(@errorName(err)); | |
| 61 | %%io.stderr.printf("\n"); | |
| 53 | %%io.stderr.printf("Unable to write to stdout: {}\n", @errorName(err)); | |
| 62 | 54 | return err; |
| 63 | 55 | }; |
| 64 | 56 | } |
src/all_types.hpp-2| ... | ... | @@ -1042,8 +1042,6 @@ struct FnTableEntry { |
| 1042 | 1042 | |
| 1043 | 1043 | ZigList<IrInstruction *> alloca_list; |
| 1044 | 1044 | ZigList<VariableTableEntry *> variable_list; |
| 1045 | ||
| 1046 | VariableTableEntry *var_args_var; | |
| 1047 | 1045 | }; |
| 1048 | 1046 | |
| 1049 | 1047 | uint32_t fn_table_entry_hash(FnTableEntry*); |
src/analyze.cpp+7-1| ... | ... | @@ -3263,7 +3263,13 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { |
| 3263 | 3263 | case TypeTableEntryIdArray: |
| 3264 | 3264 | zig_panic("TODO"); |
| 3265 | 3265 | case TypeTableEntryIdStruct: |
| 3266 | zig_panic("TODO"); | |
| 3266 | for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) { | |
| 3267 | ConstExprValue *field_a = &a->data.x_struct.fields[i]; | |
| 3268 | ConstExprValue *field_b = &b->data.x_struct.fields[i]; | |
| 3269 | if (!const_values_equal(field_a, field_b)) | |
| 3270 | return false; | |
| 3271 | } | |
| 3272 | return true; | |
| 3267 | 3273 | case TypeTableEntryIdUnion: |
| 3268 | 3274 | zig_panic("TODO"); |
| 3269 | 3275 | case TypeTableEntryIdUndefLit: |
src/ast_render.cpp+1| ... | ... | @@ -600,6 +600,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { |
| 600 | 600 | } |
| 601 | 601 | |
| 602 | 602 | ar->indent -= ar->indent_size; |
| 603 | print_indent(ar); | |
| 603 | 604 | fprintf(ar->f, "}"); |
| 604 | 605 | break; |
| 605 | 606 | } |
src/ir.cpp+46-16| ... | ... | @@ -5743,6 +5743,8 @@ static IrBasicBlock *ir_get_new_bb(IrAnalyze *ira, IrBasicBlock *old_bb) { |
| 5743 | 5743 | IrInstruction *dep_instruction = ir_instruction_get_dep(instruction, dep_i); |
| 5744 | 5744 | if (dep_instruction == nullptr) |
| 5745 | 5745 | break; |
| 5746 | if (dep_instruction->other) | |
| 5747 | continue; | |
| 5746 | 5748 | if (dep_instruction->owner_bb == old_bb) |
| 5747 | 5749 | continue; |
| 5748 | 5750 | ir_get_new_bb(ira, dep_instruction->owner_bb); |
| ... | ... | @@ -7565,19 +7567,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod |
| 7565 | 7567 | } |
| 7566 | 7568 | |
| 7567 | 7569 | Buf *param_name = param_decl_node->data.param_decl.name; |
| 7568 | if (is_var_args) { | |
| 7569 | if (!impl_fn->var_args_var) { | |
| 7570 | ConstExprValue *var_args_val = create_const_arg_tuple(ira->codegen, | |
| 7571 | fn_type_id->param_count, fn_type_id->param_count + 1); | |
| 7572 | VariableTableEntry *var = add_variable(ira->codegen, param_decl_node, | |
| 7573 | *child_scope, param_name, true, var_args_val); | |
| 7574 | var->value.depends_on_compile_var = true; | |
| 7575 | *child_scope = var->child_scope; | |
| 7576 | impl_fn->var_args_var = var; | |
| 7577 | } | |
| 7578 | impl_fn->var_args_var->value.data.x_arg_tuple.end_index = fn_type_id->param_count + 1; | |
| 7579 | ||
| 7580 | } else { | |
| 7570 | if (!is_var_args) { | |
| 7581 | 7571 | VariableTableEntry *var = add_variable(ira->codegen, param_decl_node, |
| 7582 | 7572 | *child_scope, param_name, true, arg_val); |
| 7583 | 7573 | var->value.depends_on_compile_var = true; |
| ... | ... | @@ -7611,7 +7601,8 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal |
| 7611 | 7601 | { |
| 7612 | 7602 | FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; |
| 7613 | 7603 | size_t first_arg_1_or_0 = first_arg_ptr ? 1 : 0; |
| 7614 | size_t src_param_count = fn_type_id->param_count; | |
| 7604 | size_t var_args_1_or_0 = fn_type_id->is_var_args ? 1 : 0; | |
| 7605 | size_t src_param_count = fn_type_id->param_count - var_args_1_or_0; | |
| 7615 | 7606 | size_t call_param_count = call_instruction->arg_count + first_arg_1_or_0; |
| 7616 | 7607 | AstNode *source_node = call_instruction->base.source_node; |
| 7617 | 7608 | |
| ... | ... | @@ -7739,11 +7730,22 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal |
| 7739 | 7730 | return ira->codegen->builtin_types.entry_invalid; |
| 7740 | 7731 | } |
| 7741 | 7732 | } |
| 7733 | ||
| 7734 | bool found_first_var_arg = false; | |
| 7735 | size_t first_var_arg = inst_fn_type_id.param_count; | |
| 7742 | 7736 | for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) { |
| 7743 | 7737 | IrInstruction *arg = call_instruction->args[call_i]->other; |
| 7744 | 7738 | if (arg->value.type->id == TypeTableEntryIdInvalid) |
| 7745 | 7739 | return ira->codegen->builtin_types.entry_invalid; |
| 7746 | 7740 | |
| 7741 | AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i); | |
| 7742 | assert(param_decl_node->type == NodeTypeParamDecl); | |
| 7743 | bool is_var_args = param_decl_node->data.param_decl.is_var_args; | |
| 7744 | if (is_var_args && !found_first_var_arg) { | |
| 7745 | first_var_arg = inst_fn_type_id.param_count; | |
| 7746 | found_first_var_arg = true; | |
| 7747 | } | |
| 7748 | ||
| 7747 | 7749 | if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg, &impl_fn->child_scope, |
| 7748 | 7750 | &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn)) |
| 7749 | 7751 | { |
| ... | ... | @@ -7751,6 +7753,17 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal |
| 7751 | 7753 | } |
| 7752 | 7754 | } |
| 7753 | 7755 | |
| 7756 | if (fn_proto_node->data.fn_proto.is_var_args) { | |
| 7757 | AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i); | |
| 7758 | Buf *param_name = param_decl_node->data.param_decl.name; | |
| 7759 | ||
| 7760 | ConstExprValue *var_args_val = create_const_arg_tuple(ira->codegen, | |
| 7761 | first_var_arg, inst_fn_type_id.param_count); | |
| 7762 | VariableTableEntry *var = add_variable(ira->codegen, param_decl_node, | |
| 7763 | impl_fn->child_scope, param_name, true, var_args_val); | |
| 7764 | var->value.depends_on_compile_var = true; | |
| 7765 | impl_fn->child_scope = var->child_scope; | |
| 7766 | } | |
| 7754 | 7767 | { |
| 7755 | 7768 | AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; |
| 7756 | 7769 | TypeTableEntry *return_type = analyze_type_expr(ira->codegen, impl_fn->child_scope, return_type_node); |
| ... | ... | @@ -8317,8 +8330,9 @@ static TypeTableEntry *ir_analyze_var_ptr(IrAnalyze *ira, IrInstruction *instruc |
| 8317 | 8330 | if (mem_slot && mem_slot->special != ConstValSpecialRuntime) { |
| 8318 | 8331 | ConstPtrSpecial ptr_special = is_comptime ? ConstPtrSpecialInline : ConstPtrSpecialNone; |
| 8319 | 8332 | bool is_const = (var->value.type->id == TypeTableEntryIdMetaType) ? is_const_ptr : var->src_is_const; |
| 8333 | depends_on_compile_var = mem_slot->depends_on_compile_var || depends_on_compile_var || is_comptime; | |
| 8320 | 8334 | return ir_analyze_const_ptr(ira, instruction, mem_slot, var->value.type, |
| 8321 | mem_slot->depends_on_compile_var || depends_on_compile_var, ptr_special, is_const); | |
| 8335 | depends_on_compile_var, ptr_special, is_const); | |
| 8322 | 8336 | } else { |
| 8323 | 8337 | ir_build_var_ptr_from(&ira->new_irb, instruction, var, false); |
| 8324 | 8338 | type_ensure_zero_bits_known(ira->codegen, var->value.type); |
| ... | ... | @@ -8513,6 +8527,22 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field |
| 8513 | 8527 | |
| 8514 | 8528 | TypeStructField *field = find_struct_type_field(bare_type, field_name); |
| 8515 | 8529 | if (field) { |
| 8530 | if (instr_is_comptime(container_ptr)) { | |
| 8531 | ConstExprValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); | |
| 8532 | if (!ptr_val) | |
| 8533 | return ira->codegen->builtin_types.entry_invalid; | |
| 8534 | ||
| 8535 | ConstExprValue *struct_val = const_ptr_pointee(ptr_val); | |
| 8536 | if (value_is_comptime(struct_val)) { | |
| 8537 | ConstExprValue *field_val = &struct_val->data.x_struct.fields[field->src_index]; | |
| 8538 | if (value_is_comptime(field_val)) { | |
| 8539 | bool depends_on_compile_var = field_val->depends_on_compile_var || | |
| 8540 | struct_val->depends_on_compile_var || ptr_val->depends_on_compile_var; | |
| 8541 | return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, field_val, | |
| 8542 | field_val->type, depends_on_compile_var, ConstPtrSpecialNone, is_const); | |
| 8543 | } | |
| 8544 | } | |
| 8545 | } | |
| 8516 | 8546 | ir_build_struct_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field); |
| 8517 | 8547 | return get_pointer_to_type(ira->codegen, field->type_entry, is_const); |
| 8518 | 8548 | } else { |
| ... | ... | @@ -10937,7 +10967,7 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio |
| 10937 | 10967 | init_const_ptr(ira->codegen, ptr_val, base_ptr, index, instruction->is_const); |
| 10938 | 10968 | |
| 10939 | 10969 | ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index]; |
| 10940 | init_const_usize(ira->codegen, len_val, rel_end); | |
| 10970 | init_const_usize(ira->codegen, len_val, end_scalar - start_scalar); | |
| 10941 | 10971 | |
| 10942 | 10972 | return return_type; |
| 10943 | 10973 | } |
src/parser.cpp+6-6| ... | ... | @@ -624,7 +624,7 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b |
| 624 | 624 | } |
| 625 | 625 | |
| 626 | 626 | /* |
| 627 | PrimaryExpression = Number | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | CompTimeExpression | BlockExpression | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl | |
| 627 | PrimaryExpression = Number | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl | |
| 628 | 628 | KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" | "type" | "this" |
| 629 | 629 | */ |
| 630 | 630 | static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) { |
| ... | ... | @@ -709,10 +709,6 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo |
| 709 | 709 | if (goto_node) |
| 710 | 710 | return goto_node; |
| 711 | 711 | |
| 712 | AstNode *comptime_node = ast_parse_comptime_expr(pc, token_index, false); | |
| 713 | if (comptime_node) | |
| 714 | return comptime_node; | |
| 715 | ||
| 716 | 712 | AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false); |
| 717 | 713 | if (grouped_expr_node) { |
| 718 | 714 | return grouped_expr_node; |
| ... | ... | @@ -1810,7 +1806,7 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, boo |
| 1810 | 1806 | } |
| 1811 | 1807 | |
| 1812 | 1808 | /* |
| 1813 | BlockExpression : IfExpression | Block | WhileExpression | ForExpression | SwitchExpression | |
| 1809 | BlockExpression = IfExpression | Block | WhileExpression | ForExpression | SwitchExpression | CompTimeExpression | |
| 1814 | 1810 | */ |
| 1815 | 1811 | static AstNode *ast_parse_block_expr(ParseContext *pc, size_t *token_index, bool mandatory) { |
| 1816 | 1812 | Token *token = &pc->tokens->at(*token_index); |
| ... | ... | @@ -1835,6 +1831,10 @@ static AstNode *ast_parse_block_expr(ParseContext *pc, size_t *token_index, bool |
| 1835 | 1831 | if (block) |
| 1836 | 1832 | return block; |
| 1837 | 1833 | |
| 1834 | AstNode *comptime_node = ast_parse_comptime_expr(pc, token_index, false); | |
| 1835 | if (comptime_node) | |
| 1836 | return comptime_node; | |
| 1837 | ||
| 1838 | 1838 | if (mandatory) |
| 1839 | 1839 | ast_invalid_token_error(pc, token); |
| 1840 | 1840 |
test/cases/eval.zig+14| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const assert = @import("std").debug.assert; |
| 2 | const str = @import("std").str; | |
| 2 | 3 | |
| 3 | 4 | fn compileTimeRecursion() { |
| 4 | 5 | @setFnTest(this); |
| ... | ... | @@ -161,3 +162,16 @@ fn staticallyInitializedArrayLiteral() { |
| 161 | 162 | assert(y[3] == 4); |
| 162 | 163 | } |
| 163 | 164 | const st_init_arr_lit_x = []u8{1,2,3,4}; |
| 165 | ||
| 166 | ||
| 167 | fn constSlice() { | |
| 168 | @setFnTest(this); | |
| 169 | ||
| 170 | comptime { | |
| 171 | const a = "1234567890"; | |
| 172 | assert(a.len == 10); | |
| 173 | const b = a[1...2]; | |
| 174 | assert(b.len == 1); | |
| 175 | assert(b[0] == '2'); | |
| 176 | } | |
| 177 | } |
test/cases/generics.zig+1-1| ... | ... | @@ -13,7 +13,7 @@ fn max(comptime T: type, a: T, b: T) -> T { |
| 13 | 13 | } |
| 14 | 14 | |
| 15 | 15 | fn add(comptime a: i32, b: i32) -> i32 { |
| 16 | return comptime {a} + b; | |
| 16 | return (comptime {a}) + b; | |
| 17 | 17 | } |
| 18 | 18 | |
| 19 | 19 | const the_max = max(u32, 1234, 5678); |
test/cases/var_args.zig+1| ... | ... | @@ -13,4 +13,5 @@ fn testAddArbitraryArgs() { |
| 13 | 13 | |
| 14 | 14 | assert(add(i32(1), i32(2), i32(3), i32(4)) == 10); |
| 15 | 15 | assert(add(i32(1234)) == 1234); |
| 16 | assert(add() == 0); | |
| 16 | 17 | } |