From fc100d7b3b27bd514dca4e02c160e5b96d4da648 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 9 Feb 2017 02:50:03 -0500 Subject: lots of miscellaneous things all in one big commit * add `@compileLog(...)` builtin function - Helps debug code running at compile time - See #240 * fix crash when there is an error on the start value of a slice * add implicit cast from int and float types to int and float literals if the value is known at compile time * make array concatenation work with slices in addition to arrays and c string literals * fix compile error message for something not having field access * fix crash when `@setDebugSafety()` was called from a function being evaluated at compile-time * fix compile-time evaluation of overflow math builtins. * avoid debug safety panic handler in builtin.o and compiler_rt.o since we use no debug safety in these modules anyway * add compiler_rt functions for division on ARM - Closes #254 * move default panic handler to std.debug so users can call it manually * std.io.printf supports a width in the format specifier --- doc/langref.md | 19 ++- src/all_types.hpp | 9 ++ src/analyze.cpp | 24 +++- src/codegen.cpp | 2 + src/ir.cpp | 131 ++++++++++++++++++- src/ir_print.cpp | 14 +++ std/builtin.zig | 5 + std/compiler_rt.zig | 263 ++++++++++++++++++++++++++++++++++++++- std/debug.zig | 21 ++++ std/io.zig | 134 ++++++++++++++------ std/math.zig | 17 +++ std/panic.zig | 23 +--- test/cases/enum_with_members.zig | 4 +- test/cases/eval.zig | 12 ++ test/run_tests.cpp | 22 +++- 15 files changed, 622 insertions(+), 78 deletions(-) diff --git a/doc/langref.md b/doc/langref.md index b8a6a5775a..0aace75996 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -639,10 +639,23 @@ const b: u8 = @truncate(u8, a); ### @compileError(comptime msg: []u8) -This function, when semantically analyzed, causes a compile error with the message `msg`. +This function, when semantically analyzed, causes a compile error with the +message `msg`. -There are several ways that code avoids being semantically checked, such as using `if` -or `switch` with compile time constants, and comptime functions. +There are several ways that code avoids being semantically checked, such as +using `if` or `switch` with compile time constants, and comptime functions. + +### @compileLog(args: ...) + +This function, when semantically analyzed, causes a compile error, but it does +not prevent compile-time code from continuing to run, and it otherwise does not +interfere with analysis. + +Each of the arguments will be serialized to a printable debug value and output +to stderr, and then a newline at the end. + +This function can be used to do "printf debugging" on compile-time executing +code. ### @intType(comptime is_signed: bool, comptime bit_count: u8) -> type diff --git a/src/all_types.hpp b/src/all_types.hpp index bbd8df4b70..1e2ff3aaa4 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1096,6 +1096,7 @@ enum BuiltinFnId { BuiltinFnIdCUndef, BuiltinFnIdCompileVar, BuiltinFnIdCompileErr, + BuiltinFnIdCompileLog, BuiltinFnIdGeneratedCode, BuiltinFnIdCtz, BuiltinFnIdClz, @@ -1541,6 +1542,7 @@ enum IrInstructionId { IrInstructionIdMinValue, IrInstructionIdMaxValue, IrInstructionIdCompileErr, + IrInstructionIdCompileLog, IrInstructionIdErrName, IrInstructionIdEmbedFile, IrInstructionIdCmpxchg, @@ -1993,6 +1995,13 @@ struct IrInstructionCompileErr { IrInstruction *msg; }; +struct IrInstructionCompileLog { + IrInstruction base; + + size_t msg_count; + IrInstruction **msg_list; +}; + struct IrInstructionErrName { IrInstruction base; diff --git a/src/analyze.cpp b/src/analyze.cpp index 7fd1fee675..984fa32a83 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3439,7 +3439,8 @@ void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue * void render_const_value(Buf *buf, ConstExprValue *const_val) { switch (const_val->special) { case ConstValSpecialRuntime: - zig_unreachable(); + buf_appendf(buf, "(runtime value)"); + return; case ConstValSpecialUndef: buf_appendf(buf, "undefined"); return; @@ -3522,7 +3523,28 @@ void render_const_value(Buf *buf, ConstExprValue *const_val) { } case TypeTableEntryIdArray: { + TypeTableEntry *child_type = canon_type->data.array.child_type; uint64_t len = canon_type->data.array.len; + + // if it's []u8, assume UTF-8 and output a string + if (child_type->id == TypeTableEntryIdInt && + child_type->data.integral.bit_count == 8 && + !child_type->data.integral.is_signed) + { + buf_append_char(buf, '"'); + for (uint64_t i = 0; i < len; i += 1) { + ConstExprValue *child_value = &const_val->data.x_array.elements[i]; + uint64_t x = child_value->data.x_bignum.data.x_uint; + if (x == '"') { + buf_append_str(buf, "\\\""); + } else { + buf_append_char(buf, x); + } + } + buf_append_char(buf, '"'); + return; + } + buf_appendf(buf, "%s{", buf_ptr(&canon_type->name)); for (uint64_t i = 0; i < len; i += 1) { if (i != 0) diff --git a/src/codegen.cpp b/src/codegen.cpp index a7ed6cd704..5849bf8e6d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2377,6 +2377,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdMinValue: case IrInstructionIdMaxValue: case IrInstructionIdCompileErr: + case IrInstructionIdCompileLog: case IrInstructionIdArrayLen: case IrInstructionIdImport: case IrInstructionIdCImport: @@ -3791,6 +3792,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdDivExact, "divExact", 2); create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2); create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); + create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); create_builtin_fn(g, BuiltinFnIdIntType, "intType", 2); create_builtin_fn(g, BuiltinFnIdUnreachable, "unreachable", 0); create_builtin_fn(g, BuiltinFnIdSetFnTest, "setFnTest", 1); diff --git a/src/ir.cpp b/src/ir.cpp index e89f89f89c..7af27ec765 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -375,6 +375,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCompileErr *) { return IrInstructionIdCompileErr; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionCompileLog *) { + return IrInstructionIdCompileLog; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionErrName *) { return IrInstructionIdErrName; } @@ -1510,6 +1514,20 @@ static IrInstruction *ir_build_compile_err(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } +static IrInstruction *ir_build_compile_log(IrBuilder *irb, Scope *scope, AstNode *source_node, + size_t msg_count, IrInstruction **msg_list) +{ + IrInstructionCompileLog *instruction = ir_build_instruction(irb, scope, source_node); + instruction->msg_count = msg_count; + instruction->msg_list = msg_list; + + for (size_t i = 0; i < msg_count; i += 1) { + ir_ref_instruction(msg_list[i], irb->current_basic_block); + } + + return &instruction->base; +} + static IrInstruction *ir_build_err_name(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) { IrInstructionErrName *instruction = ir_build_instruction(irb, scope, source_node); instruction->value = value; @@ -2461,6 +2479,12 @@ static IrInstruction *ir_instruction_compileerr_get_dep(IrInstructionCompileErr } } +static IrInstruction *ir_instruction_compilelog_get_dep(IrInstructionCompileLog *instruction, size_t index) { + if (index < instruction->msg_count) + return instruction->msg_list[index]; + return nullptr; +} + static IrInstruction *ir_instruction_errname_get_dep(IrInstructionErrName *instruction, size_t index) { switch (index) { case 0: return instruction->value; @@ -2848,6 +2872,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t return ir_instruction_maxvalue_get_dep((IrInstructionMaxValue *) instruction, index); case IrInstructionIdCompileErr: return ir_instruction_compileerr_get_dep((IrInstructionCompileErr *) instruction, index); + case IrInstructionIdCompileLog: + return ir_instruction_compilelog_get_dep((IrInstructionCompileLog *) instruction, index); case IrInstructionIdErrName: return ir_instruction_errname_get_dep((IrInstructionErrName *) instruction, index); case IrInstructionIdEmbedFile: @@ -3767,7 +3793,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo BuiltinFnEntry *builtin_fn = entry->value; size_t actual_param_count = node->data.fn_call_expr.params.length; - if (builtin_fn->param_count != actual_param_count) { + if (builtin_fn->param_count != SIZE_MAX && builtin_fn->param_count != actual_param_count) { add_node_error(irb->codegen, node, buf_sprintf("expected %zu arguments, found %zu", builtin_fn->param_count, actual_param_count)); @@ -3958,6 +3984,19 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_compile_err(irb, scope, node, arg0_value); } + case BuiltinFnIdCompileLog: + { + IrInstruction **args = allocate(actual_param_count); + + for (size_t i = 0; i < actual_param_count; i += 1) { + AstNode *arg_node = node->data.fn_call_expr.params.at(i); + args[i] = ir_gen_node(irb, arg_node, scope); + if (args[i] == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + } + + return ir_build_compile_log(irb, scope, node, actual_param_count, args); + } case BuiltinFnIdErrName: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -5254,7 +5293,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node) return irb->codegen->invalid_instruction; IrInstruction *start_value = ir_gen_node(irb, start_node, scope); - if (ptr_value == irb->codegen->invalid_instruction) + if (start_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; IrInstruction *end_value; @@ -5800,6 +5839,16 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, } } + // implicit typed number to integer or float literal. + // works when the number is known + if (value->value.special == ConstValSpecialStatic) { + if (actual_type->id == TypeTableEntryIdInt && expected_type->id == TypeTableEntryIdNumLitInt) { + return ImplicitCastMatchResultYes; + } else if (actual_type->id == TypeTableEntryIdFloat && expected_type->id == TypeTableEntryIdNumLitFloat) { + return ImplicitCastMatchResultYes; + } + } + // implicit undefined literal to anything if (actual_type->id == TypeTableEntryIdUndefLit) { return ImplicitCastMatchResultYes; @@ -6654,6 +6703,19 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour return result; } +static IrInstruction *ir_analyze_number_to_literal(IrAnalyze *ira, IrInstruction *source_instr, + IrInstruction *target, TypeTableEntry *wanted_type) +{ + ConstExprValue *val = ir_resolve_const(ira, target, UndefBad); + if (!val) + return ira->codegen->invalid_instruction; + + IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope, + source_instr->source_node, wanted_type, true); + bignum_init_bignum(&result->value.data.x_bignum, &val->data.x_bignum); + return result; +} + static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_instr, TypeTableEntry *wanted_type, IrInstruction *value) { @@ -6858,6 +6920,15 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst } } + // explicit cast from typed number to integer or float literal. + // works when the number is known at compile time + if (instr_is_comptime(value) && + ((actual_type->id == TypeTableEntryIdInt && wanted_type->id == TypeTableEntryIdNumLitInt) || + (actual_type->id == TypeTableEntryIdFloat && wanted_type->id == TypeTableEntryIdNumLitFloat))) + { + return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type); + } + // explicit cast from %void to integer type which can fit it bool actual_type_is_void_err = actual_type->id == TypeTableEntryIdErrorUnion && !type_has_bits(actual_type->data.error.child_type); @@ -7552,6 +7623,13 @@ static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp * op1_array_val = op1_val->data.x_ptr.base_ptr; op1_array_index = op1_val->data.x_ptr.index; op1_array_end = op1_array_val->data.x_array.size - 1; + } else if (is_slice(op1_canon_type)) { + TypeTableEntry *ptr_type = op1_canon_type->data.structure.fields[slice_ptr_index].type_entry; + child_type = ptr_type->data.pointer.child_type; + ConstExprValue *ptr_val = &op1_val->data.x_struct.fields[slice_ptr_index]; + op1_array_val = ptr_val->data.x_ptr.base_ptr; + op1_array_index = ptr_val->data.x_ptr.index; + op1_array_end = op1_array_val->data.x_array.size; } else { ir_add_error(ira, op1, buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op1->value.type->name))); @@ -7585,6 +7663,18 @@ static TypeTableEntry *ir_analyze_array_cat(IrAnalyze *ira, IrInstructionBinOp * op2_array_val = op2_val->data.x_ptr.base_ptr; op2_array_index = op2_val->data.x_ptr.index; op2_array_end = op2_array_val->data.x_array.size - 1; + } else if (is_slice(op2_canon_type)) { + TypeTableEntry *ptr_type = op2_canon_type->data.structure.fields[slice_ptr_index].type_entry; + if (ptr_type->data.pointer.child_type != child_type) { + ir_add_error(ira, op2, buf_sprintf("expected array of type '%s', found '%s'", + buf_ptr(&child_type->name), + buf_ptr(&op2->value.type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + ConstExprValue *ptr_val = &op2_val->data.x_struct.fields[slice_ptr_index]; + op2_array_val = ptr_val->data.x_ptr.base_ptr; + op2_array_index = ptr_val->data.x_ptr.index; + op2_array_end = op2_array_val->data.x_array.size; } else { ir_add_error(ira, op2, buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op2->value.type->name))); @@ -9177,7 +9267,7 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru } } else { ir_add_error(ira, &field_ptr_instruction->base, - buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); + buf_sprintf("type '%s' does not support field access", buf_ptr(&child_type->name))); return ira->codegen->builtin_types.entry_invalid; } } else if (container_type->id == TypeTableEntryIdNamespace) { @@ -9528,6 +9618,12 @@ static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira, if (!target_val) return ira->codegen->builtin_types.entry_invalid; + if (ira->new_irb.exec->is_inline) { + // ignore setDebugSafety when running functions at compile time + ir_build_const_from(ira, &set_debug_safety_instruction->base, false); + return ira->codegen->builtin_types.entry_void; + } + bool *safety_off_ptr; AstNode **safety_set_node_ptr; if (target_type->id == TypeTableEntryIdBlock) { @@ -10703,6 +10799,26 @@ static TypeTableEntry *ir_analyze_instruction_compile_err(IrAnalyze *ira, return ira->codegen->builtin_types.entry_invalid; } +static TypeTableEntry *ir_analyze_instruction_compile_log(IrAnalyze *ira, IrInstructionCompileLog *instruction) { + Buf buf = BUF_INIT; + fprintf(stderr, "| "); + for (size_t i = 0; i < instruction->msg_count; i += 1) { + IrInstruction *msg = instruction->msg_list[i]->other; + if (msg->value.type->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + buf_resize(&buf, 0); + render_const_value(&buf, &msg->value); + const char *comma_str = (i != 0) ? ", " : ""; + fprintf(stderr, "%s%s", comma_str, buf_ptr(&buf)); + } + fprintf(stderr, "\n"); + + ir_add_error(ira, &instruction->base, buf_sprintf("found compile log statement")); + + ir_build_const_from(ira, &instruction->base, false); + return ira->codegen->builtin_types.entry_void; +} + static TypeTableEntry *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstructionErrName *instruction) { IrInstruction *value = instruction->value->other; if (value->value.type->id == TypeTableEntryIdInvalid) @@ -11602,13 +11718,13 @@ static TypeTableEntry *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInst out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum); break; case IrOverflowOpSub: - out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum); + out_val->data.x_bool = bignum_sub(dest_bignum, op1_bignum, op2_bignum); break; case IrOverflowOpMul: - out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum); + out_val->data.x_bool = bignum_mul(dest_bignum, op1_bignum, op2_bignum); break; case IrOverflowOpShl: - out_val->data.x_bool = bignum_add(dest_bignum, op1_bignum, op2_bignum); + out_val->data.x_bool = bignum_shl(dest_bignum, op1_bignum, op2_bignum); break; } if (!bignum_fits_in_bits(dest_bignum, canon_type->data.integral.bit_count, @@ -12007,6 +12123,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_max_value(ira, (IrInstructionMaxValue *)instruction); case IrInstructionIdCompileErr: return ir_analyze_instruction_compile_err(ira, (IrInstructionCompileErr *)instruction); + case IrInstructionIdCompileLog: + return ir_analyze_instruction_compile_log(ira, (IrInstructionCompileLog *)instruction); case IrInstructionIdErrName: return ir_analyze_instruction_err_name(ira, (IrInstructionErrName *)instruction); case IrInstructionIdTypeName: @@ -12164,6 +12282,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdSetDebugSafety: case IrInstructionIdImport: case IrInstructionIdCompileErr: + case IrInstructionIdCompileLog: case IrInstructionIdCImport: case IrInstructionIdCInclude: case IrInstructionIdCDefine: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index a378df04b5..3c9e53662e 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -532,6 +532,17 @@ static void ir_print_compile_err(IrPrint *irp, IrInstructionCompileErr *instruct fprintf(irp->f, ")"); } +static void ir_print_compile_log(IrPrint *irp, IrInstructionCompileLog *instruction) { + fprintf(irp->f, "@compileLog("); + for (size_t i = 0; i < instruction->msg_count; i += 1) { + if (i != 0) + fprintf(irp->f, ","); + IrInstruction *msg = instruction->msg_list[i]; + ir_print_other_instruction(irp, msg); + } + fprintf(irp->f, ")"); +} + static void ir_print_err_name(IrPrint *irp, IrInstructionErrName *instruction) { fprintf(irp->f, "@errorName("); ir_print_other_instruction(irp, instruction->value); @@ -990,6 +1001,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdCompileErr: ir_print_compile_err(irp, (IrInstructionCompileErr *)instruction); break; + case IrInstructionIdCompileLog: + ir_print_compile_log(irp, (IrInstructionCompileLog *)instruction); + break; case IrInstructionIdErrName: ir_print_err_name(irp, (IrInstructionErrName *)instruction); break; diff --git a/std/builtin.zig b/std/builtin.zig index 9d150f2224..dfdad01abf 100644 --- a/std/builtin.zig +++ b/std/builtin.zig @@ -29,3 +29,8 @@ export fn memcpy(noalias dest: ?&u8, noalias src: ?&const u8, n: usize) { while (index != n; index += 1) d[index] = s[index]; } + +// Avoid dragging in the debug safety mechanisms into this .o file. +pub fn panic(message: []const u8) -> unreachable { + @unreachable(); +} diff --git a/std/compiler_rt.zig b/std/compiler_rt.zig index 3331ce0aed..aa6217b493 100644 --- a/std/compiler_rt.zig +++ b/std/compiler_rt.zig @@ -1,3 +1,13 @@ +// Avoid dragging in the debug safety mechanisms into this .o file, +// unless we're trying to test this file. +pub fn panic(message: []const u8) -> unreachable { + if (@compileVar("is_test")) { + @import("std").debug.panic(message); + } else { + @unreachable(); + } +} + const CHAR_BIT = 8; const du_int = u64; const di_int = i64; @@ -212,6 +222,106 @@ export fn __umoddi3(a: du_int, b: du_int) -> du_int { return r; } +fn isArmArch() -> bool { + return switch (@compileVar("arch")) { + Arch.armv8_2a, + Arch.armv8_1a, + Arch.armv8, + Arch.armv8m_baseline, + Arch.armv8m_mainline, + Arch.armv7, + Arch.armv7em, + Arch.armv7m, + Arch.armv7s, + Arch.armv7k, + Arch.armv6, + Arch.armv6m, + Arch.armv6k, + Arch.armv6t2, + Arch.armv5, + Arch.armv5te, + Arch.armv4t, + Arch.armeb => true, + else => false, + }; +} + +export nakedcc fn __aeabi_uidivmod() { + @setDebugSafety(this, false); + + if (comptime isArmArch()) { + asm volatile ( + \\ push { lr } + \\ sub sp, sp, #4 + \\ mov r2, sp + \\ bl __udivmodsi4 + \\ ldr r1, [sp] + \\ add sp, sp, #4 + \\ pop { pc } + ::: "r2", "r1"); + @unreachable(); + } + + @setFnVisible(this, false); +} + +export fn __udivmodsi4(a: su_int, b: su_int, rem: &su_int) -> su_int { + @setDebugSafety(this, false); + + const d = __udivsi3(a, b); + *rem = su_int(si_int(a) -% (si_int(d) * si_int(b))); + return d; +} + + +// TODO make this an alias instead of an extra function call +// https://github.com/andrewrk/zig/issues/256 + +export fn __aeabi_uidiv(n: su_int, d: su_int) -> su_int { + @setDebugSafety(this, false); + + return __udivsi3(n, d); +} + +export fn __udivsi3(n: su_int, d: su_int) -> su_int { + @setDebugSafety(this, false); + + const n_uword_bits: c_uint = @sizeOf(su_int) * CHAR_BIT; + // special cases + if (d == 0) + return 0; // ?! + if (n == 0) + return 0; + var sr: c_uint = @clz(d) - @clz(n); + // 0 <= sr <= n_uword_bits - 1 or sr large + if (sr > n_uword_bits - 1) // d > r + return 0; + if (sr == n_uword_bits - 1) // d == 1 + return n; + sr += 1; + // 1 <= sr <= n_uword_bits - 1 + // Not a special case + var q: su_int = n << (n_uword_bits - sr); + var r: su_int = n >> sr; + var carry: su_int = 0; + while (sr > 0; sr -= 1) { + // r:q = ((r:q) << 1) | carry + r = (r << 1) | (q >> (n_uword_bits - 1)); + q = (q << 1) | carry; + // carry = 0; + // if (r.all >= d.all) + // { + // r.all -= d.all; + // carry = 1; + // } + const s = si_int(d - r - 1) >> si_int(n_uword_bits - 1); + carry = su_int(s & 1); + r -= d & su_int(s); + } + q = (q << 1) | carry; + return q; +} + fn test_umoddi3() { @setFnTest(this); @@ -257,6 +367,155 @@ fn test_one_udivmoddi4(a: du_int, b: du_int, expected_q: du_int, expected_r: du_ assert(r == expected_r); } -fn assert(b: bool) { - if (!b) @unreachable(); +fn test_udivsi3() { + @setFnTest(this); + + const cases = [][3]su_int { + []su_int{0x00000000, 0x00000001, 0x00000000}, + []su_int{0x00000000, 0x00000002, 0x00000000}, + []su_int{0x00000000, 0x00000003, 0x00000000}, + []su_int{0x00000000, 0x00000010, 0x00000000}, + []su_int{0x00000000, 0x078644FA, 0x00000000}, + []su_int{0x00000000, 0x0747AE14, 0x00000000}, + []su_int{0x00000000, 0x7FFFFFFF, 0x00000000}, + []su_int{0x00000000, 0x80000000, 0x00000000}, + []su_int{0x00000000, 0xFFFFFFFD, 0x00000000}, + []su_int{0x00000000, 0xFFFFFFFE, 0x00000000}, + []su_int{0x00000000, 0xFFFFFFFF, 0x00000000}, + []su_int{0x00000001, 0x00000001, 0x00000001}, + []su_int{0x00000001, 0x00000002, 0x00000000}, + []su_int{0x00000001, 0x00000003, 0x00000000}, + []su_int{0x00000001, 0x00000010, 0x00000000}, + []su_int{0x00000001, 0x078644FA, 0x00000000}, + []su_int{0x00000001, 0x0747AE14, 0x00000000}, + []su_int{0x00000001, 0x7FFFFFFF, 0x00000000}, + []su_int{0x00000001, 0x80000000, 0x00000000}, + []su_int{0x00000001, 0xFFFFFFFD, 0x00000000}, + []su_int{0x00000001, 0xFFFFFFFE, 0x00000000}, + []su_int{0x00000001, 0xFFFFFFFF, 0x00000000}, + []su_int{0x00000002, 0x00000001, 0x00000002}, + []su_int{0x00000002, 0x00000002, 0x00000001}, + []su_int{0x00000002, 0x00000003, 0x00000000}, + []su_int{0x00000002, 0x00000010, 0x00000000}, + []su_int{0x00000002, 0x078644FA, 0x00000000}, + []su_int{0x00000002, 0x0747AE14, 0x00000000}, + []su_int{0x00000002, 0x7FFFFFFF, 0x00000000}, + []su_int{0x00000002, 0x80000000, 0x00000000}, + []su_int{0x00000002, 0xFFFFFFFD, 0x00000000}, + []su_int{0x00000002, 0xFFFFFFFE, 0x00000000}, + []su_int{0x00000002, 0xFFFFFFFF, 0x00000000}, + []su_int{0x00000003, 0x00000001, 0x00000003}, + []su_int{0x00000003, 0x00000002, 0x00000001}, + []su_int{0x00000003, 0x00000003, 0x00000001}, + []su_int{0x00000003, 0x00000010, 0x00000000}, + []su_int{0x00000003, 0x078644FA, 0x00000000}, + []su_int{0x00000003, 0x0747AE14, 0x00000000}, + []su_int{0x00000003, 0x7FFFFFFF, 0x00000000}, + []su_int{0x00000003, 0x80000000, 0x00000000}, + []su_int{0x00000003, 0xFFFFFFFD, 0x00000000}, + []su_int{0x00000003, 0xFFFFFFFE, 0x00000000}, + []su_int{0x00000003, 0xFFFFFFFF, 0x00000000}, + []su_int{0x00000010, 0x00000001, 0x00000010}, + []su_int{0x00000010, 0x00000002, 0x00000008}, + []su_int{0x00000010, 0x00000003, 0x00000005}, + []su_int{0x00000010, 0x00000010, 0x00000001}, + []su_int{0x00000010, 0x078644FA, 0x00000000}, + []su_int{0x00000010, 0x0747AE14, 0x00000000}, + []su_int{0x00000010, 0x7FFFFFFF, 0x00000000}, + []su_int{0x00000010, 0x80000000, 0x00000000}, + []su_int{0x00000010, 0xFFFFFFFD, 0x00000000}, + []su_int{0x00000010, 0xFFFFFFFE, 0x00000000}, + []su_int{0x00000010, 0xFFFFFFFF, 0x00000000}, + []su_int{0x078644FA, 0x00000001, 0x078644FA}, + []su_int{0x078644FA, 0x00000002, 0x03C3227D}, + []su_int{0x078644FA, 0x00000003, 0x028216FE}, + []su_int{0x078644FA, 0x00000010, 0x0078644F}, + []su_int{0x078644FA, 0x078644FA, 0x00000001}, + []su_int{0x078644FA, 0x0747AE14, 0x00000001}, + []su_int{0x078644FA, 0x7FFFFFFF, 0x00000000}, + []su_int{0x078644FA, 0x80000000, 0x00000000}, + []su_int{0x078644FA, 0xFFFFFFFD, 0x00000000}, + []su_int{0x078644FA, 0xFFFFFFFE, 0x00000000}, + []su_int{0x078644FA, 0xFFFFFFFF, 0x00000000}, + []su_int{0x0747AE14, 0x00000001, 0x0747AE14}, + []su_int{0x0747AE14, 0x00000002, 0x03A3D70A}, + []su_int{0x0747AE14, 0x00000003, 0x026D3A06}, + []su_int{0x0747AE14, 0x00000010, 0x00747AE1}, + []su_int{0x0747AE14, 0x078644FA, 0x00000000}, + []su_int{0x0747AE14, 0x0747AE14, 0x00000001}, + []su_int{0x0747AE14, 0x7FFFFFFF, 0x00000000}, + []su_int{0x0747AE14, 0x80000000, 0x00000000}, + []su_int{0x0747AE14, 0xFFFFFFFD, 0x00000000}, + []su_int{0x0747AE14, 0xFFFFFFFE, 0x00000000}, + []su_int{0x0747AE14, 0xFFFFFFFF, 0x00000000}, + []su_int{0x7FFFFFFF, 0x00000001, 0x7FFFFFFF}, + []su_int{0x7FFFFFFF, 0x00000002, 0x3FFFFFFF}, + []su_int{0x7FFFFFFF, 0x00000003, 0x2AAAAAAA}, + []su_int{0x7FFFFFFF, 0x00000010, 0x07FFFFFF}, + []su_int{0x7FFFFFFF, 0x078644FA, 0x00000011}, + []su_int{0x7FFFFFFF, 0x0747AE14, 0x00000011}, + []su_int{0x7FFFFFFF, 0x7FFFFFFF, 0x00000001}, + []su_int{0x7FFFFFFF, 0x80000000, 0x00000000}, + []su_int{0x7FFFFFFF, 0xFFFFFFFD, 0x00000000}, + []su_int{0x7FFFFFFF, 0xFFFFFFFE, 0x00000000}, + []su_int{0x7FFFFFFF, 0xFFFFFFFF, 0x00000000}, + []su_int{0x80000000, 0x00000001, 0x80000000}, + []su_int{0x80000000, 0x00000002, 0x40000000}, + []su_int{0x80000000, 0x00000003, 0x2AAAAAAA}, + []su_int{0x80000000, 0x00000010, 0x08000000}, + []su_int{0x80000000, 0x078644FA, 0x00000011}, + []su_int{0x80000000, 0x0747AE14, 0x00000011}, + []su_int{0x80000000, 0x7FFFFFFF, 0x00000001}, + []su_int{0x80000000, 0x80000000, 0x00000001}, + []su_int{0x80000000, 0xFFFFFFFD, 0x00000000}, + []su_int{0x80000000, 0xFFFFFFFE, 0x00000000}, + []su_int{0x80000000, 0xFFFFFFFF, 0x00000000}, + []su_int{0xFFFFFFFD, 0x00000001, 0xFFFFFFFD}, + []su_int{0xFFFFFFFD, 0x00000002, 0x7FFFFFFE}, + []su_int{0xFFFFFFFD, 0x00000003, 0x55555554}, + []su_int{0xFFFFFFFD, 0x00000010, 0x0FFFFFFF}, + []su_int{0xFFFFFFFD, 0x078644FA, 0x00000022}, + []su_int{0xFFFFFFFD, 0x0747AE14, 0x00000023}, + []su_int{0xFFFFFFFD, 0x7FFFFFFF, 0x00000001}, + []su_int{0xFFFFFFFD, 0x80000000, 0x00000001}, + []su_int{0xFFFFFFFD, 0xFFFFFFFD, 0x00000001}, + []su_int{0xFFFFFFFD, 0xFFFFFFFE, 0x00000000}, + []su_int{0xFFFFFFFD, 0xFFFFFFFF, 0x00000000}, + []su_int{0xFFFFFFFE, 0x00000001, 0xFFFFFFFE}, + []su_int{0xFFFFFFFE, 0x00000002, 0x7FFFFFFF}, + []su_int{0xFFFFFFFE, 0x00000003, 0x55555554}, + []su_int{0xFFFFFFFE, 0x00000010, 0x0FFFFFFF}, + []su_int{0xFFFFFFFE, 0x078644FA, 0x00000022}, + []su_int{0xFFFFFFFE, 0x0747AE14, 0x00000023}, + []su_int{0xFFFFFFFE, 0x7FFFFFFF, 0x00000002}, + []su_int{0xFFFFFFFE, 0x80000000, 0x00000001}, + []su_int{0xFFFFFFFE, 0xFFFFFFFD, 0x00000001}, + []su_int{0xFFFFFFFE, 0xFFFFFFFE, 0x00000001}, + []su_int{0xFFFFFFFE, 0xFFFFFFFF, 0x00000000}, + []su_int{0xFFFFFFFF, 0x00000001, 0xFFFFFFFF}, + []su_int{0xFFFFFFFF, 0x00000002, 0x7FFFFFFF}, + []su_int{0xFFFFFFFF, 0x00000003, 0x55555555}, + []su_int{0xFFFFFFFF, 0x00000010, 0x0FFFFFFF}, + []su_int{0xFFFFFFFF, 0x078644FA, 0x00000022}, + []su_int{0xFFFFFFFF, 0x0747AE14, 0x00000023}, + []su_int{0xFFFFFFFF, 0x7FFFFFFF, 0x00000002}, + []su_int{0xFFFFFFFF, 0x80000000, 0x00000001}, + []su_int{0xFFFFFFFF, 0xFFFFFFFD, 0x00000001}, + []su_int{0xFFFFFFFF, 0xFFFFFFFE, 0x00000001}, + []su_int{0xFFFFFFFF, 0xFFFFFFFF, 0x00000001}, + }; + + for (cases) |case| { + test_one_udivsi3(case[0], case[1], case[2]); + } +} + +fn test_one_udivsi3(a: su_int, b: su_int, expected_q: su_int) { + const q: su_int = __udivsi3(a, b); + assert(q == expected_q); +} + + +fn assert(ok: bool) { + if (!ok) @unreachable(); } diff --git a/std/debug.zig b/std/debug.zig index 435d572ac2..f3278a0e45 100644 --- a/std/debug.zig +++ b/std/debug.zig @@ -13,6 +13,27 @@ pub fn assert(ok: bool) { if (!ok) @unreachable() } +var panicking = false; +/// This is the default panic implementation. +pub coldcc fn panic(message: []const u8) -> unreachable { + // TODO + // if (@atomicRmw(AtomicOp.XChg, &panicking, true, AtomicOrder.SeqCst)) { } + if (panicking) { + // Panicked during a panic. + // TODO detect if a different thread caused the panic, because in that case + // we would want to return here instead of calling abort, so that the thread + // which first called panic can finish printing a stack trace. + os.abort(); + } else { + panicking = true; + } + + %%io.stderr.printf("{}\n", message); + %%printStackTrace(); + + os.abort(); +} + pub fn printStackTrace() -> %void { %return writeStackTrace(&io.stderr); %return io.stderr.flush(); diff --git a/std/io.zig b/std/io.zig index f726c30cbc..291563cbb4 100644 --- a/std/io.zig +++ b/std/io.zig @@ -100,14 +100,20 @@ pub const OutStream = struct { Start, OpenBrace, CloseBrace, - Hex: bool, + Integer, + IntegerWidth, }; /// Calls print and then flushes the buffer. pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) -> %void { - comptime var start_index: usize = 0; + comptime var start_index = 0; comptime var state = State.Start; - comptime var next_arg: usize = 0; + comptime var next_arg = 0; + comptime var radix = 0; + comptime var uppercase = false; + comptime var width = 0; + comptime var width_start = 0; + inline for (format) |c, i| { switch (state) { State.Start => switch (c) { @@ -132,11 +138,23 @@ pub const OutStream = struct { state = State.Start; start_index = i + 1; }, + 'd' => { + radix = 10; + uppercase = false; + width = 0; + state = State.Integer; + }, 'x' => { - state = State.Hex { false }; + radix = 16; + uppercase = false; + width = 0; + state = State.Integer; }, 'X' => { - state = State.Hex { true }; + radix = 16; + uppercase = true; + width = 0; + state = State.Integer; }, else => @compileError("Unknown format character: " ++ c), }, @@ -147,14 +165,29 @@ pub const OutStream = struct { }, else => @compileError("Single '}' encountered in format string"), }, - State.Hex => |uppercase| switch (c) { + State.Integer => switch (c) { + '}' => { + self.printInt(args[next_arg], radix, uppercase, width); + next_arg += 1; + state = State.Start; + start_index = i + 1; + }, + '0' ... '9' => { + width_start = i; + state = State.IntegerWidth; + }, + else => @compileError("Unexpected character in format string: " ++ []u8{c}), + }, + State.IntegerWidth => switch (c) { '}' => { - self.printInt(args[next_arg], 16, uppercase); + width = comptime %%parseUnsigned(usize, format[width_start...i], 10); + self.printInt(args[next_arg], radix, uppercase, width); next_arg += 1; state = State.Start; start_index = i + 1; }, - else => @compileError("Expected '}' after 'x'/'X' in format string"), + '0' ... '9' => {}, + else => @compileError("Unexpected character in format string: " ++ []u8{c}), }, } } @@ -162,10 +195,8 @@ pub const OutStream = struct { if (args.len != next_arg) { @compileError("Unused arguments"); } - // TODO https://github.com/andrewrk/zig/issues/253 - switch (state) { - State.Start => {}, - else => @compileError("Incomplete format string: " ++ format), + if (state != State.Start) { + @compileError("Incomplete format string: " ++ format); } } if (start_index < format.len) { @@ -177,7 +208,7 @@ pub const OutStream = struct { pub fn printValue(self: &OutStream, value: var) -> %void { const T = @typeOf(value); if (@isInteger(T)) { - return self.printInt(value, 10, false); + return self.printInt(value, 10, false, 0); } else if (@isFloat(T)) { return self.printFloat(T, value); } else if (@canImplicitCast([]const u8, value)) { @@ -190,11 +221,11 @@ pub const OutStream = struct { } } - pub fn printInt(self: &OutStream, x: var, base: u8, uppercase: bool) -> %void { + pub fn printInt(self: &OutStream, x: var, base: u8, uppercase: bool, width: usize) -> %void { if (self.index + max_int_digits >= self.buffer.len) { %return self.flush(); } - const amt_printed = bufPrintInt(self.buffer[self.index...], x, base, uppercase); + const amt_printed = bufPrintInt(self.buffer[self.index...], x, base, uppercase, width); self.index += amt_printed; } @@ -474,24 +505,29 @@ fn digitToChar(digit: u8, uppercase: bool) -> u8 { } /// Guaranteed to not use more than max_int_digits -pub fn bufPrintInt(out_buf: []u8, x: var, base: u8, uppercase: bool) -> usize { +pub fn bufPrintInt(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize { if (@typeOf(x).is_signed) - bufPrintSigned(out_buf, x, base, uppercase) + bufPrintSigned(out_buf, x, base, uppercase, width) else - bufPrintUnsigned(out_buf, x, base, uppercase) + bufPrintUnsigned(out_buf, x, base, uppercase, width) } -fn bufPrintSigned(out_buf: []u8, x: var, base: u8, uppercase: bool) -> usize { +fn bufPrintSigned(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize { const uint = @intType(false, @typeOf(x).bit_count); + // include the sign in the width + const new_width = if (width == 0) 0 else (width - 1); + var new_value: uint = undefined; if (x < 0) { out_buf[0] = '-'; - return 1 + bufPrintUnsigned(out_buf[1...], uint(-(x + 1)) + 1, base, uppercase); + new_value = uint(-(x + 1)) + 1; } else { - return bufPrintUnsigned(out_buf, uint(x), base, uppercase); + out_buf[0] = '+'; + new_value = uint(x); } + return 1 + bufPrintUnsigned(out_buf[1...], new_value, base, uppercase, new_width); } -fn bufPrintUnsigned(out_buf: []u8, x: var, base: u8, uppercase: bool) -> usize { +fn bufPrintUnsigned(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize { // max_int_digits accounts for the minus sign. when printing an unsigned // number we don't need to do that. var buf: [max_int_digits - 1]u8 = undefined; @@ -508,18 +544,11 @@ fn bufPrintUnsigned(out_buf: []u8, x: var, base: u8, uppercase: bool) -> usize { } const src_buf = buf[index...]; - mem.copy(u8, out_buf, src_buf); - return src_buf.len; -} - -fn parseU64DigitTooBig() { - @setFnTest(this); + const padding = if (width > src_buf.len) (width - src_buf.len) else 0; - parseUnsigned(u64, "123a", 10) %% |err| { - if (err == error.InvalidChar) return; - @unreachable(); - }; - @unreachable(); + mem.set(u8, out_buf[0...padding], '0'); + mem.copy(u8, out_buf[padding...], src_buf); + return src_buf.len + padding; } pub fn openSelfExe(stream: &InStream) -> %void { @@ -535,18 +564,43 @@ pub fn openSelfExe(stream: &InStream) -> %void { } } -fn bufPrintIntToSlice(buf: []u8, x: var, base: u8, uppercase: bool) -> []u8 { - return buf[0...bufPrintInt(buf, x, base, uppercase)]; +fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: usize) -> []u8 { + return buf[0...bufPrintInt(buf, value, base, uppercase, width)]; +} + +fn testParseU64DigitTooBig() { + @setFnTest(this); + + parseUnsigned(u64, "123a", 10) %% |err| { + if (err == error.InvalidChar) return; + @unreachable(); + }; + @unreachable(); +} + +fn testParseUnsignedComptime() { + @setFnTest(this); + + comptime { + assert(%%parseUnsigned(usize, "2", 10) == 2); + } } fn testBufPrintInt() { @setFnTest(this); var buf: [max_int_digits]u8 = undefined; - assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 2, false), "-101111000110000101001110")); - assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 10, false), "-12345678")); - assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 16, false), "-bc614e")); - assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 16, true), "-BC614E")); + assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110")); + assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678")); + assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e")); + assert(mem.eql(bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E")); + + assert(mem.eql(bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678")); + + assert(mem.eql(bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666")); + assert(mem.eql(bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234")); + assert(mem.eql(bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234")); - assert(mem.eql(bufPrintIntToSlice(buf, u32(12345678), 10, true), "12345678")); + assert(mem.eql(bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42")); + assert(mem.eql(bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42")); } diff --git a/std/math.zig b/std/math.zig index d8e4951fa9..49396ca25d 100644 --- a/std/math.zig +++ b/std/math.zig @@ -1,3 +1,5 @@ +const assert = @import("debug.zig").assert; + pub const Cmp = enum { Equal, Greater, @@ -66,3 +68,18 @@ fn getReturnTypeForAbs(comptime T: type) -> type { } } +fn testMath() { + @setFnTest(this); + + assert(%%mulOverflow(i32, 3, 4) == 12); + assert(%%addOverflow(i32, 3, 4) == 7); + assert(%%subOverflow(i32, 3, 4) == -1); + assert(%%shlOverflow(i32, 0b11, 4) == 0b110000); + + comptime { + assert(%%mulOverflow(i32, 3, 4) == 12); + assert(%%addOverflow(i32, 3, 4) == 7); + assert(%%subOverflow(i32, 3, 4) == -1); + assert(%%shlOverflow(i32, 0b11, 4) == 0b110000); + } +} diff --git a/std/panic.zig b/std/panic.zig index 593930a4af..5be47b0c6e 100644 --- a/std/panic.zig +++ b/std/panic.zig @@ -3,31 +3,10 @@ // If this file wants to import other files *by name*, support for that would // have to be added in the compiler. -var panicking = false; pub coldcc fn panic(message: []const u8) -> unreachable { if (@compileVar("os") == Os.freestanding) { while (true) {} } else { - const std = @import("std"); - const io = std.io; - const debug = std.debug; - const os = std.os; - - // TODO - // if (@atomicRmw(AtomicOp.XChg, &panicking, true, AtomicOrder.SeqCst)) { - if (panicking) { - // Panicked during a panic. - // TODO detect if a different thread caused the panic, because in that case - // we would want to return here instead of calling abort, so that the thread - // which first called panic can finish printing a stack trace. - os.abort(); - } else { - panicking = true; - } - - %%io.stderr.printf("{}\n", message); - %%debug.printStackTrace(); - - os.abort(); + @import("std").debug.panic(message); } } diff --git a/test/cases/enum_with_members.zig b/test/cases/enum_with_members.zig index b338b9b014..5abb297674 100644 --- a/test/cases/enum_with_members.zig +++ b/test/cases/enum_with_members.zig @@ -8,8 +8,8 @@ const ET = enum { pub fn print(a: &const ET, buf: []u8) -> %usize { return switch (*a) { - ET.SINT => |x| { io.bufPrintInt(buf, x, 10, false) }, - ET.UINT => |x| { io.bufPrintInt(buf, x, 10, false) }, + ET.SINT => |x| { io.bufPrintInt(buf, x, 10, false, 0) }, + ET.UINT => |x| { io.bufPrintInt(buf, x, 10, false, 0) }, } } }; diff --git a/test/cases/eval.zig b/test/cases/eval.zig index 9f3b1d2fcd..027bb643b6 100644 --- a/test/cases/eval.zig +++ b/test/cases/eval.zig @@ -251,3 +251,15 @@ fn comptimeIterateOverFnPtrList() { assert(performFn('o', 0) == 1); assert(performFn('w', 99) == 99); } + +fn evalSetDebugSafetyAtCompileTime() { + @setFnTest(this); + + const result = comptime fnWithSetDebugSafety(); + assert(result == 1234); +} + +fn fnWithSetDebugSafety() -> i32{ + @setDebugSafety(this, true); + return 1234; +} diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 01d16a2543..828f3469bf 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -1015,8 +1015,9 @@ const x = foo(); add_compile_fail_case("array concatenation with wrong type", R"SOURCE( const src = "aoeu"; -const a = src[0...] ++ "foo"; - )SOURCE", 1, ".tmp_source.zig:3:14: error: expected array or C string literal, found '[]u8'"); +const derp = usize(1234); +const a = derp ++ "foo"; + )SOURCE", 1, ".tmp_source.zig:4:11: error: expected array or C string literal, found 'usize'"); add_compile_fail_case("non compile time array concatenation", R"SOURCE( fn f(s: [10]u8) -> []u8 { @@ -1632,6 +1633,23 @@ const some_data: [100]u8 = { }; )SOURCE", 1, ".tmp_source.zig:3:32: error: alignment value must be power of 2"); + add_compile_fail_case("compile log", R"SOURCE( +fn foo() { + comptime bar(12, "hi"); +} +fn bar(a: i32, b: []const u8) { + @compileLog("begin"); + @compileLog("a", a, "b", b); + @compileLog("end"); +} + )SOURCE", 6, + ".tmp_source.zig:6:5: error: found compile log statement", + ".tmp_source.zig:3:17: note: called from here", + ".tmp_source.zig:7:5: error: found compile log statement", + ".tmp_source.zig:3:17: note: called from here", + ".tmp_source.zig:8:5: error: found compile log statement", + ".tmp_source.zig:3:17: note: called from here"); + } ////////////////////////////////////////////////////////////////////////////// -- cgit v1.2.3