Commit bcb18338cd

Andrew Kelley <superjoe30@gmail.com>
2016-01-25 06:53:00
update std lib to use error type and global variables
1 parent 29a83f6
Changed files (10)
example
guess_number
hello_world
multiple_files
shared_library
src
std
test
example/guess_number/main.zig
@@ -3,45 +3,33 @@ export executable "guess_number";
 import "std.zig";
 import "rand.zig";
 
-error GetRandomFail;
-error ReadInputFail;
-
 pub fn main(args: [][]u8) %void => {
-    print_str("Welcome to the Guess Number Game in Zig.\n");
+    stderr.print_str("Welcome to the Guess Number Game in Zig.\n");
 
     var seed : u32;
-    const seed_bytes = (&u8)(&seed)[0...@sizeof(u32)];
-    const err = os_get_random_bytes(seed_bytes);
-    if (err != @sizeof(u32)) {
-        // TODO full error message
-        fprint_str(stderr_fileno, "unable to get random bytes\n");
-        return error.GetRandomFail;
-    }
+    const seed_bytes = (&u8)(&seed)[0...4];
+    os_get_random_bytes(seed_bytes);
 
     var rand = rand_new(seed);
 
     const answer = rand.range_u64(0, 100) + 1;
 
     while (true) {
-        print_str("\nGuess a number between 1 and 100: ");
+        stderr.print_str("\nGuess a number between 1 and 100: ");
         var line_buf : [20]u8;
-        var line_len : isize;
-        // TODO fix this awkward error handling
-        if (readline(line_buf, &line_len) || line_len == line_buf.len) {
-            // TODO full error message
-            fprint_str(stderr_fileno, "unable to read input\n");
-            return error.ReadInputFail;
-        }
+
+        // TODO print error message instead of returning
+        const line_len = %return stdin.readline(line_buf);
 
         var guess : u64;
         if (parse_u64(line_buf[0...line_len - 1], 10, &guess)) {
-            print_str("Invalid number format.\n");
+            stderr.print_str("Invalid number format.\n");
         } else if (guess > answer) {
-            print_str("Guess lower.\n");
+            stderr.print_str("Guess lower.\n");
         } else if (guess < answer) {
-            print_str("Guess higher.\n");
+            stderr.print_str("Guess higher.\n");
         } else {
-            print_str("You win!\n");
+            stderr.print_str("You win!\n");
             return;
         }
     }
example/hello_world/hello.zig
@@ -3,6 +3,5 @@ export executable "hello";
 import "std.zig";
 
 pub fn main(args: [][]u8) %void => {
-    //stderr.print_str("Hello, world!\n");
-    print_str("Hello, world!\n");
+    stdout.printf("Hello, world!\n");
 }
example/multiple_files/foo.zig
@@ -3,7 +3,7 @@ import "std.zig";
 // purposefully conflicting function with main.zig
 // but it's private so it should be OK
 fn private_function() => {
-    print_str("OK 1\n");
+    stdout.printf("OK 1\n");
 }
 
 pub fn print_text() => {
example/multiple_files/main.zig
@@ -5,7 +5,7 @@ import "foo.zig";
 
 pub fn main(args: [][]u8) i32 => {
     private_function();
-    print_str("OK 2\n");
+    stdout.printf("OK 2\n");
     return 0;
 }
 
example/shared_library/mathtest.zig
@@ -1,10 +1,10 @@
 #version("2.0.0")
 export library "mathtest";
 
-export fn add(a: i32, b: i32) -> i32 {
+export fn add(a: i32, b: i32) i32 => {
     a + b
 }
 
-export fn hang() -> unreachable {
-entry: goto entry;
+export fn hang() unreachable => {
+    while (true) { }
 }
src/analyze.cpp
@@ -3058,6 +3058,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
     if (wanted_type->id == TypeTableEntryIdMaybe) {
         if (types_match_const_cast_only(wanted_type->data.maybe.child_type, actual_type)) {
             node->data.fn_call_expr.cast_op = CastOpMaybeWrap;
+            context->cast_alloca_list.append(node);
             eval_const_expr_implicit_cast(g, node, expr_node);
             return wanted_type;
         } else if (actual_type->id == TypeTableEntryIdNumLitInt ||
@@ -3065,6 +3066,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
         {
             if (num_lit_fits_in_other_type(g, expr_node, wanted_type->data.maybe.child_type)) {
                 node->data.fn_call_expr.cast_op = CastOpMaybeWrap;
+                context->cast_alloca_list.append(node);
                 eval_const_expr_implicit_cast(g, node, expr_node);
                 return wanted_type;
             } else {
@@ -3077,6 +3079,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
     if (wanted_type->id == TypeTableEntryIdErrorUnion) {
         if (types_match_const_cast_only(wanted_type->data.error.child_type, actual_type)) {
             node->data.fn_call_expr.cast_op = CastOpErrorWrap;
+            context->cast_alloca_list.append(node);
             eval_const_expr_implicit_cast(g, node, expr_node);
             return wanted_type;
         } else if (actual_type->id == TypeTableEntryIdNumLitInt ||
@@ -3084,6 +3087,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
         {
             if (num_lit_fits_in_other_type(g, expr_node, wanted_type->data.error.child_type)) {
                 node->data.fn_call_expr.cast_op = CastOpErrorWrap;
+                context->cast_alloca_list.append(node);
                 eval_const_expr_implicit_cast(g, node, expr_node);
                 return wanted_type;
             } else {
src/codegen.cpp
@@ -325,11 +325,28 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) {
                 return cast_expr->tmp_ptr;
             }
         case CastOpErrorWrap:
-            assert(wanted_type->id == TypeTableEntryIdErrorUnion);
-            if (wanted_type->data.error.child_type->size_in_bits == 0) {
-                return LLVMConstNull(g->err_tag_type->type_ref);
-            } else {
-                zig_panic("TODO");
+            {
+                assert(wanted_type->id == TypeTableEntryIdErrorUnion);
+                TypeTableEntry *child_type = wanted_type->data.error.child_type;
+                LLVMValueRef ok_err_val = LLVMConstNull(g->err_tag_type->type_ref);
+
+                if (child_type->size_in_bits == 0) {
+                    return ok_err_val;
+                } else {
+                    assert(cast_expr->tmp_ptr);
+                    assert(wanted_type->id == TypeTableEntryIdErrorUnion);
+                    assert(actual_type);
+
+                    add_debug_source_node(g, node);
+                    LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 0, "");
+                    LLVMBuildStore(g->builder, ok_err_val, err_tag_ptr);
+
+                    LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, cast_expr->tmp_ptr, 1, "");
+                    gen_assign_raw(g, node, BinOpTypeAssign,
+                            payload_ptr, expr_val, child_type, actual_type);
+
+                    return cast_expr->tmp_ptr;
+                }
             }
         case CastOpPureErrorWrap:
             assert(wanted_type->id == TypeTableEntryIdErrorUnion);
@@ -1286,12 +1303,16 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) {
                 if (return_type->id == TypeTableEntryIdPureError) {
                     gen_return(g, node, err_val);
                 } else if (return_type->id == TypeTableEntryIdErrorUnion) {
-                    assert(g->cur_ret_ptr);
+                    if (return_type->data.error.child_type->size_in_bits > 0) {
+                        assert(g->cur_ret_ptr);
 
-                    add_debug_source_node(g, node);
-                    LLVMValueRef tag_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, 0, "");
-                    LLVMBuildStore(g->builder, err_val, tag_ptr);
-                    LLVMBuildRetVoid(g->builder);
+                        add_debug_source_node(g, node);
+                        LLVMValueRef tag_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, 0, "");
+                        LLVMBuildStore(g->builder, err_val, tag_ptr);
+                        LLVMBuildRetVoid(g->builder);
+                    } else {
+                        gen_return(g, node, err_val);
+                    }
                 } else {
                     zig_unreachable();
                 }
@@ -2011,7 +2032,7 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
     LLVMPositionBuilderAtEnd(g->builder, end_block);
 
     add_debug_source_node(g, node);
-    LLVMValueRef phi = LLVMBuildPhi(g->builder, get_expr_type(node)->type_ref, "");
+    LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(incoming_values.at(0)), "");
     LLVMAddIncoming(phi, incoming_values.items, incoming_blocks.items, incoming_values.length);
 
     return phi;
std/std.zig
@@ -5,7 +5,6 @@ pub const stdin_fileno = 0;
 pub const stdout_fileno = 1;
 pub const stderr_fileno = 2;
 
-/*
 pub var stdin = InStream {
     .fd = stdin_fileno,
 };
@@ -23,9 +22,16 @@ pub var stderr = OutStream {
     .index = 0,
     .buffered = false,
 };
-*/
 
+/// The function received invalid input at runtime. An Invalid error means a
+/// bug in the program that called the function.
+pub error Invalid;
+
+/// When an Unexpected error occurs, code that emitted the error likely needs
+/// a patch to recognize the unexpected case so that it can handle it and emit
+/// a more specific error.
 pub error Unexpected;
+
 pub error DiskQuota;
 pub error FileTooBig;
 pub error SigInterrupt;
@@ -33,26 +39,25 @@ pub error Io;
 pub error NoSpaceLeft;
 pub error BadPerm;
 pub error PipeFail;
-pub error Invalid;
+pub error BadFd;
 
 const buffer_size = 4 * 1024;
 const max_u64_base10_digits = 20;
 
-/*
 pub struct OutStream {
     fd: isize,
     buffer: [buffer_size]u8,
-    index: @typeof(buffer_size),
+    index: isize,
     buffered: bool,
 
     pub fn print_str(os: &OutStream, str: []const u8) %isize => {
         var src_bytes_left = str.len;
         var src_index: @typeof(str.len) = 0;
-        const dest_space_left = os.buffer.len - index;
+        const dest_space_left = os.buffer.len - os.index;
 
         while (src_bytes_left > 0) {
             const copy_amt = min_isize(dest_space_left, src_bytes_left);
-            @memcpy(&buffer[os.index], &str[src_index], copy_amt);
+            @memcpy(&os.buffer[os.index], &str[src_index], copy_amt);
             os.index += copy_amt;
             if (os.index == os.buffer.len) {
                 %return os.flush();
@@ -65,11 +70,19 @@ pub struct OutStream {
         return str.len;
     }
 
+    /// Prints a byte buffer, flushes the buffer, then returns the number of
+    /// bytes printed. The "f" is for "flush".
+    pub fn printf(os: &OutStream, str: []const u8) %isize => {
+        const byte_count = %return os.print_str(str);
+        %return os.flush();
+        return byte_count;
+    }
+
     pub fn print_u64(os: &OutStream, x: u64) %isize => {
         if (os.index + max_u64_base10_digits >= os.buffer.len) {
             %return os.flush();
         }
-        const amt_printed = buf_print_u64(buf[os.index...], x);
+        const amt_printed = buf_print_u64(os.buffer[os.index...], x);
         os.index += amt_printed;
 
         if (!os.buffered) {
@@ -84,7 +97,7 @@ pub struct OutStream {
         if (os.index + max_u64_base10_digits >= os.buffer.len) {
             %return os.flush();
         }
-        const amt_printed = buf_print_i64(buf[os.index...], x);
+        const amt_printed = buf_print_i64(os.buffer[os.index...], x);
         os.index += amt_printed;
 
         if (!os.buffered) {
@@ -96,18 +109,20 @@ pub struct OutStream {
 
 
     pub fn flush(os: &OutStream) %void => {
-        const amt_to_write = os.index;
+        const amt_written = write(os.fd, os.buffer.ptr, os.index);
         os.index = 0;
-        switch (write(os.fd, os.buffer.ptr, amt_to_write)) {
-            EINVAL => unreachable{},
-            EDQUOT => error.DiskQuota,
-            EFBIG  => error.FileTooBig,
-            EINTR  => error.SigInterrupt,
-            EIO    => error.Io,
-            ENOSPC => error.NoSpaceLeft,
-            EPERM  => error.BadPerm,
-            EPIPE  => error.PipeFail,
-            else   => error.Unexpected,
+        if (amt_written < 0) {
+            return switch (-amt_written) {
+                EINVAL => unreachable{},
+                EDQUOT => error.DiskQuota,
+                EFBIG  => error.FileTooBig,
+                EINTR  => error.SigInterrupt,
+                EIO    => error.Io,
+                ENOSPC => error.NoSpaceLeft,
+                EPERM  => error.BadPerm,
+                EPIPE  => error.PipeFail,
+                else   => error.Unexpected,
+            }
         }
     }
 }
@@ -115,10 +130,10 @@ pub struct OutStream {
 pub struct InStream {
     fd: isize,
 
-    pub fn readline(buf: []u8) %isize => {
-        const amt_read = read(stdin_fileno, buf.ptr, buf.len);
+    pub fn readline(is: &InStream, buf: []u8) %isize => {
+        const amt_read = read(is.fd, buf.ptr, buf.len);
         if (amt_read < 0) {
-            switch (-amt_read) {
+            return switch (-amt_read) {
                 EINVAL => unreachable{},
                 EFAULT => unreachable{},
                 EBADF  => error.BadFd,
@@ -133,53 +148,15 @@ pub struct InStream {
 }
 
 pub fn os_get_random_bytes(buf: []u8) %void => {
-    switch (getrandom(buf.ptr, buf.len, 0)) {
-        EINVAL => unreachable{},
-        EFAULT => unreachable{},
-        EINTR  => error.SigInterrupt,
-        else   => error.Unexpected,
-    }
-}
-*/
-
-
-// TODO remove this
-pub fn print_str(str: []const u8) isize => {
-    fprint_str(stdout_fileno, str)
-}
-
-// TODO remove this
-pub fn fprint_str(fd: isize, str: []const u8) isize => {
-    write(fd, str.ptr, str.len)
-}
-
-// TODO remove this
-pub fn os_get_random_bytes(buf: []u8) isize => {
-    getrandom(buf.ptr, buf.len, 0)
-}
-
-// TODO remove this
-pub fn print_u64(x: u64) isize => {
-    var buf: [max_u64_base10_digits]u8;
-    const len = buf_print_u64(buf, x);
-    return write(stdout_fileno, buf.ptr, len);
-}
-
-// TODO remove this
-pub fn print_i64(x: i64) isize => {
-    var buf: [max_u64_base10_digits]u8;
-    const len = buf_print_i64(buf, x);
-    return write(stdout_fileno, buf.ptr, len);
-}
-
-// TODO remove this
-pub fn readline(buf: []u8, out_len: &isize) bool => {
-    const amt_read = read(stdin_fileno, buf.ptr, buf.len);
-    if (amt_read < 0) {
-        return true;
+    const amt_got = getrandom(buf.ptr, buf.len, 0);
+    if (amt_got < 0) {
+        return switch (-amt_got) {
+            EINVAL => unreachable{},
+            EFAULT => unreachable{},
+            EINTR  => error.SigInterrupt,
+            else   => error.Unexpected,
+        }
     }
-    *out_len = isize(amt_read);
-    return false;
 }
 
 
@@ -251,3 +228,7 @@ fn buf_print_u64(out_buf: []u8, x: u64) isize => {
 
     return len;
 }
+
+fn min_isize(x: isize, y: isize) isize => {
+    if (x < y) x else y
+}
std/syscall.zig
@@ -1,7 +1,7 @@
-const SYS_read : isize = 0;
-const SYS_write : isize = 1;
-const SYS_exit : isize = 60;
-const SYS_getrandom : isize = 318;
+const SYS_read      = 0;
+const SYS_write     = 1;
+const SYS_exit      = 60;
+const SYS_getrandom = 318;
 
 fn syscall1(number: isize, arg1: isize) isize => {
     asm volatile ("syscall"
@@ -18,11 +18,11 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) isize => {
 }
 
 pub fn read(fd: isize, buf: &u8, count: isize) isize => {
-    isize(syscall3(SYS_read, isize(fd), isize(buf), count))
+    syscall3(SYS_read, isize(fd), isize(buf), count)
 }
 
 pub fn write(fd: isize, buf: &const u8, count: isize) isize => {
-    isize(syscall3(SYS_write, isize(fd), isize(buf), count))
+    syscall3(SYS_write, isize(fd), isize(buf), count)
 }
 
 pub fn exit(status: i32) unreachable => {
@@ -31,5 +31,5 @@ pub fn exit(status: i32) unreachable => {
 }
 
 pub fn getrandom(buf: &u8, count: isize, flags: u32) isize => {
-    isize(syscall3(SYS_getrandom, isize(buf), count, isize(flags)))
+    syscall3(SYS_getrandom, isize(buf), count, isize(flags))
 }
test/run_tests.cpp
@@ -121,7 +121,7 @@ pub fn main(args: [][]u8) %void => {
 }
 
 fn this_is_a_function() unreachable => {
-    print_str("OK\n");
+    stdout.printf("OK\n");
     exit(0);
 }
     )SOURCE", "OK\n");
@@ -137,7 +137,7 @@ fn another_function() => {}
 /// this is a documentation comment
 /// doc comment line 2
 pub fn main(args: [][]u8) %void => {
-    print_str(/* mid-line comment /* nested */ */ "OK\n");
+    stdout.printf(/* mid-line comment /* nested */ */ "OK\n");
 }
     )SOURCE", "OK\n");
 
@@ -148,7 +148,7 @@ import "foo.zig";
 
 pub fn main(args: [][]u8) %void => {
     private_function();
-    print_str("OK 2\n");
+    stdout.printf("OK 2\n");
 }
 
 fn private_function() => {
@@ -162,7 +162,7 @@ import "std.zig";
 // purposefully conflicting function with main.zig
 // but it's private so it should be OK
 fn private_function() => {
-    print_str("OK 1\n");
+    stdout.printf("OK 1\n");
 }
 
 pub fn print_text() => {
@@ -185,7 +185,7 @@ pub fn main(args: [][]u8) %void => {
         add_source_file(tc, "foo.zig", R"SOURCE(
 import "std.zig";
 pub fn foo_function() => {
-    print_str("OK\n");
+    stdout.printf("OK\n");
 }
         )SOURCE");
 
@@ -195,7 +195,7 @@ import "std.zig";
 
 pub fn bar_function() => {
     if (foo_function()) {
-        print_str("OK\n");
+        stdout.printf("OK\n");
     }
 }
         )SOURCE");
@@ -213,17 +213,17 @@ import "std.zig";
 
 pub fn main(args: [][]u8) %void => {
     if (1 != 0) {
-        print_str("1 is true\n");
+        stdout.printf("1 is true\n");
     } else {
-        print_str("1 is false\n");
+        stdout.printf("1 is false\n");
     }
     if (0 != 0) {
-        print_str("0 is true\n");
+        stdout.printf("0 is true\n");
     } else if (1 - 1 != 0) {
-        print_str("1 - 1 is true\n");
+        stdout.printf("1 - 1 is true\n");
     }
     if (!(0 != 0)) {
-        print_str("!0 is true\n");
+        stdout.printf("!0 is true\n");
     }
 }
     )SOURCE", "1 is true\n!0 is true\n");
@@ -237,7 +237,7 @@ fn add(a: i32, b: i32) i32 => {
 
 pub fn main(args: [][]u8) %void => {
     if (add(22, 11) == 33) {
-        print_str("pass\n");
+        stdout.printf("pass\n");
     }
 }
     )SOURCE", "pass\n");
@@ -249,7 +249,7 @@ fn loop(a : i32) => {
     if (a == 0) {
         goto done;
     }
-    print_str("loop\n");
+    stdout.printf("loop\n");
     loop(a - 1);
 
 done:
@@ -268,7 +268,7 @@ pub fn main(args: [][]u8) %void => {
     const a : i32 = 1;
     const b = i32(2);
     if (a + b == 3) {
-        print_str("OK\n");
+        stdout.printf("OK\n");
     }
 }
     )SOURCE", "OK\n");
@@ -277,10 +277,10 @@ pub fn main(args: [][]u8) %void => {
 import "std.zig";
 
 pub fn main(args: [][]u8) %void => {
-    if (true)   { print_str("OK 1\n"); }
-    if (false)  { print_str("BAD 1\n"); }
-    if (!true)  { print_str("BAD 2\n"); }
-    if (!false) { print_str("OK 2\n"); }
+    if (true)   { stdout.printf("OK 1\n"); }
+    if (false)  { stdout.printf("BAD 1\n"); }
+    if (!true)  { stdout.printf("BAD 2\n"); }
+    if (!false) { stdout.printf("OK 2\n"); }
 }
     )SOURCE", "OK 1\nOK 2\n");
 
@@ -290,14 +290,14 @@ import "std.zig";
 pub fn main(args: [][]u8) %void => {
     if (true) {
         const no_conflict : i32 = 5;
-        if (no_conflict == 5) { print_str("OK 1\n"); }
+        if (no_conflict == 5) { stdout.printf("OK 1\n"); }
     }
 
     const c = {
         const no_conflict = i32(10);
         no_conflict
     };
-    if (c == 10) { print_str("OK 2\n"); }
+    if (c == 10) { stdout.printf("OK 2\n"); }
 }
     )SOURCE", "OK 1\nOK 2\n");
 
@@ -311,7 +311,7 @@ pub fn main(args: [][]u8) %void => {
 fn void_fun(a : i32, b : void, c : i32) => {
     const v = b;
     const vv : void = if (a == 1) {v} else {};
-    if (a + c == 3) { print_str("OK\n"); }
+    if (a + c == 3) { stdout.printf("OK\n"); }
     return vv;
 }
     )SOURCE", "OK\n");
@@ -330,12 +330,12 @@ pub fn main(args: [][]u8) %void => {
         .c = void{},
     };
     if (foo.b != 1) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
     if (@sizeof(Foo) != 4) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
-    print_str("OK\n");
+    stdout.printf("OK\n");
 }
 
     )SOURCE", "OK\n");
@@ -348,12 +348,12 @@ pub fn main(args: [][]u8) %void => {
     array[0] = void{};
     array[1] = array[2];
     if (@sizeof(@typeof(array)) != 0) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
     if (array.len != 4) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
-    print_str("OK\n");
+    stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
@@ -363,11 +363,11 @@ import "std.zig";
 
 pub fn main(args: [][]u8) %void => {
     var zero : i32 = 0;
-    if (zero == 0) { print_str("zero\n"); }
+    if (zero == 0) { stdout.printf("zero\n"); }
 
     var i = i32(0);
     while (i != 3) {
-        print_str("loop\n");
+        stdout.printf("loop\n");
         i += 1;
     }
 }
@@ -394,11 +394,11 @@ pub fn main(args: [][]u8) %void => {
     }
 
     if (accumulator == 15) {
-        print_str("OK\n");
+        stdout.printf("OK\n");
     }
 
     if (get_array_len(array) != 5) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 }
 fn get_array_len(a: []i32) isize => {
@@ -411,7 +411,7 @@ fn get_array_len(a: []i32) isize => {
 import "std.zig";
 
 pub fn main(args: [][]u8) %void => {
-    print_str("Hello, world!\n");
+    stdout.printf("Hello, world!\n");
 }
     )SOURCE", "Hello, world!\n");
 
@@ -420,20 +420,20 @@ pub fn main(args: [][]u8) %void => {
 import "std.zig";
 
 pub fn main(args: [][]u8) %void => {
-    if (false || false || false) { print_str("BAD 1\n"); }
-    if (true && true && false)   { print_str("BAD 2\n"); }
-    if (1 | 2 | 4 != 7)          { print_str("BAD 3\n"); }
-    if (3 ^ 6 ^ 8 != 13)         { print_str("BAD 4\n"); }
-    if (7 & 14 & 28 != 4)        { print_str("BAD 5\n"); }
-    if (9  << 1 << 2 != 9  << 3) { print_str("BAD 6\n"); }
-    if (90 >> 1 >> 2 != 90 >> 3) { print_str("BAD 7\n"); }
-    if (100 - 1 + 1000 != 1099)  { print_str("BAD 8\n"); }
-    if (5 * 4 / 2 % 3 != 1)      { print_str("BAD 9\n"); }
-    if (i32(i32(5)) != 5)        { print_str("BAD 10\n"); }
-    if (!!false)                 { print_str("BAD 11\n"); }
-    if (i32(7) != --(i32(7)))    { print_str("BAD 12\n"); }
-
-    print_str("OK\n");
+    if (false || false || false) { stdout.printf("BAD 1\n"); }
+    if (true && true && false)   { stdout.printf("BAD 2\n"); }
+    if (1 | 2 | 4 != 7)          { stdout.printf("BAD 3\n"); }
+    if (3 ^ 6 ^ 8 != 13)         { stdout.printf("BAD 4\n"); }
+    if (7 & 14 & 28 != 4)        { stdout.printf("BAD 5\n"); }
+    if (9  << 1 << 2 != 9  << 3) { stdout.printf("BAD 6\n"); }
+    if (90 >> 1 >> 2 != 90 >> 3) { stdout.printf("BAD 7\n"); }
+    if (100 - 1 + 1000 != 1099)  { stdout.printf("BAD 8\n"); }
+    if (5 * 4 / 2 % 3 != 1)      { stdout.printf("BAD 9\n"); }
+    if (i32(i32(5)) != 5)        { stdout.printf("BAD 10\n"); }
+    if (!!false)                 { stdout.printf("BAD 11\n"); }
+    if (i32(7) != --(i32(7)))    { stdout.printf("BAD 12\n"); }
+
+    stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
@@ -441,19 +441,19 @@ pub fn main(args: [][]u8) %void => {
 import "std.zig";
 
 pub fn main(args: [][]u8) %void => {
-    if (true || { print_str("BAD 1\n"); false }) {
-      print_str("OK 1\n");
+    if (true || { stdout.printf("BAD 1\n"); false }) {
+      stdout.printf("OK 1\n");
     }
-    if (false || { print_str("OK 2\n"); false }) {
-      print_str("BAD 2\n");
+    if (false || { stdout.printf("OK 2\n"); false }) {
+      stdout.printf("BAD 2\n");
     }
 
-    if (true && { print_str("OK 3\n"); false }) {
-      print_str("BAD 3\n");
+    if (true && { stdout.printf("OK 3\n"); false }) {
+      stdout.printf("BAD 3\n");
     }
-    if (false && { print_str("BAD 4\n"); false }) {
+    if (false && { stdout.printf("BAD 4\n"); false }) {
     } else {
-      print_str("OK 4\n");
+      stdout.printf("OK 4\n");
     }
 }
     )SOURCE", "OK 1\nOK 2\nOK 3\nOK 4\n");
@@ -463,20 +463,20 @@ import "std.zig";
 
 pub fn main(args: [][]u8) %void => {
     var i : i32 = 0;
-    i += 5;  if (i != 5)  { print_str("BAD +=\n"); }
-    i -= 2;  if (i != 3)  { print_str("BAD -=\n"); }
-    i *= 20; if (i != 60) { print_str("BAD *=\n"); }
-    i /= 3;  if (i != 20) { print_str("BAD /=\n"); }
-    i %= 11; if (i != 9)  { print_str("BAD %=\n"); }
-    i <<= 1; if (i != 18) { print_str("BAD <<=\n"); }
-    i >>= 2; if (i != 4)  { print_str("BAD >>=\n"); }
+    i += 5;  if (i != 5)  { stdout.printf("BAD +=\n"); }
+    i -= 2;  if (i != 3)  { stdout.printf("BAD -=\n"); }
+    i *= 20; if (i != 60) { stdout.printf("BAD *=\n"); }
+    i /= 3;  if (i != 20) { stdout.printf("BAD /=\n"); }
+    i %= 11; if (i != 9)  { stdout.printf("BAD %=\n"); }
+    i <<= 1; if (i != 18) { stdout.printf("BAD <<=\n"); }
+    i >>= 2; if (i != 4)  { stdout.printf("BAD >>=\n"); }
     i = 6;
-    i &= 5;  if (i != 4)  { print_str("BAD &=\n"); }
-    i ^= 6;  if (i != 2)  { print_str("BAD ^=\n"); }
+    i &= 5;  if (i != 4)  { stdout.printf("BAD &=\n"); }
+    i ^= 6;  if (i != 2)  { stdout.printf("BAD ^=\n"); }
     i = 6;
-    i |= 3;  if (i != 7)  { print_str("BAD |=\n"); }
+    i |= 3;  if (i != 7)  { stdout.printf("BAD |=\n"); }
 
-    print_str("OK\n");
+    stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
@@ -620,12 +620,12 @@ pub fn main(args: [][]u8) %void => {
     test_foo(foo);
     test_mutation(&foo);
     if (foo.c != 100) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
     test_point_to_self();
     test_byval_assign();
     test_initializer();
-    print_str("OK\n");
+    stdout.printf("OK\n");
 }
 struct Foo {
     a : i32,
@@ -634,7 +634,7 @@ struct Foo {
 }
 fn test_foo(foo : Foo) => {
     if (!foo.b) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 }
 fn test_mutation(foo : &Foo) => {
@@ -659,7 +659,7 @@ fn test_point_to_self() => {
     root.next = &node;
 
     if (node.next.next.next.val.x != 1) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 }
 fn test_byval_assign() => {
@@ -668,15 +668,15 @@ fn test_byval_assign() => {
 
     foo1.a = 1234;
 
-    if (foo2.a != 0) { print_str("BAD\n"); }
+    if (foo2.a != 0) { stdout.printf("BAD\n"); }
 
     foo2 = foo1;
 
-    if (foo2.a != 1234) { print_str("BAD - byval assignment failed\n"); }
+    if (foo2.a != 1234) { stdout.printf("BAD - byval assignment failed\n"); }
 }
 fn test_initializer() => {
     const val = Val { .x = 42 };
-    if (val.x != 42) { print_str("BAD\n"); }
+    if (val.x != 42) { stdout.printf("BAD\n"); }
 }
     )SOURCE", "OK\n");
 
@@ -687,10 +687,10 @@ const g1 : i32 = 1233 + 1;
 var g2 : i32 = 0;
 
 pub fn main(args: [][]u8) %void => {
-    if (g2 != 0) { print_str("BAD\n"); }
+    if (g2 != 0) { stdout.printf("BAD\n"); }
     g2 = g1;
-    if (g2 != 1234) { print_str("BAD\n"); }
-    print_str("OK\n");
+    if (g2 != 1234) { stdout.printf("BAD\n"); }
+    stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
@@ -699,7 +699,7 @@ import "std.zig";
 pub fn main(args: [][]u8) %void => {
     var i : i32 = 0;
     while (i < 4) {
-        print_str("loop\n");
+        stdout.printf("loop\n");
         i += 1;
     }
     g();
@@ -719,7 +719,7 @@ import "std.zig";
 pub fn main(args: [][]u8) %void => {
     var i : i32 = 0;
     while (true) {
-        print_str("loop\n");
+        stdout.printf("loop\n");
         i += 1;
         if (i < 4) {
             continue;
@@ -736,12 +736,12 @@ pub fn main(args: [][]u8) %void => {
 
     if (const y ?= x) {
         if (y) {
-            print_str("x is true\n");
+            stdout.printf("x is true\n");
         } else {
-            print_str("x is false\n");
+            stdout.printf("x is false\n");
         }
     } else {
-        print_str("x is none\n");
+        stdout.printf("x is none\n");
     }
 
     const next_x : ?i32 = null;
@@ -749,7 +749,7 @@ pub fn main(args: [][]u8) %void => {
     const z = next_x ?? 1234;
 
     if (z != 1234) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 
     const final_x : ?i32 = 13;
@@ -757,7 +757,7 @@ pub fn main(args: [][]u8) %void => {
     const num = final_x ?? unreachable{};
 
     if (num != 13) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 }
     )SOURCE", "x is true\n");
@@ -767,7 +767,7 @@ import "std.zig";
 pub fn main(args: [][]u8) %void => {
     const x = outer();
     if (x == 1234) {
-        print_str("OK\n");
+        stdout.printf("OK\n");
     }
 }
 fn inner() i32 => { 1234 }
@@ -782,8 +782,8 @@ const x: u16 = 13;
 const z: @typeof(x) = 19;
 pub fn main(args: [][]u8) %void => {
     const y: @typeof(x) = 120;
-    print_u64(@sizeof(@typeof(y)));
-    print_str("\n");
+    stdout.print_u64(@sizeof(@typeof(y)));
+    stdout.printf("\n");
 }
     )SOURCE", "2\n");
 
@@ -798,9 +798,9 @@ struct Rand {
 pub fn main(args: [][]u8) %void => {
     const r = Rand {.seed = 1234};
     if (r.get_seed() != 1234) {
-        print_str("BAD seed\n");
+        stdout.printf("BAD seed\n");
     }
-    print_str("OK\n");
+    stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
@@ -814,12 +814,12 @@ pub fn main(args: [][]u8) %void => {
     *y += 1;
 
     if (x != 4) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
     if (*y != 4) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
-    print_str("OK\n");
+    stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
@@ -830,77 +830,77 @@ const ARRAY_SIZE : i8 = 20;
 
 pub fn main(args: [][]u8) %void => {
     var array : [ARRAY_SIZE]u8;
-    print_u64(@sizeof(@typeof(array)));
-    print_str("\n");
+    stdout.print_u64(@sizeof(@typeof(array)));
+    stdout.printf("\n");
 }
     )SOURCE", "20\n");
 
     add_simple_case("@min_value() and @max_value()", R"SOURCE(
 import "std.zig";
 pub fn main(args: [][]u8) %void => {
-    print_str("max u8: ");
-    print_u64(@max_value(u8));
-    print_str("\n");
+    stdout.printf("max u8: ");
+    stdout.print_u64(@max_value(u8));
+    stdout.printf("\n");
 
-    print_str("max u16: ");
-    print_u64(@max_value(u16));
-    print_str("\n");
+    stdout.printf("max u16: ");
+    stdout.print_u64(@max_value(u16));
+    stdout.printf("\n");
 
-    print_str("max u32: ");
-    print_u64(@max_value(u32));
-    print_str("\n");
+    stdout.printf("max u32: ");
+    stdout.print_u64(@max_value(u32));
+    stdout.printf("\n");
 
-    print_str("max u64: ");
-    print_u64(@max_value(u64));
-    print_str("\n");
+    stdout.printf("max u64: ");
+    stdout.print_u64(@max_value(u64));
+    stdout.printf("\n");
 
-    print_str("max i8: ");
-    print_i64(@max_value(i8));
-    print_str("\n");
+    stdout.printf("max i8: ");
+    stdout.print_i64(@max_value(i8));
+    stdout.printf("\n");
 
-    print_str("max i16: ");
-    print_i64(@max_value(i16));
-    print_str("\n");
+    stdout.printf("max i16: ");
+    stdout.print_i64(@max_value(i16));
+    stdout.printf("\n");
 
-    print_str("max i32: ");
-    print_i64(@max_value(i32));
-    print_str("\n");
+    stdout.printf("max i32: ");
+    stdout.print_i64(@max_value(i32));
+    stdout.printf("\n");
 
-    print_str("max i64: ");
-    print_i64(@max_value(i64));
-    print_str("\n");
+    stdout.printf("max i64: ");
+    stdout.print_i64(@max_value(i64));
+    stdout.printf("\n");
 
-    print_str("min u8: ");
-    print_u64(@min_value(u8));
-    print_str("\n");
+    stdout.printf("min u8: ");
+    stdout.print_u64(@min_value(u8));
+    stdout.printf("\n");
 
-    print_str("min u16: ");
-    print_u64(@min_value(u16));
-    print_str("\n");
+    stdout.printf("min u16: ");
+    stdout.print_u64(@min_value(u16));
+    stdout.printf("\n");
 
-    print_str("min u32: ");
-    print_u64(@min_value(u32));
-    print_str("\n");
+    stdout.printf("min u32: ");
+    stdout.print_u64(@min_value(u32));
+    stdout.printf("\n");
 
-    print_str("min u64: ");
-    print_u64(@min_value(u64));
-    print_str("\n");
+    stdout.printf("min u64: ");
+    stdout.print_u64(@min_value(u64));
+    stdout.printf("\n");
 
-    print_str("min i8: ");
-    print_i64(@min_value(i8));
-    print_str("\n");
+    stdout.printf("min i8: ");
+    stdout.print_i64(@min_value(i8));
+    stdout.printf("\n");
 
-    print_str("min i16: ");
-    print_i64(@min_value(i16));
-    print_str("\n");
+    stdout.printf("min i16: ");
+    stdout.print_i64(@min_value(i16));
+    stdout.printf("\n");
 
-    print_str("min i32: ");
-    print_i64(@min_value(i32));
-    print_str("\n");
+    stdout.printf("min i32: ");
+    stdout.print_i64(@min_value(i32));
+    stdout.printf("\n");
 
-    print_str("min i64: ");
-    print_i64(@min_value(i64));
-    print_str("\n");
+    stdout.printf("min i64: ");
+    stdout.print_i64(@min_value(i64));
+    stdout.printf("\n");
 }
     )SOURCE",
         "max u8: 255\n"
@@ -931,19 +931,19 @@ pub fn main(args: [][]u8) %void => {
     var slice = array[5...10];
 
     if (slice.len != 5) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 
     if (slice.ptr[0] != 1234) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 
     var slice_rest = array[10...];
     if (slice_rest.len != 10) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 
-    print_str("OK\n");
+    stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
@@ -952,7 +952,7 @@ pub fn main(args: [][]u8) %void => {
 import "std.zig";
 pub fn main(args: [][]u8) %void => {
     if (f(1) == 1) {
-        print_str("OK\n");
+        stdout.printf("OK\n");
     }
 }
 fn f(c: u8) u8 => {
@@ -971,15 +971,15 @@ import "std.zig";
 pub fn main(args: [][]u8) %void => {
     var result: u8;
     if (!@add_with_overflow(u8, 250, 100, &result)) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
     if (@add_with_overflow(u8, 100, 150, &result)) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
     if (result != 250) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
-    print_str("OK\n");
+    stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
@@ -993,10 +993,10 @@ pub fn main(args: [][]u8) %void => {
     @memcpy(bar.ptr, foo.ptr, bar.len);
 
     if (bar[11] != 'A') {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 
-    print_str("OK\n");
+    stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
@@ -1009,7 +1009,7 @@ pub fn main(args: [][]u8) %void => {
     var x : i32 = print_ok(x);
 }
 fn print_ok(val: @typeof(x)) @typeof(foo) => {
-    print_str("OK\n");
+    stdout.printf("OK\n");
     return 0;
 }
 const foo : i32 = 0;
@@ -1042,25 +1042,25 @@ pub fn main(args: [][]u8) %void => {
     const bar = Bar.B;
 
     if (bar != Bar.B) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 
     if (@member_count(Foo) != 3) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 
     if (@member_count(Bar) != 4) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 
     if (@sizeof(Foo) != 17) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
     if (@sizeof(Bar) != 1) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 
-    print_str("OK\n");
+    stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
@@ -1071,14 +1071,14 @@ pub fn main(args: [][]u8) %void => {
     const HEX_MULT = []u16{4096, 256, 16, 1};
 
     if (HEX_MULT.len != 4) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 
     if (HEX_MULT[1] != 256) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 
-    print_str("OK\n");
+    stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
@@ -1089,8 +1089,8 @@ pub fn main(args: [][]u8) %void => {
     const array_of_strings = [][]u8 {"hello", "this", "is", "my", "thing"};
     var i: @typeof(array_of_strings.len) = 0;
     while (i < array_of_strings.len) {
-        print_str(array_of_strings[i]);
-        print_str("\n");
+        stdout.printf(array_of_strings[i]);
+        stdout.printf("\n");
         i += 1;
     }
 }
@@ -1102,21 +1102,21 @@ import "std.zig";
 pub fn main(args: [][]u8) %void => {
     const array = []u8 {9, 8, 7, 6};
     for (item, array) {
-        print_u64(item);
-        print_str("\n");
+        stdout.print_u64(item);
+        stdout.printf("\n");
     }
     for (item, array, index) {
-        print_i64(index);
-        print_str("\n");
+        stdout.print_i64(index);
+        stdout.printf("\n");
     }
     const unknown_size: []u8 = array;
     for (item, unknown_size) {
-        print_u64(item);
-        print_str("\n");
+        stdout.print_u64(item);
+        stdout.printf("\n");
     }
     for (item, unknown_size, index) {
-        print_i64(index);
-        print_str("\n");
+        stdout.print_i64(index);
+        stdout.printf("\n");
     }
 }
     )SOURCE", "9\n8\n7\n6\n0\n1\n2\n3\n9\n8\n7\n6\n0\n1\n2\n3\n");
@@ -1127,8 +1127,8 @@ import "std.zig";
 pub fn main(args: [][]u8) %void => {
     const fns = []@typeof(fn1) { fn1, fn2, fn3, fn4, };
     for (f, fns) {
-        print_u64(f());
-        print_str("\n");
+        stdout.print_u64(f());
+        stdout.printf("\n");
     }
 }
 
@@ -1157,10 +1157,10 @@ pub fn main(args: [][]u8) %void => {
         Foo.D => 4,
     };
     if (val != 3) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 
-    print_str("OK\n");
+    stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
@@ -1174,10 +1174,10 @@ pub fn main(args: [][]u8) %void => {
     const eleven = ten + one;
 
     if (eleven != 11) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 
-    print_str("OK\n");
+    stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
@@ -1191,10 +1191,10 @@ var foo = Foo { .x = 13, .y = true, };
 pub fn main(args: [][]u8) %void => {
     foo.x += 1;
     if (foo.x != 14) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 
-    print_str("OK\n");
+    stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
@@ -1204,10 +1204,10 @@ const x = []u8{1,2,3,4};
 pub fn main(args: [][]u8) %void => {
     const y : [4]u8 = x;
     if (y[3] != 4) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 
-    print_str("OK\n");
+    stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
@@ -1219,10 +1219,10 @@ pub fn main(args: [][]u8) %void => {
     const a = i32(error.err1);
     const b = i32(error.err2);
     if (a == b) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
 
-    print_str("OK\n");
+    stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 
@@ -1230,7 +1230,7 @@ pub fn main(args: [][]u8) %void => {
 import "std.zig";
 pub fn main(args: [][]u8) %void => {
     while (true) {
-        print_str("OK\n");
+        stdout.printf("OK\n");
         return;
     }
 }
@@ -1251,9 +1251,9 @@ fn make_foo(x: i32, y: i32) Foo => {
 pub fn main(args: [][]u8) %void => {
     const foo = make_foo(1234, 5678);
     if (foo.y != 5678) {
-        print_str("BAD\n");
+        stdout.printf("BAD\n");
     }
-    print_str("OK\n");
+    stdout.printf("OK\n");
 }
     )SOURCE", "OK\n");
 }