Commit fc100d7b3b

Andrew Kelley <superjoe30@gmail.com>
2017-02-09 08:50:03
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
1 parent 8a859af
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
 
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;
 
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)
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);
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<IrInstructionCompileLog>(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<IrInstructionErrName>(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<IrInstruction*>(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:
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;
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();
+}
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();
 }
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();
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"));
 }
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);
+    }
+}
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);
     }
 }
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) },
         }
     }
 };
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;
+}
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");
+
 }
 
 //////////////////////////////////////////////////////////////////////////////