diff --git a/doc/langref.md b/doc/langref.md index 5df990ad65..6c2789367b 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -69,7 +69,9 @@ AssignmentExpression = UnwrapExpression AssignmentOperator UnwrapExpression | Un AssignmentOperator = "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" | "&&=" | "||=" | "*%=" | "+%=" | "-%=" | "<<%=" -BlockExpression = IfExpression | Block | WhileExpression | ForExpression | SwitchExpression +BlockExpression = IfExpression | Block | WhileExpression | ForExpression | SwitchExpression | CompTimeExpression + +CompTimeExpression = option("comptime") Expression SwitchExpression = option("inline") "switch" "(" Expression ")" "{" many(SwitchProng) "}" @@ -141,14 +143,12 @@ StructLiteralField = "." Symbol "=" Expression PrefixOp = "!" | "-" | "~" | "*" | ("&" option("const")) | "?" | "%" | "%%" | "??" | "-%" -PrimaryExpression = Number | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | CompTimeExpression | BlockExpression | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl +PrimaryExpression = Number | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl ArrayType = "[" option(Expression) "]" option("const") TypeExpr GotoExpression = "goto" Symbol -CompTimeExpression = option("comptime") Expression - GroupedExpression = "(" Expression ")" KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" | "type" | "this" diff --git a/example/cat/main.zig b/example/cat/main.zig index a24b268228..7aaefd8bf7 100644 --- a/example/cat/main.zig +++ b/example/cat/main.zig @@ -16,9 +16,7 @@ pub fn main(args: [][]u8) -> %void { } else { var is: io.InStream = undefined; is.open(arg) %% |err| { - %%io.stderr.printf("Unable to open file: "); - %%io.stderr.printf(@errorName(err)); - %%io.stderr.printf("\n"); + %%io.stderr.printf("Unable to open file: {}\n", @errorName(err)); return err; }; defer %%is.close(); @@ -34,9 +32,7 @@ pub fn main(args: [][]u8) -> %void { } fn usage(exe: []u8) -> %void { - %%io.stderr.printf("Usage: "); - %%io.stderr.printf(exe); - %%io.stderr.printf(" [FILE]...\n"); + %%io.stderr.printf("Usage: {} [FILE]...\n", exe); return error.Invalid; } @@ -45,9 +41,7 @@ fn cat_stream(is: &io.InStream) -> %void { while (true) { const bytes_read = is.read(buf) %% |err| { - %%io.stderr.printf("Unable to read from stream: "); - %%io.stderr.printf(@errorName(err)); - %%io.stderr.printf("\n"); + %%io.stderr.printf("Unable to read from stream: {}\n", @errorName(err)); return err; }; @@ -56,9 +50,7 @@ fn cat_stream(is: &io.InStream) -> %void { } io.stdout.write(buf[0...bytes_read]) %% |err| { - %%io.stderr.printf("Unable to write to stdout: "); - %%io.stderr.printf(@errorName(err)); - %%io.stderr.printf("\n"); + %%io.stderr.printf("Unable to write to stdout: {}\n", @errorName(err)); return err; }; } diff --git a/src/all_types.hpp b/src/all_types.hpp index 76e759a1f9..c5b78fe2b5 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1042,8 +1042,6 @@ struct FnTableEntry { ZigList alloca_list; ZigList variable_list; - - VariableTableEntry *var_args_var; }; uint32_t fn_table_entry_hash(FnTableEntry*); diff --git a/src/analyze.cpp b/src/analyze.cpp index aeeefb3eed..d773efdd86 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3263,7 +3263,13 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { case TypeTableEntryIdArray: zig_panic("TODO"); case TypeTableEntryIdStruct: - zig_panic("TODO"); + for (size_t i = 0; i < a->type->data.structure.src_field_count; i += 1) { + ConstExprValue *field_a = &a->data.x_struct.fields[i]; + ConstExprValue *field_b = &b->data.x_struct.fields[i]; + if (!const_values_equal(field_a, field_b)) + return false; + } + return true; case TypeTableEntryIdUnion: zig_panic("TODO"); case TypeTableEntryIdUndefLit: diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 2166d68cd5..3bba70486a 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -600,6 +600,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { } ar->indent -= ar->indent_size; + print_indent(ar); fprintf(ar->f, "}"); break; } diff --git a/src/ir.cpp b/src/ir.cpp index e3ab55b72e..588859ea8e 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5743,6 +5743,8 @@ static IrBasicBlock *ir_get_new_bb(IrAnalyze *ira, IrBasicBlock *old_bb) { IrInstruction *dep_instruction = ir_instruction_get_dep(instruction, dep_i); if (dep_instruction == nullptr) break; + if (dep_instruction->other) + continue; if (dep_instruction->owner_bb == old_bb) continue; 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 } Buf *param_name = param_decl_node->data.param_decl.name; - if (is_var_args) { - if (!impl_fn->var_args_var) { - ConstExprValue *var_args_val = create_const_arg_tuple(ira->codegen, - fn_type_id->param_count, fn_type_id->param_count + 1); - VariableTableEntry *var = add_variable(ira->codegen, param_decl_node, - *child_scope, param_name, true, var_args_val); - var->value.depends_on_compile_var = true; - *child_scope = var->child_scope; - impl_fn->var_args_var = var; - } - impl_fn->var_args_var->value.data.x_arg_tuple.end_index = fn_type_id->param_count + 1; - - } else { + if (!is_var_args) { VariableTableEntry *var = add_variable(ira->codegen, param_decl_node, *child_scope, param_name, true, arg_val); var->value.depends_on_compile_var = true; @@ -7611,7 +7601,8 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal { FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; size_t first_arg_1_or_0 = first_arg_ptr ? 1 : 0; - size_t src_param_count = fn_type_id->param_count; + size_t var_args_1_or_0 = fn_type_id->is_var_args ? 1 : 0; + size_t src_param_count = fn_type_id->param_count - var_args_1_or_0; size_t call_param_count = call_instruction->arg_count + first_arg_1_or_0; AstNode *source_node = call_instruction->base.source_node; @@ -7739,11 +7730,22 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal return ira->codegen->builtin_types.entry_invalid; } } + + bool found_first_var_arg = false; + size_t first_var_arg = inst_fn_type_id.param_count; for (size_t call_i = 0; call_i < call_instruction->arg_count; call_i += 1) { IrInstruction *arg = call_instruction->args[call_i]->other; if (arg->value.type->id == TypeTableEntryIdInvalid) return ira->codegen->builtin_types.entry_invalid; + AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i); + assert(param_decl_node->type == NodeTypeParamDecl); + bool is_var_args = param_decl_node->data.param_decl.is_var_args; + if (is_var_args && !found_first_var_arg) { + first_var_arg = inst_fn_type_id.param_count; + found_first_var_arg = true; + } + if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg, &impl_fn->child_scope, &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn)) { @@ -7751,6 +7753,17 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal } } + if (fn_proto_node->data.fn_proto.is_var_args) { + AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i); + Buf *param_name = param_decl_node->data.param_decl.name; + + ConstExprValue *var_args_val = create_const_arg_tuple(ira->codegen, + first_var_arg, inst_fn_type_id.param_count); + VariableTableEntry *var = add_variable(ira->codegen, param_decl_node, + impl_fn->child_scope, param_name, true, var_args_val); + var->value.depends_on_compile_var = true; + impl_fn->child_scope = var->child_scope; + } { AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; 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 if (mem_slot && mem_slot->special != ConstValSpecialRuntime) { ConstPtrSpecial ptr_special = is_comptime ? ConstPtrSpecialInline : ConstPtrSpecialNone; bool is_const = (var->value.type->id == TypeTableEntryIdMetaType) ? is_const_ptr : var->src_is_const; + depends_on_compile_var = mem_slot->depends_on_compile_var || depends_on_compile_var || is_comptime; return ir_analyze_const_ptr(ira, instruction, mem_slot, var->value.type, - mem_slot->depends_on_compile_var || depends_on_compile_var, ptr_special, is_const); + depends_on_compile_var, ptr_special, is_const); } else { ir_build_var_ptr_from(&ira->new_irb, instruction, var, false); 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 TypeStructField *field = find_struct_type_field(bare_type, field_name); if (field) { + if (instr_is_comptime(container_ptr)) { + ConstExprValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); + if (!ptr_val) + return ira->codegen->builtin_types.entry_invalid; + + ConstExprValue *struct_val = const_ptr_pointee(ptr_val); + if (value_is_comptime(struct_val)) { + ConstExprValue *field_val = &struct_val->data.x_struct.fields[field->src_index]; + if (value_is_comptime(field_val)) { + bool depends_on_compile_var = field_val->depends_on_compile_var || + struct_val->depends_on_compile_var || ptr_val->depends_on_compile_var; + return ir_analyze_const_ptr(ira, &field_ptr_instruction->base, field_val, + field_val->type, depends_on_compile_var, ConstPtrSpecialNone, is_const); + } + } + } ir_build_struct_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field); return get_pointer_to_type(ira->codegen, field->type_entry, is_const); } else { @@ -10937,7 +10967,7 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio init_const_ptr(ira->codegen, ptr_val, base_ptr, index, instruction->is_const); ConstExprValue *len_val = &out_val->data.x_struct.fields[slice_len_index]; - init_const_usize(ira->codegen, len_val, rel_end); + init_const_usize(ira->codegen, len_val, end_scalar - start_scalar); return return_type; } diff --git a/src/parser.cpp b/src/parser.cpp index 04c31fcfe0..02926db46a 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -624,7 +624,7 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b } /* -PrimaryExpression = Number | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | CompTimeExpression | BlockExpression | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl +PrimaryExpression = Number | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" | "type" | "this" */ 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 if (goto_node) return goto_node; - AstNode *comptime_node = ast_parse_comptime_expr(pc, token_index, false); - if (comptime_node) - return comptime_node; - AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false); if (grouped_expr_node) { return grouped_expr_node; @@ -1810,7 +1806,7 @@ static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, boo } /* -BlockExpression : IfExpression | Block | WhileExpression | ForExpression | SwitchExpression +BlockExpression = IfExpression | Block | WhileExpression | ForExpression | SwitchExpression | CompTimeExpression */ static AstNode *ast_parse_block_expr(ParseContext *pc, size_t *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -1835,6 +1831,10 @@ static AstNode *ast_parse_block_expr(ParseContext *pc, size_t *token_index, bool if (block) return block; + AstNode *comptime_node = ast_parse_comptime_expr(pc, token_index, false); + if (comptime_node) + return comptime_node; + if (mandatory) ast_invalid_token_error(pc, token); diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 4b6a095726..2d6c6a8b94 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -1,4 +1,5 @@ const assert = @import("std").debug.assert; +const str = @import("std").str; fn compileTimeRecursion() { @setFnTest(this); @@ -161,3 +162,16 @@ fn staticallyInitializedArrayLiteral() { assert(y[3] == 4); } const st_init_arr_lit_x = []u8{1,2,3,4}; + + +fn constSlice() { + @setFnTest(this); + + comptime { + const a = "1234567890"; + assert(a.len == 10); + const b = a[1...2]; + assert(b.len == 1); + assert(b[0] == '2'); + } +} diff --git a/test/cases/generics.zig b/test/cases/generics.zig index 069b468966..822c7e0833 100644 --- a/test/cases/generics.zig +++ b/test/cases/generics.zig @@ -13,7 +13,7 @@ fn max(comptime T: type, a: T, b: T) -> T { } fn add(comptime a: i32, b: i32) -> i32 { - return comptime {a} + b; + return (comptime {a}) + b; } const the_max = max(u32, 1234, 5678); diff --git a/test/cases/var_args.zig b/test/cases/var_args.zig index 6b30edd258..680380d7d2 100644 --- a/test/cases/var_args.zig +++ b/test/cases/var_args.zig @@ -13,4 +13,5 @@ fn testAddArbitraryArgs() { assert(add(i32(1), i32(2), i32(3), i32(4)) == 10); assert(add(i32(1234)) == 1234); + assert(add() == 0); }