diff --git a/CMakeLists.txt b/CMakeLists.txt index 8906798c69..72c480cd40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -339,7 +339,7 @@ set(ZIG_SOURCES "${CMAKE_SOURCE_DIR}/src/target.cpp" "${CMAKE_SOURCE_DIR}/src/tokenizer.cpp" "${CMAKE_SOURCE_DIR}/src/util.cpp" - "${CMAKE_SOURCE_DIR}/src/parsec.cpp" + "${CMAKE_SOURCE_DIR}/src/translate_c.cpp" "${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp" ) diff --git a/build.zig b/build.zig index 79fefd5c7a..1c1d4f832e 100644 --- a/build.zig +++ b/build.zig @@ -58,5 +58,5 @@ pub fn build(b: &Builder) { test_step.dependOn(tests.addCompileErrorTests(b, test_filter)); test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter)); test_step.dependOn(tests.addDebugSafetyTests(b, test_filter)); - test_step.dependOn(tests.addParseCTests(b, test_filter)); + test_step.dependOn(tests.addTranslateCTests(b, test_filter)); } diff --git a/ci/travis_osx_script b/ci/travis_osx_script index 1f345c9c50..a55132395b 100755 --- a/ci/travis_osx_script +++ b/ci/travis_osx_script @@ -22,4 +22,4 @@ make install ./zig build --build-file ../build.zig test-compile-errors --verbose ./zig build --build-file ../build.zig test-asm-link --verbose ./zig build --build-file ../build.zig test-debug-safety --verbose -./zig build --build-file ../build.zig test-parsec --verbose +./zig build --build-file ../build.zig test-translate-c --verbose diff --git a/doc/home.html.in b/doc/home.html.in index 002ac0b70a..3b2ef3407b 100644 --- a/doc/home.html.in +++ b/doc/home.html.in @@ -70,10 +70,14 @@
  • Mersenne Twister Random Number Generator
  • Hello World

    -
    const io = @import("std").io;
    +    
    const std = @import("std");
     
     pub fn main() -> %void {
    -    %return io.stdout.printf("Hello, world!\n");
    +    // If this program is run without stdout attached, exit with an error.
    +    var stdout_file = %return std.io.getStdOut();
    +    // If this program encounters pipe failure when printing to stdout, exit
    +    // with an error.
    +    %return stdout_file.write("Hello, world!\n");
     }

    Build this with:

    zig build-exe hello.zig
    diff --git a/doc/langref.html.in b/doc/langref.html.in index 9aa142fc46..e8f76e230b 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -75,6 +75,7 @@
  • Slices
  • struct
  • enum
  • +
  • union
  • switch
  • while
  • for
  • @@ -209,6 +210,7 @@
  • Invalid Error Code
  • Invalid Enum Cast
  • Incorrect Pointer Alignment
  • +
  • Wrong Union Field Access
  • Memory
  • @@ -2189,6 +2191,8 @@ Test 4/4 enum builtins...OK
  • @enumTagName
  • @memberCount
  • +

    union

    +

    TODO union documentation

    switch

    const assert = @import("std").debug.assert;
     const builtin = @import("builtin");
    @@ -5117,6 +5121,9 @@ comptime {
           

    Incorrect Pointer Alignment

    TODO

    +

    Wrong Union Field Access

    +

    TODO

    +

    Memory

    TODO: explain no default allocator in zig

    TODO: show how to use the allocator interface

    @@ -5405,10 +5412,14 @@ const c = @cImport({ export fn decode_base_64(dest_ptr: &u8, dest_len: usize, source_ptr: &const u8, source_len: usize) -> usize { - const src = source_ptr[0...source_len]; - const dest = dest_ptr[0...dest_len]; - return base64.decode(dest, src).len; -}
    + const src = source_ptr[0..source_len]; + const dest = dest_ptr[0..dest_len]; + const base64_decoder = base64.standard_decoder_unsafe; + const decoded_size = base64_decoder.calcSize(src); + base64_decoder.decode(dest[0..decoded_size], src); + return decoded_size; +} +

    test.c

    // This header is generated by zig from base64.zig
     #include "base64.h"
    diff --git a/example/mix_o_files/base64.zig b/example/mix_o_files/base64.zig
    index 10438ad077..49c9bc6012 100644
    --- a/example/mix_o_files/base64.zig
    +++ b/example/mix_o_files/base64.zig
    @@ -3,5 +3,8 @@ const base64 = @import("std").base64;
     export fn decode_base_64(dest_ptr: &u8, dest_len: usize, source_ptr: &const u8, source_len: usize) -> usize {
         const src = source_ptr[0..source_len];
         const dest = dest_ptr[0..dest_len];
    -    return base64.decode(dest, src).len;
    +    const base64_decoder = base64.standard_decoder_unsafe;
    +    const decoded_size = base64_decoder.calcSize(src);
    +    base64_decoder.decode(dest[0..decoded_size], src);
    +    return decoded_size;
     }
    diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
    index 0716ad433b..71180b2001 100644
    --- a/src-self-hosted/main.zig
    +++ b/src-self-hosted/main.zig
    @@ -208,7 +208,7 @@ fn printUsage(outstream: &io.OutStream) -> %void {
             \\  build-exe [source]           create executable from source or object files
             \\  build-lib [source]           create library from source or object files
             \\  build-obj [source]           create object from source or assembly
    -        \\  parsec [source]              convert c code to zig code
    +        \\  translate-c [source]         convert c code to zig code
             \\  targets                      list available compilation targets
             \\  test [source]                create and run a test build
             \\  version                      print version number and exit
    diff --git a/src/all_types.hpp b/src/all_types.hpp
    index 0b4efd8415..2fccf08e88 100644
    --- a/src/all_types.hpp
    +++ b/src/all_types.hpp
    @@ -36,6 +36,7 @@ struct IrInstructionCast;
     struct IrBasicBlock;
     struct ScopeDecls;
     struct ZigWindowsSDK;
    +struct Tld;
     
     struct IrGotoItem {
         AstNode *source_node;
    @@ -59,7 +60,9 @@ struct IrExecutable {
         Buf *c_import_buf;
         AstNode *source_node;
         IrExecutable *parent_exec;
    +    IrExecutable *source_exec;
         Scope *begin_scope;
    +    ZigList tld_list;
     };
     
     enum OutType {
    @@ -73,6 +76,7 @@ enum ConstParentId {
         ConstParentIdNone,
         ConstParentIdStruct,
         ConstParentIdArray,
    +    ConstParentIdUnion,
     };
     
     struct ConstParent {
    @@ -87,6 +91,9 @@ struct ConstParent {
                 ConstExprValue *struct_val;
                 size_t field_index;
             } p_struct;
    +        struct {
    +            ConstExprValue *union_val;
    +        } p_union;
         } data;
     };
     
    @@ -100,6 +107,12 @@ struct ConstStructValue {
         ConstParent parent;
     };
     
    +struct ConstUnionValue {
    +    uint64_t tag;
    +    ConstExprValue *payload;
    +    ConstParent parent;
    +};
    +
     enum ConstArraySpecial {
         ConstArraySpecialNone,
         ConstArraySpecialUndef,
    @@ -238,6 +251,7 @@ struct ConstExprValue {
             ErrorTableEntry *x_pure_err;
             ConstEnumValue x_enum;
             ConstStructValue x_struct;
    +        ConstUnionValue x_union;
             ConstArrayValue x_array;
             ConstPtrValue x_ptr;
             ImportTableEntry *x_import;
    @@ -336,6 +350,13 @@ struct TypeEnumField {
         uint32_t gen_index;
     };
     
    +struct TypeUnionField {
    +    Buf *name;
    +    TypeTableEntry *type_entry;
    +    uint32_t value;
    +    uint32_t gen_index;
    +};
    +
     enum NodeType {
         NodeTypeRoot,
         NodeTypeFnProto,
    @@ -1021,14 +1042,19 @@ struct TypeTableEntryEnumTag {
         LLVMValueRef name_table;
     };
     
    +uint32_t type_ptr_hash(const TypeTableEntry *ptr);
    +bool type_ptr_eql(const TypeTableEntry *a, const TypeTableEntry *b);
    +
     struct TypeTableEntryUnion {
         AstNode *decl_node;
         ContainerLayout layout;
         uint32_t src_field_count;
         uint32_t gen_field_count;
    -    TypeStructField *fields;
    -    uint64_t size_bytes;
    +    TypeUnionField *fields;
         bool is_invalid; // true if any fields are invalid
    +    TypeTableEntry *tag_type;
    +    LLVMTypeRef union_type_ref;
    +
         ScopeDecls *decls_scope;
     
         // set this flag temporarily to detect infinite loops
    @@ -1039,6 +1065,13 @@ struct TypeTableEntryUnion {
     
         bool zero_bits_loop_flag;
         bool zero_bits_known;
    +    uint32_t abi_alignment; // also figured out with zero_bits pass
    +
    +    size_t gen_union_index;
    +    size_t gen_tag_index;
    +
    +    uint32_t union_size_bytes;
    +    TypeTableEntry *most_aligned_union_member;
     };
     
     struct FnGenParamInfo {
    @@ -1287,6 +1320,7 @@ enum PanicMsgId {
         PanicMsgIdUnwrapMaybeFail,
         PanicMsgIdInvalidErrorCode,
         PanicMsgIdIncorrectAlignment,
    +    PanicMsgIdBadUnionField,
     
         PanicMsgIdCount,
     };
    @@ -1796,6 +1830,7 @@ enum IrInstructionId {
         IrInstructionIdFieldPtr,
         IrInstructionIdStructFieldPtr,
         IrInstructionIdEnumFieldPtr,
    +    IrInstructionIdUnionFieldPtr,
         IrInstructionIdElemPtr,
         IrInstructionIdVarPtr,
         IrInstructionIdCall,
    @@ -1805,6 +1840,7 @@ enum IrInstructionId {
         IrInstructionIdContainerInitList,
         IrInstructionIdContainerInitFields,
         IrInstructionIdStructInit,
    +    IrInstructionIdUnionInit,
         IrInstructionIdUnreachable,
         IrInstructionIdTypeOf,
         IrInstructionIdToPtrType,
    @@ -2060,6 +2096,14 @@ struct IrInstructionEnumFieldPtr {
         bool is_const;
     };
     
    +struct IrInstructionUnionFieldPtr {
    +    IrInstruction base;
    +
    +    IrInstruction *union_ptr;
    +    TypeUnionField *field;
    +    bool is_const;
    +};
    +
     struct IrInstructionElemPtr {
         IrInstruction base;
     
    @@ -2150,6 +2194,15 @@ struct IrInstructionStructInit {
         LLVMValueRef tmp_ptr;
     };
     
    +struct IrInstructionUnionInit {
    +    IrInstruction base;
    +
    +    TypeTableEntry *union_type;
    +    TypeUnionField *field;
    +    IrInstruction *init_value;
    +    LLVMValueRef tmp_ptr;
    +};
    +
     struct IrInstructionUnreachable {
         IrInstruction base;
     };
    diff --git a/src/analyze.cpp b/src/analyze.cpp
    index 343b1ecdb0..1c223c63f7 100644
    --- a/src/analyze.cpp
    +++ b/src/analyze.cpp
    @@ -28,7 +28,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type);
     
     ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
         if (node->owner->c_import_node != nullptr) {
    -        // if this happens, then parsec generated code that
    +        // if this happens, then translate_c generated code that
             // failed semantic analysis, which isn't supposed to happen
             ErrorMsg *err = add_node_error(g, node->owner->c_import_node,
                 buf_sprintf("compiler bug: @cImport generated invalid zig code"));
    @@ -48,7 +48,7 @@ ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
     
     ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) {
         if (node->owner->c_import_node != nullptr) {
    -        // if this happens, then parsec generated code that
    +        // if this happens, then translate_c generated code that
             // failed semantic analysis, which isn't supposed to happen
     
             Buf *note_path = buf_create_from_str("?.c");
    @@ -338,7 +338,7 @@ TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) {
     TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type, bool is_const,
             bool is_volatile, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count)
     {
    -    assert(child_type->id != TypeTableEntryIdInvalid);
    +    assert(!type_is_invalid(child_type));
     
         TypeId type_id = {};
         TypeTableEntry **parent_pointer = nullptr;
    @@ -1008,11 +1008,12 @@ TypeTableEntry *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKi
         }
     
         size_t line = decl_node ? decl_node->line : 0;
    +    unsigned dwarf_kind = ZigLLVMTag_DW_structure_type();
     
         ImportTableEntry *import = get_scope_import(scope);
         entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), name);
         entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder,
    -        ZigLLVMTag_DW_structure_type(), name,
    +        dwarf_kind, name,
             ZigLLVMFileToScope(import->di_file), import->di_file, (unsigned)(line + 1));
     
         buf_init_from_str(&entry->name, name);
    @@ -1285,7 +1286,7 @@ static void resolve_enum_type(CodeGen *g, TypeTableEntry *enum_type) {
             return;
     
         resolve_enum_zero_bits(g, enum_type);
    -    if (enum_type->data.enumeration.is_invalid)
    +    if (type_is_invalid(enum_type))
             return;
     
         AstNode *decl_node = enum_type->data.enumeration.decl_node;
    @@ -1834,7 +1835,246 @@ static void resolve_struct_type(CodeGen *g, TypeTableEntry *struct_type) {
     }
     
     static void resolve_union_type(CodeGen *g, TypeTableEntry *union_type) {
    -    zig_panic("TODO");
    +    assert(union_type->id == TypeTableEntryIdUnion);
    +
    +    if (union_type->data.unionation.complete)
    +        return;
    +
    +    resolve_union_zero_bits(g, union_type);
    +    if (type_is_invalid(union_type))
    +        return;
    +
    +    AstNode *decl_node = union_type->data.unionation.decl_node;
    +
    +    if (union_type->data.unionation.embedded_in_current) {
    +        if (!union_type->data.unionation.reported_infinite_err) {
    +            union_type->data.unionation.reported_infinite_err = true;
    +            add_node_error(g, decl_node, buf_sprintf("union '%s' contains itself", buf_ptr(&union_type->name)));
    +        }
    +        return;
    +    }
    +
    +    assert(!union_type->data.unionation.zero_bits_loop_flag);
    +    assert(decl_node->type == NodeTypeContainerDecl);
    +    assert(union_type->di_type);
    +
    +    uint32_t field_count = union_type->data.unionation.src_field_count;
    +
    +    assert(union_type->data.unionation.fields);
    +
    +    uint32_t gen_field_count = union_type->data.unionation.gen_field_count;
    +    ZigLLVMDIType **union_inner_di_types = allocate(gen_field_count);
    +
    +    TypeTableEntry *most_aligned_union_member = nullptr;
    +    uint64_t size_of_most_aligned_member_in_bits = 0;
    +    uint64_t biggest_align_in_bits = 0;
    +    uint64_t biggest_size_in_bits = 0;
    +
    +    bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto);
    +    ZigLLVMDIEnumerator **di_enumerators = allocate(field_count);
    +
    +    Scope *scope = &union_type->data.unionation.decls_scope->base;
    +    ImportTableEntry *import = get_scope_import(scope);
    +
    +    // set temporary flag
    +    union_type->data.unionation.embedded_in_current = true;
    +
    +    for (uint32_t i = 0; i < field_count; i += 1) {
    +        AstNode *field_node = decl_node->data.container_decl.fields.at(i);
    +        TypeUnionField *type_union_field = &union_type->data.unionation.fields[i];
    +        TypeTableEntry *field_type = type_union_field->type_entry;
    +
    +        ensure_complete_type(g, field_type);
    +        if (type_is_invalid(field_type)) {
    +            union_type->data.unionation.is_invalid = true;
    +            continue;
    +        }
    +
    +        if (!type_has_bits(field_type))
    +            continue;
    +
    +        di_enumerators[i] = ZigLLVMCreateDebugEnumerator(g->dbuilder, buf_ptr(type_union_field->name), i);
    +
    +        uint64_t store_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref);
    +        uint64_t abi_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, field_type->type_ref);
    +
    +        assert(store_size_in_bits > 0);
    +        assert(abi_align_in_bits > 0);
    +
    +        union_inner_di_types[type_union_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder,
    +                ZigLLVMTypeToScope(union_type->di_type), buf_ptr(type_union_field->name),
    +                import->di_file, (unsigned)(field_node->line + 1),
    +                store_size_in_bits,
    +                abi_align_in_bits,
    +                0,
    +                0, field_type->di_type);
    +
    +        biggest_size_in_bits = max(biggest_size_in_bits, store_size_in_bits);
    +
    +        if (!most_aligned_union_member || abi_align_in_bits > biggest_align_in_bits) {
    +            most_aligned_union_member = field_type;
    +            biggest_align_in_bits = abi_align_in_bits;
    +            size_of_most_aligned_member_in_bits = store_size_in_bits;
    +        }
    +    }
    +
    +    // unset temporary flag
    +    union_type->data.unionation.embedded_in_current = false;
    +    union_type->data.unionation.complete = true;
    +    union_type->data.unionation.union_size_bytes = biggest_size_in_bits / 8;
    +    union_type->data.unionation.most_aligned_union_member = most_aligned_union_member;
    +
    +    if (union_type->data.unionation.is_invalid)
    +        return;
    +
    +    if (union_type->zero_bits) {
    +        union_type->type_ref = LLVMVoidType();
    +
    +        uint64_t debug_size_in_bits = 0;
    +        uint64_t debug_align_in_bits = 0;
    +        ZigLLVMDIType **di_root_members = nullptr;
    +        size_t debug_member_count = 0;
    +        ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
    +                ZigLLVMFileToScope(import->di_file),
    +                buf_ptr(&union_type->name),
    +                import->di_file, (unsigned)(decl_node->line + 1),
    +                debug_size_in_bits,
    +                debug_align_in_bits,
    +                0, di_root_members, (int)debug_member_count, 0, "");
    +
    +        ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type);
    +        union_type->di_type = replacement_di_type;
    +        return;
    +    }
    +
    +    assert(most_aligned_union_member != nullptr);
    +
    +    bool want_safety = auto_layout && (field_count >= 2);
    +    uint64_t padding_in_bits = biggest_size_in_bits - size_of_most_aligned_member_in_bits;
    +
    +
    +    if (!want_safety) {
    +        if (padding_in_bits > 0) {
    +            TypeTableEntry *u8_type = get_int_type(g, false, 8);
    +            TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8);
    +            LLVMTypeRef union_element_types[] = {
    +                most_aligned_union_member->type_ref,
    +                padding_array->type_ref,
    +            };
    +            LLVMStructSetBody(union_type->type_ref, union_element_types, 2, false);
    +        } else {
    +            LLVMStructSetBody(union_type->type_ref, &most_aligned_union_member->type_ref, 1, false);
    +        }
    +        union_type->data.unionation.union_type_ref = union_type->type_ref;
    +        union_type->data.unionation.gen_tag_index = SIZE_MAX;
    +        union_type->data.unionation.gen_union_index = SIZE_MAX;
    +
    +        assert(8*LLVMABIAlignmentOfType(g->target_data_ref, union_type->type_ref) >= biggest_align_in_bits);
    +        assert(8*LLVMStoreSizeOfType(g->target_data_ref, union_type->type_ref) >= biggest_size_in_bits);
    +
    +        // create debug type for union
    +        ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
    +            ZigLLVMFileToScope(import->di_file), buf_ptr(&union_type->name),
    +            import->di_file, (unsigned)(decl_node->line + 1),
    +            biggest_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types,
    +            gen_field_count, 0, "");
    +
    +        ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type);
    +        union_type->di_type = replacement_di_type;
    +        return;
    +    }
    +
    +    LLVMTypeRef union_type_ref;
    +    if (padding_in_bits > 0) {
    +        TypeTableEntry *u8_type = get_int_type(g, false, 8);
    +        TypeTableEntry *padding_array = get_array_type(g, u8_type, padding_in_bits / 8);
    +        LLVMTypeRef union_element_types[] = {
    +            most_aligned_union_member->type_ref,
    +            padding_array->type_ref,
    +        };
    +        union_type_ref = LLVMStructType(union_element_types, 2, false);
    +    } else {
    +        union_type_ref = most_aligned_union_member->type_ref;
    +    }
    +    union_type->data.unionation.union_type_ref = union_type_ref;
    +
    +    assert(8*LLVMABIAlignmentOfType(g->target_data_ref, union_type_ref) >= biggest_align_in_bits);
    +    assert(8*LLVMStoreSizeOfType(g->target_data_ref, union_type_ref) >= biggest_size_in_bits);
    +
    +    // create llvm type for root struct
    +    TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1);
    +    TypeTableEntry *tag_type_entry = tag_int_type;
    +    union_type->data.unionation.tag_type = tag_type_entry;
    +    uint64_t align_of_tag_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref);
    +
    +    if (align_of_tag_in_bits >= biggest_align_in_bits) {
    +        union_type->data.unionation.gen_tag_index = 0;
    +        union_type->data.unionation.gen_union_index = 1;
    +    } else {
    +        union_type->data.unionation.gen_union_index = 0;
    +        union_type->data.unionation.gen_tag_index = 1;
    +    }
    +
    +    LLVMTypeRef root_struct_element_types[2];
    +    root_struct_element_types[union_type->data.unionation.gen_tag_index] = tag_type_entry->type_ref;
    +    root_struct_element_types[union_type->data.unionation.gen_union_index] = union_type_ref;
    +    LLVMStructSetBody(union_type->type_ref, root_struct_element_types, 2, false);
    +
    +
    +    // create debug type for root struct
    +
    +    // create debug type for tag
    +    uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type_entry->type_ref);
    +    uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type_entry->type_ref);
    +    ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder,
    +            ZigLLVMTypeToScope(union_type->di_type), "AnonEnum",
    +            import->di_file, (unsigned)(decl_node->line + 1),
    +            tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count,
    +            tag_type_entry->di_type, "");
    +
    +    // create debug type for union
    +    ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder,
    +            ZigLLVMTypeToScope(union_type->di_type), "AnonUnion",
    +            import->di_file, (unsigned)(decl_node->line + 1),
    +            biggest_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types,
    +            gen_field_count, 0, "");
    +
    +    uint64_t union_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, union_type->type_ref,
    +            union_type->data.unionation.gen_union_index);
    +    uint64_t tag_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, union_type->type_ref,
    +            union_type->data.unionation.gen_tag_index);
    +
    +    ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
    +            ZigLLVMTypeToScope(union_type->di_type), "union_field",
    +            import->di_file, (unsigned)(decl_node->line + 1),
    +            biggest_size_in_bits,
    +            biggest_align_in_bits,
    +            union_offset_in_bits,
    +            0, union_di_type);
    +    ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder,
    +            ZigLLVMTypeToScope(union_type->di_type), "tag_field",
    +            import->di_file, (unsigned)(decl_node->line + 1),
    +            tag_debug_size_in_bits,
    +            tag_debug_align_in_bits,
    +            tag_offset_in_bits,
    +            0, tag_di_type);
    +
    +    ZigLLVMDIType *di_root_members[2];
    +    di_root_members[union_type->data.unionation.gen_tag_index] = tag_member_di_type;
    +    di_root_members[union_type->data.unionation.gen_union_index] = union_member_di_type;
    +
    +    uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, union_type->type_ref);
    +    uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, union_type->type_ref);
    +    ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder,
    +            ZigLLVMFileToScope(import->di_file),
    +            buf_ptr(&union_type->name),
    +            import->di_file, (unsigned)(decl_node->line + 1),
    +            debug_size_in_bits,
    +            debug_align_in_bits,
    +            0, nullptr, di_root_members, 2, 0, nullptr, "");
    +
    +    ZigLLVMReplaceTemporary(g->dbuilder, union_type->di_type, replacement_di_type);
    +    union_type->di_type = replacement_di_type;
     }
     
     static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
    @@ -1873,7 +2113,7 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
             type_enum_field->value = i;
     
             type_ensure_zero_bits_known(g, field_type);
    -        if (field_type->id == TypeTableEntryIdInvalid) {
    +        if (type_is_invalid(field_type)) {
                 enum_type->data.enumeration.is_invalid = true;
                 continue;
             }
    @@ -1980,7 +2220,69 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
     }
     
     static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
    -    zig_panic("TODO resolve_union_zero_bits");
    +    assert(union_type->id == TypeTableEntryIdUnion);
    +
    +    if (union_type->data.unionation.zero_bits_known)
    +        return;
    +
    +    if (union_type->data.unionation.zero_bits_loop_flag) {
    +        union_type->data.unionation.zero_bits_known = true;
    +        return;
    +    }
    +
    +    union_type->data.unionation.zero_bits_loop_flag = true;
    +
    +    AstNode *decl_node = union_type->data.unionation.decl_node;
    +    assert(decl_node->type == NodeTypeContainerDecl);
    +    assert(union_type->di_type);
    +
    +    assert(!union_type->data.unionation.fields);
    +    uint32_t field_count = (uint32_t)decl_node->data.container_decl.fields.length;
    +    union_type->data.unionation.src_field_count = field_count;
    +    union_type->data.unionation.fields = allocate(field_count);
    +
    +    uint32_t biggest_align_bytes = 0;
    +
    +    Scope *scope = &union_type->data.unionation.decls_scope->base;
    +
    +    uint32_t gen_field_index = 0;
    +    for (uint32_t i = 0; i < field_count; i += 1) {
    +        AstNode *field_node = decl_node->data.container_decl.fields.at(i);
    +        TypeUnionField *type_union_field = &union_type->data.unionation.fields[i];
    +        type_union_field->name = field_node->data.struct_field.name;
    +        TypeTableEntry *field_type = analyze_type_expr(g, scope, field_node->data.struct_field.type);
    +        type_union_field->type_entry = field_type;
    +        type_union_field->value = i;
    +
    +        type_ensure_zero_bits_known(g, field_type);
    +        if (type_is_invalid(field_type)) {
    +            union_type->data.unionation.is_invalid = true;
    +            continue;
    +        }
    +
    +        if (!type_has_bits(field_type))
    +            continue;
    +
    +        type_union_field->gen_index = gen_field_index;
    +        gen_field_index += 1;
    +
    +        uint32_t field_align_bytes = get_abi_alignment(g, field_type);
    +        if (field_align_bytes > biggest_align_bytes) {
    +            biggest_align_bytes = field_align_bytes;
    +        }
    +    }
    +
    +    bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto);
    +
    +    union_type->data.unionation.zero_bits_loop_flag = false;
    +    union_type->data.unionation.gen_field_count = gen_field_index;
    +    union_type->zero_bits = (gen_field_index == 0 && (field_count < 2 || !auto_layout));
    +    union_type->data.unionation.zero_bits_known = true;
    +
    +    // also compute abi_alignment
    +    if (!union_type->zero_bits) {
    +        union_type->data.unionation.abi_alignment = biggest_align_bytes;
    +    }
     }
     
     static void get_fully_qualified_decl_name_internal(Buf *buf, Scope *scope, uint8_t sep) {
    @@ -2851,6 +3153,18 @@ TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) {
         return nullptr;
     }
     
    +TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) {
    +    assert(type_entry->id == TypeTableEntryIdUnion);
    +    assert(type_entry->data.unionation.complete);
    +    for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) {
    +        TypeUnionField *field = &type_entry->data.unionation.fields[i];
    +        if (buf_eql_buf(field->name, name)) {
    +            return field;
    +        }
    +    }
    +    return nullptr;
    +}
    +
     static bool is_container(TypeTableEntry *type_entry) {
         switch (type_entry->id) {
             case TypeTableEntryIdInvalid:
    @@ -4703,6 +5017,8 @@ ConstParent *get_const_val_parent(CodeGen *g, ConstExprValue *value) {
             return &value->data.x_array.s_none.parent;
         } else if (type_entry->id == TypeTableEntryIdStruct) {
             return &value->data.x_struct.parent;
    +    } else if (type_entry->id == TypeTableEntryIdUnion) {
    +        return &value->data.x_union.parent;
         }
         return nullptr;
     }
    @@ -4914,7 +5230,8 @@ uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) {
             assert(type_entry->data.enumeration.abi_alignment != 0);
             return type_entry->data.enumeration.abi_alignment;
         } else if (type_entry->id == TypeTableEntryIdUnion) {
    -        zig_panic("TODO");
    +        assert(type_entry->data.unionation.abi_alignment != 0);
    +        return type_entry->data.unionation.abi_alignment;
         } else if (type_entry->id == TypeTableEntryIdOpaque) {
             return 1;
         } else {
    @@ -4929,3 +5246,11 @@ TypeTableEntry *get_align_amt_type(CodeGen *g) {
         }
         return g->align_amt_type;
     }
    +
    +uint32_t type_ptr_hash(const TypeTableEntry *ptr) {
    +    return hash_ptr((void*)ptr);
    +}
    +
    +bool type_ptr_eql(const TypeTableEntry *a, const TypeTableEntry *b) {
    +    return a == b;
    +}
    diff --git a/src/analyze.hpp b/src/analyze.hpp
    index 4f56640592..b2464af9a0 100644
    --- a/src/analyze.hpp
    +++ b/src/analyze.hpp
    @@ -63,6 +63,7 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry);
     TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name);
     ScopeDecls *get_container_scope(TypeTableEntry *type_entry);
     TypeEnumField *find_enum_type_field(TypeTableEntry *enum_type, Buf *name);
    +TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name);
     bool is_container_ref(TypeTableEntry *type_entry);
     void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);
     void scan_import(CodeGen *g, ImportTableEntry *import);
    diff --git a/src/c_tokenizer.cpp b/src/c_tokenizer.cpp
    index 044831f72e..6be2cf991e 100644
    --- a/src/c_tokenizer.cpp
    +++ b/src/c_tokenizer.cpp
    @@ -120,6 +120,7 @@ static void begin_token(CTokenize *ctok, CTokId id) {
             case CTokIdLParen:
             case CTokIdRParen:
             case CTokIdEOF:
    +        case CTokIdDot:
                 break;
         }
     }
    @@ -216,9 +217,8 @@ void tokenize_c_macro(CTokenize *ctok, const uint8_t *c) {
                             buf_append_char(&ctok->buf, '0');
                             break;
                         case '.':
    -                        begin_token(ctok, CTokIdNumLitFloat);
    -                        ctok->state = CTokStateFloat;
    -                        buf_init_from_str(&ctok->buf, "0.");
    +                        begin_token(ctok, CTokIdDot);
    +                        end_token(ctok);
                             break;
                         case '(':
                             begin_token(ctok, CTokIdLParen);
    @@ -238,6 +238,8 @@ void tokenize_c_macro(CTokenize *ctok, const uint8_t *c) {
                     break;
                 case CTokStateFloat:
                     switch (*c) {
    +                    case '.':
    +                        break;
                         case 'e':
                         case 'E':
                             buf_append_char(&ctok->buf, 'e');
    diff --git a/src/c_tokenizer.hpp b/src/c_tokenizer.hpp
    index 8eea6c56c7..a3df2b94af 100644
    --- a/src/c_tokenizer.hpp
    +++ b/src/c_tokenizer.hpp
    @@ -21,6 +21,7 @@ enum CTokId {
         CTokIdLParen,
         CTokIdRParen,
         CTokIdEOF,
    +    CTokIdDot,
     };
     
     enum CNumLitSuffix {
    diff --git a/src/codegen.cpp b/src/codegen.cpp
    index 17c0ffc653..24d24a91e5 100644
    --- a/src/codegen.cpp
    +++ b/src/codegen.cpp
    @@ -15,7 +15,7 @@
     #include "ir.hpp"
     #include "link.hpp"
     #include "os.hpp"
    -#include "parsec.hpp"
    +#include "translate_c.hpp"
     #include "target.hpp"
     #include "zig_llvm.hpp"
     
    @@ -810,6 +810,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
                 return buf_create_from_str("invalid error code");
             case PanicMsgIdIncorrectAlignment:
                 return buf_create_from_str("incorrect alignment");
    +        case PanicMsgIdBadUnionField:
    +            return buf_create_from_str("access of inactive union field");
         }
         zig_unreachable();
     }
    @@ -2393,6 +2395,50 @@ static LLVMValueRef ir_render_enum_field_ptr(CodeGen *g, IrExecutable *executabl
         return bitcasted_union_field_ptr;
     }
     
    +static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executable,
    +    IrInstructionUnionFieldPtr *instruction)
    +{
    +    TypeTableEntry *union_ptr_type = instruction->union_ptr->value.type;
    +    assert(union_ptr_type->id == TypeTableEntryIdPointer);
    +    TypeTableEntry *union_type = union_ptr_type->data.pointer.child_type;
    +    assert(union_type->id == TypeTableEntryIdUnion);
    +
    +    TypeUnionField *field = instruction->field;
    +
    +    if (!type_has_bits(field->type_entry))
    +        return nullptr;
    +
    +    LLVMValueRef union_ptr = ir_llvm_value(g, instruction->union_ptr);
    +    LLVMTypeRef field_type_ref = LLVMPointerType(field->type_entry->type_ref, 0);
    +
    +    if (union_type->data.unionation.gen_tag_index == SIZE_MAX) {
    +        LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, 0, "");
    +        LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, "");
    +        return bitcasted_union_field_ptr;
    +    }
    +
    +    if (ir_want_debug_safety(g, &instruction->base)) {
    +        LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_tag_index, "");
    +        LLVMValueRef tag_value = gen_load_untyped(g, tag_field_ptr, 0, false, "");
    +        LLVMValueRef expected_tag_value = LLVMConstInt(union_type->data.unionation.tag_type->type_ref,
    +                field->value, false);
    +
    +        LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckOk");
    +        LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckFail");
    +        LLVMValueRef ok_val = LLVMBuildICmp(g->builder, LLVMIntEQ, tag_value, expected_tag_value, "");
    +        LLVMBuildCondBr(g->builder, ok_val, ok_block, bad_block);
    +
    +        LLVMPositionBuilderAtEnd(g->builder, bad_block);
    +        gen_debug_safety_crash(g, PanicMsgIdBadUnionField);
    +
    +        LLVMPositionBuilderAtEnd(g->builder, ok_block);
    +    }
    +
    +    LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_union_index, "");
    +    LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, "");
    +    return bitcasted_union_field_ptr;
    +}
    +
     static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok) {
         const char *ptr = buf_ptr(node->data.asm_expr.asm_template) + tok->start + 2;
         size_t len = tok->end - tok->start - 2;
    @@ -3365,6 +3411,42 @@ static LLVMValueRef ir_render_struct_init(CodeGen *g, IrExecutable *executable,
         return instruction->tmp_ptr;
     }
     
    +static LLVMValueRef ir_render_union_init(CodeGen *g, IrExecutable *executable, IrInstructionUnionInit *instruction) {
    +    TypeUnionField *type_union_field = instruction->field;
    +
    +    if (!type_has_bits(type_union_field->type_entry))
    +        return nullptr;
    +
    +    uint32_t field_align_bytes = get_abi_alignment(g, type_union_field->type_entry);
    +    TypeTableEntry *ptr_type = get_pointer_to_type_extra(g, type_union_field->type_entry,
    +            false, false, field_align_bytes,
    +            0, 0);
    +
    +    LLVMValueRef uncasted_union_ptr;
    +    // Even if safety is off in this block, if the union type has the safety field, we have to populate it
    +    // correctly. Otherwise safety code somewhere other than here could fail.
    +    TypeTableEntry *union_type = instruction->union_type;
    +    if (union_type->data.unionation.gen_tag_index != SIZE_MAX) {
    +        LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr,
    +                union_type->data.unionation.gen_tag_index, "");
    +        LLVMValueRef tag_value = LLVMConstInt(union_type->data.unionation.tag_type->type_ref,
    +                type_union_field->value, false);
    +        gen_store_untyped(g, tag_value, tag_field_ptr, 0, false);
    +
    +        uncasted_union_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr,
    +                (unsigned)union_type->data.unionation.gen_union_index, "");
    +    } else {
    +        uncasted_union_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, (unsigned)0, "");
    +    }
    +
    +    LLVMValueRef field_ptr = LLVMBuildBitCast(g->builder, uncasted_union_ptr, ptr_type->type_ref, "");
    +    LLVMValueRef value = ir_llvm_value(g, instruction->init_value);
    +
    +    gen_assign_raw(g, field_ptr, ptr_type, value);
    +
    +    return instruction->tmp_ptr;
    +}
    +
     static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *executable,
             IrInstructionContainerInitList *instruction)
     {
    @@ -3486,6 +3568,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
                 return ir_render_struct_field_ptr(g, executable, (IrInstructionStructFieldPtr *)instruction);
             case IrInstructionIdEnumFieldPtr:
                 return ir_render_enum_field_ptr(g, executable, (IrInstructionEnumFieldPtr *)instruction);
    +        case IrInstructionIdUnionFieldPtr:
    +            return ir_render_union_field_ptr(g, executable, (IrInstructionUnionFieldPtr *)instruction);
             case IrInstructionIdAsm:
                 return ir_render_asm(g, executable, (IrInstructionAsm *)instruction);
             case IrInstructionIdTestNonNull:
    @@ -3544,6 +3628,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
                 return ir_render_init_enum(g, executable, (IrInstructionInitEnum *)instruction);
             case IrInstructionIdStructInit:
                 return ir_render_struct_init(g, executable, (IrInstructionStructInit *)instruction);
    +        case IrInstructionIdUnionInit:
    +            return ir_render_union_init(g, executable, (IrInstructionUnionInit *)instruction);
             case IrInstructionIdPtrCast:
                 return ir_render_ptr_cast(g, executable, (IrInstructionPtrCast *)instruction);
             case IrInstructionIdBitCast:
    @@ -3595,6 +3681,7 @@ static void ir_render(CodeGen *g, FnTableEntry *fn_entry) {
     
     static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *struct_const_val, size_t field_index);
     static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ConstExprValue *array_const_val, size_t index);
    +static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *array_const_val);
     
     static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent *parent) {
         switch (parent->id) {
    @@ -3608,6 +3695,8 @@ static LLVMValueRef gen_parent_ptr(CodeGen *g, ConstExprValue *val, ConstParent
             case ConstParentIdArray:
                 return gen_const_ptr_array_recursive(g, parent->data.p_array.array_val,
                         parent->data.p_array.elem_index);
    +        case ConstParentIdUnion:
    +            return gen_const_ptr_union_recursive(g, parent->data.p_union.union_val);
         }
         zig_unreachable();
     }
    @@ -3637,6 +3726,18 @@ static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ConstExprValue *s
         return LLVMConstInBoundsGEP(base_ptr, indices, 2);
     }
     
    +static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ConstExprValue *union_const_val) {
    +    ConstParent *parent = &union_const_val->data.x_union.parent;
    +    LLVMValueRef base_ptr = gen_parent_ptr(g, union_const_val, parent);
    +
    +    TypeTableEntry *u32 = g->builtin_types.entry_u32;
    +    LLVMValueRef indices[] = {
    +        LLVMConstNull(u32->type_ref),
    +        LLVMConstInt(u32->type_ref, 0, false),
    +    };
    +    return LLVMConstInBoundsGEP(base_ptr, indices, 2);
    +}
    +
     static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, ConstExprValue *const_val) {
         switch (const_val->special) {
             case ConstValSpecialRuntime:
    @@ -3872,10 +3973,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
                         return LLVMConstNamedStruct(type_entry->type_ref, fields, type_entry->data.structure.gen_field_count);
                     }
                 }
    -        case TypeTableEntryIdUnion:
    -            {
    -                zig_panic("TODO");
    -            }
             case TypeTableEntryIdArray:
                 {
                     uint64_t len = type_entry->data.array.len;
    @@ -3898,6 +3995,55 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
                         return LLVMConstArray(element_type_ref, values, (unsigned)len);
                     }
                 }
    +        case TypeTableEntryIdUnion:
    +            {
    +                LLVMTypeRef union_type_ref = type_entry->data.unionation.union_type_ref;
    +                ConstExprValue *payload_value = const_val->data.x_union.payload;
    +                assert(payload_value != nullptr);
    +
    +                if (!type_has_bits(payload_value->type)) {
    +                    return LLVMGetUndef(union_type_ref);
    +                }
    +
    +                uint64_t field_type_bytes = LLVMStoreSizeOfType(g->target_data_ref, payload_value->type->type_ref);
    +                uint64_t pad_bytes = type_entry->data.unionation.union_size_bytes - field_type_bytes;
    +                LLVMValueRef correctly_typed_value = gen_const_val(g, payload_value);
    +                bool make_unnamed_struct = is_llvm_value_unnamed_type(payload_value->type, correctly_typed_value) ||
    +                    payload_value->type != type_entry->data.unionation.most_aligned_union_member;
    +
    +                LLVMValueRef union_value_ref;
    +                {
    +                    if (pad_bytes == 0) {
    +                        union_value_ref = correctly_typed_value;
    +                    } else {
    +                        LLVMValueRef fields[2];
    +                        fields[0] = correctly_typed_value;
    +                        fields[1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), (unsigned)pad_bytes));
    +                        if (make_unnamed_struct || type_entry->data.unionation.gen_tag_index != SIZE_MAX) {
    +                            union_value_ref = LLVMConstStruct(fields, 2, false);
    +                        } else {
    +                            union_value_ref = LLVMConstNamedStruct(union_type_ref, fields, 2);
    +                        }
    +                    }
    +                }
    +
    +                if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) {
    +                    return union_value_ref;
    +                }
    +
    +                LLVMValueRef tag_value = LLVMConstInt(type_entry->data.unionation.tag_type->type_ref, const_val->data.x_union.tag, false);
    +
    +                LLVMValueRef fields[2];
    +                fields[type_entry->data.unionation.gen_union_index] = union_value_ref;
    +                fields[type_entry->data.unionation.gen_tag_index] = tag_value;
    +
    +                if (make_unnamed_struct) {
    +                    return LLVMConstStruct(fields, 2, false);
    +                } else {
    +                    return LLVMConstNamedStruct(type_entry->type_ref, fields, 2);
    +                }
    +
    +            }
             case TypeTableEntryIdEnum:
                 {
                     LLVMTypeRef tag_type_ref = type_entry->data.enumeration.tag_type->type_ref;
    @@ -4376,6 +4522,9 @@ static void do_code_gen(CodeGen *g) {
                 } else if (instruction->id == IrInstructionIdStructInit) {
                     IrInstructionStructInit *struct_init_instruction = (IrInstructionStructInit *)instruction;
                     slot = &struct_init_instruction->tmp_ptr;
    +            } else if (instruction->id == IrInstructionIdUnionInit) {
    +                IrInstructionUnionInit *union_init_instruction = (IrInstructionUnionInit *)instruction;
    +                slot = &union_init_instruction->tmp_ptr;
                 } else if (instruction->id == IrInstructionIdCall) {
                     IrInstructionCall *call_instruction = (IrInstructionCall *)instruction;
                     slot = &call_instruction->tmp_ptr;
    @@ -5204,7 +5353,7 @@ static void init(CodeGen *g) {
         define_builtin_compile_vars(g);
     }
     
    -void codegen_parsec(CodeGen *g, Buf *full_path) {
    +void codegen_translate_c(CodeGen *g, Buf *full_path) {
         find_libc_include_path(g);
     
         Buf *src_basename = buf_alloc();
    diff --git a/src/codegen.hpp b/src/codegen.hpp
    index b71a7fa651..b29cadee55 100644
    --- a/src/codegen.hpp
    +++ b/src/codegen.hpp
    @@ -56,7 +56,7 @@ PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir,
     void codegen_add_assembly(CodeGen *g, Buf *path);
     void codegen_add_object(CodeGen *g, Buf *object_path);
     
    -void codegen_parsec(CodeGen *g, Buf *path);
    +void codegen_translate_c(CodeGen *g, Buf *path);
     
     
     #endif
    diff --git a/src/ir.cpp b/src/ir.cpp
    index fdaced6806..7c15b48bee 100644
    --- a/src/ir.cpp
    +++ b/src/ir.cpp
    @@ -11,7 +11,7 @@
     #include "ir.hpp"
     #include "ir_print.hpp"
     #include "os.hpp"
    -#include "parsec.hpp"
    +#include "translate_c.hpp"
     #include "range_set.hpp"
     #include "softfloat.hpp"
     
    @@ -227,6 +227,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumFieldPtr *)
         return IrInstructionIdEnumFieldPtr;
     }
     
    +static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionFieldPtr *) {
    +    return IrInstructionIdUnionFieldPtr;
    +}
    +
     static constexpr IrInstructionId ir_instruction_id(IrInstructionElemPtr *) {
         return IrInstructionIdElemPtr;
     }
    @@ -351,6 +355,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionStructInit *) {
         return IrInstructionIdStructInit;
     }
     
    +static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionInit *) {
    +    return IrInstructionIdUnionInit;
    +}
    +
     static constexpr IrInstructionId ir_instruction_id(IrInstructionMinValue *) {
         return IrInstructionIdMinValue;
     }
    @@ -922,6 +930,27 @@ static IrInstruction *ir_build_enum_field_ptr_from(IrBuilder *irb, IrInstruction
         return new_instruction;
     }
     
    +static IrInstruction *ir_build_union_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
    +    IrInstruction *union_ptr, TypeUnionField *field)
    +{
    +    IrInstructionUnionFieldPtr *instruction = ir_build_instruction(irb, scope, source_node);
    +    instruction->union_ptr = union_ptr;
    +    instruction->field = field;
    +
    +    ir_ref_instruction(union_ptr, irb->current_basic_block);
    +
    +    return &instruction->base;
    +}
    +
    +static IrInstruction *ir_build_union_field_ptr_from(IrBuilder *irb, IrInstruction *old_instruction,
    +    IrInstruction *union_ptr, TypeUnionField *type_union_field)
    +{
    +    IrInstruction *new_instruction = ir_build_union_field_ptr(irb, old_instruction->scope,
    +            old_instruction->source_node, union_ptr, type_union_field);
    +    ir_link_new_instruction(new_instruction, old_instruction);
    +    return new_instruction;
    +}
    +
     static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node,
             FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,
             bool is_comptime, bool is_inline)
    @@ -1112,6 +1141,28 @@ static IrInstruction *ir_build_struct_init_from(IrBuilder *irb, IrInstruction *o
         return new_instruction;
     }
     
    +static IrInstruction *ir_build_union_init(IrBuilder *irb, Scope *scope, AstNode *source_node,
    +        TypeTableEntry *union_type, TypeUnionField *field, IrInstruction *init_value)
    +{
    +    IrInstructionUnionInit *union_init_instruction = ir_build_instruction(irb, scope, source_node);
    +    union_init_instruction->union_type = union_type;
    +    union_init_instruction->field = field;
    +    union_init_instruction->init_value = init_value;
    +
    +    ir_ref_instruction(init_value, irb->current_basic_block);
    +
    +    return &union_init_instruction->base;
    +}
    +
    +static IrInstruction *ir_build_union_init_from(IrBuilder *irb, IrInstruction *old_instruction,
    +        TypeTableEntry *union_type, TypeUnionField *field, IrInstruction *init_value)
    +{
    +    IrInstruction *new_instruction = ir_build_union_init(irb, old_instruction->scope,
    +            old_instruction->source_node, union_type, field, init_value);
    +    ir_link_new_instruction(new_instruction, old_instruction);
    +    return new_instruction;
    +}
    +
     static IrInstruction *ir_build_unreachable(IrBuilder *irb, Scope *scope, AstNode *source_node) {
         IrInstructionUnreachable *unreachable_instruction =
             ir_build_instruction(irb, scope, source_node);
    @@ -2422,6 +2473,13 @@ static IrInstruction *ir_instruction_enumfieldptr_get_dep(IrInstructionEnumField
         }
     }
     
    +static IrInstruction *ir_instruction_unionfieldptr_get_dep(IrInstructionUnionFieldPtr *instruction, size_t index) {
    +    switch (index) {
    +        case 0: return instruction->union_ptr;
    +        default: return nullptr;
    +    }
    +}
    +
     static IrInstruction *ir_instruction_elemptr_get_dep(IrInstructionElemPtr *instruction, size_t index) {
         switch (index) {
             case 0: return instruction->array_ptr;
    @@ -2485,6 +2543,13 @@ static IrInstruction *ir_instruction_structinit_get_dep(IrInstructionStructInit
         return nullptr;
     }
     
    +static IrInstruction *ir_instruction_unioninit_get_dep(IrInstructionUnionInit *instruction, size_t index) {
    +    switch (index) {
    +        case 0: return instruction->init_value;
    +        default: return nullptr;
    +    }
    +}
    +
     static IrInstruction *ir_instruction_unreachable_get_dep(IrInstructionUnreachable *instruction, size_t index) {
         return nullptr;
     }
    @@ -3099,6 +3164,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
                 return ir_instruction_structfieldptr_get_dep((IrInstructionStructFieldPtr *) instruction, index);
             case IrInstructionIdEnumFieldPtr:
                 return ir_instruction_enumfieldptr_get_dep((IrInstructionEnumFieldPtr *) instruction, index);
    +        case IrInstructionIdUnionFieldPtr:
    +            return ir_instruction_unionfieldptr_get_dep((IrInstructionUnionFieldPtr *) instruction, index);
             case IrInstructionIdElemPtr:
                 return ir_instruction_elemptr_get_dep((IrInstructionElemPtr *) instruction, index);
             case IrInstructionIdVarPtr:
    @@ -3117,6 +3184,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
                 return ir_instruction_containerinitfields_get_dep((IrInstructionContainerInitFields *) instruction, index);
             case IrInstructionIdStructInit:
                 return ir_instruction_structinit_get_dep((IrInstructionStructInit *) instruction, index);
    +        case IrInstructionIdUnionInit:
    +            return ir_instruction_unioninit_get_dep((IrInstructionUnionInit *) instruction, index);
             case IrInstructionIdUnreachable:
                 return ir_instruction_unreachable_get_dep((IrInstructionUnreachable *) instruction, index);
             case IrInstructionIdTypeOf:
    @@ -6233,8 +6302,9 @@ static Buf *get_anon_type_name(CodeGen *codegen, IrExecutable *exec, const char
                 buf_appendf(name, ")");
                 return name;
             } else {
    +            //Note: C-imports do not have valid location information
                 return buf_sprintf("(anonymous %s at %s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ")", kind_name,
    -                buf_ptr(source_node->owner->path), source_node->line + 1, source_node->column + 1);
    +                (source_node->owner->path != nullptr) ? buf_ptr(source_node->owner->path) : "(null)", source_node->line + 1, source_node->column + 1);
             }
         }
     }
    @@ -6263,6 +6333,9 @@ static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope,
         }
         irb->codegen->resolve_queue.append(&tld_container->base);
     
    +    // Add this to the list to mark as invalid if analyzing this exec fails.
    +    irb->exec->tld_list.append(&tld_container->base);
    +
         return ir_build_const_type(irb, parent_scope, node, container_type);
     }
     
    @@ -6485,6 +6558,20 @@ static bool ir_goto_pass2(IrBuilder *irb) {
         return true;
     }
     
    +static void invalidate_exec(IrExecutable *exec) {
    +    if (exec->invalid)
    +        return;
    +
    +    exec->invalid = true;
    +
    +    for (size_t i = 0; i < exec->tld_list.length; i += 1) {
    +        exec->tld_list.items[i]->resolution = TldResolutionInvalid;
    +    }
    +
    +    if (exec->source_exec != nullptr)
    +        invalidate_exec(exec->source_exec);
    +}
    +
     bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_executable) {
         assert(node->owner);
     
    @@ -6508,7 +6595,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
         }
     
         if (!ir_goto_pass2(irb)) {
    -        irb->exec->invalid = true;
    +        invalidate_exec(ir_executable);
             return false;
         }
     
    @@ -6534,7 +6621,7 @@ static void add_call_stack_errors(CodeGen *codegen, IrExecutable *exec, ErrorMsg
     }
     
     static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutable *exec, AstNode *source_node, Buf *msg) {
    -    exec->invalid = true;
    +    invalidate_exec(exec);
         ErrorMsg *err_msg = add_node_error(codegen, source_node, msg);
         if (exec->parent_exec) {
             add_call_stack_errors(codegen, exec, err_msg, 10);
    @@ -7897,6 +7984,9 @@ static IrInstruction *ir_get_const_ptr(IrAnalyze *ira, IrInstruction *instructio
             ConstExprValue *const_val = &const_instr->value;
             const_val->type = pointee_type;
             type_ensure_zero_bits_known(ira->codegen, type_entry);
    +        if (type_is_invalid(type_entry)) {
    +            return ira->codegen->invalid_instruction;
    +        }
             const_val->data.x_type = get_pointer_to_type_extra(ira->codegen, type_entry,
                     ptr_is_const, ptr_is_volatile, get_abi_alignment(ira->codegen, type_entry), 0, 0);
             return const_instr;
    @@ -7984,6 +8074,7 @@ IrInstruction *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node
         IrExecutable analyzed_executable = {0};
         analyzed_executable.source_node = source_node;
         analyzed_executable.parent_exec = parent_exec;
    +    analyzed_executable.source_exec = &ir_executable;
         analyzed_executable.name = exec_name;
         analyzed_executable.is_inline = true;
         analyzed_executable.fn_entry = fn_entry;
    @@ -10279,30 +10370,40 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
     
         bool is_const = (var->value->type->id == TypeTableEntryIdMetaType) ? is_const_ptr : var->src_is_const;
         bool is_volatile = (var->value->type->id == TypeTableEntryIdMetaType) ? is_volatile_ptr : false;
    -    if (mem_slot && mem_slot->special != ConstValSpecialRuntime) {
    -        ConstPtrMut ptr_mut;
    -        if (comptime_var_mem) {
    -            ptr_mut = ConstPtrMutComptimeVar;
    -        } else if (var->gen_is_const) {
    -            ptr_mut = ConstPtrMutComptimeConst;
    -        } else {
    -            assert(!comptime_var_mem);
    -            ptr_mut = ConstPtrMutRuntimeVar;
    +    if (mem_slot != nullptr) {
    +        switch (mem_slot->special) {
    +            case ConstValSpecialRuntime:
    +                goto no_mem_slot;
    +            case ConstValSpecialStatic: // fallthrough
    +            case ConstValSpecialUndef: {
    +                ConstPtrMut ptr_mut;
    +                if (comptime_var_mem) {
    +                    ptr_mut = ConstPtrMutComptimeVar;
    +                } else if (var->gen_is_const) {
    +                    ptr_mut = ConstPtrMutComptimeConst;
    +                } else {
    +                    assert(!comptime_var_mem);
    +                    ptr_mut = ConstPtrMutRuntimeVar;
    +                }
    +                return ir_get_const_ptr(ira, instruction, mem_slot, var->value->type,
    +                        ptr_mut, is_const, is_volatile, var->align_bytes);
    +            }
             }
    -        return ir_get_const_ptr(ira, instruction, mem_slot, var->value->type,
    -                ptr_mut, is_const, is_volatile, var->align_bytes);
    -    } else {
    -        IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb,
    -                instruction->scope, instruction->source_node, var, is_const, is_volatile);
    -        var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type,
    -                var->src_is_const, is_volatile, var->align_bytes, 0, 0);
    -        type_ensure_zero_bits_known(ira->codegen, var->value->type);
    -
    -        bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr);
    -        var_ptr_instruction->value.data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack;
    -
    -        return var_ptr_instruction;
    +        zig_unreachable();
         }
    +
    +no_mem_slot:
    +
    +    IrInstruction *var_ptr_instruction = ir_build_var_ptr(&ira->new_irb,
    +            instruction->scope, instruction->source_node, var, is_const, is_volatile);
    +    var_ptr_instruction->value.type = get_pointer_to_type_extra(ira->codegen, var->value->type,
    +            var->src_is_const, is_volatile, var->align_bytes, 0, 0);
    +    type_ensure_zero_bits_known(ira->codegen, var->value->type);
    +
    +    bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr);
    +    var_ptr_instruction->value.data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack;
    +
    +    return var_ptr_instruction;
     }
     
     static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction,
    @@ -10408,10 +10509,11 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
                 result = ir_eval_const_value(ira->codegen, exec_scope, body_node, return_type,
                     ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, fn_entry,
                     nullptr, call_instruction->base.source_node, nullptr, ira->new_irb.exec);
    -            if (type_is_invalid(result->value.type))
    -                return ira->codegen->builtin_types.entry_invalid;
     
                 ira->codegen->memoized_fn_eval_table.put(exec_scope, result);
    +
    +            if (type_is_invalid(result->value.type))
    +                return ira->codegen->builtin_types.entry_invalid;
             }
     
             ConstExprValue *out_val = ir_build_const_from(ira, &call_instruction->base);
    @@ -11417,8 +11519,20 @@ static TypeTableEntry *ir_analyze_container_member_access_inner(IrAnalyze *ira,
                 return ir_analyze_ref(ira, &field_ptr_instruction->base, bound_fn_value, true, false);
             }
         }
    +    const char *prefix_name;
    +    if (is_slice(bare_struct_type)) {
    +        prefix_name = "";
    +    } else if (bare_struct_type->id == TypeTableEntryIdStruct) {
    +        prefix_name = "struct ";
    +    } else if (bare_struct_type->id == TypeTableEntryIdEnum) {
    +        prefix_name = "enum ";
    +    } else if (bare_struct_type->id == TypeTableEntryIdUnion) {
    +        prefix_name = "union ";
    +    } else {
    +        prefix_name = "";
    +    }
         ir_add_error_node(ira, field_ptr_instruction->base.source_node,
    -        buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&bare_struct_type->name)));
    +        buf_sprintf("no member named '%s' in %s'%s'", buf_ptr(field_name), prefix_name, buf_ptr(&bare_struct_type->name)));
         return ira->codegen->builtin_types.entry_invalid;
     }
     
    @@ -11428,14 +11542,13 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field
     {
         TypeTableEntry *bare_type = container_ref_type(container_type);
         ensure_complete_type(ira->codegen, bare_type);
    +    if (type_is_invalid(bare_type))
    +        return ira->codegen->builtin_types.entry_invalid;
     
         assert(container_ptr->value.type->id == TypeTableEntryIdPointer);
         bool is_const = container_ptr->value.type->data.pointer.is_const;
         bool is_volatile = container_ptr->value.type->data.pointer.is_volatile;
         if (bare_type->id == TypeTableEntryIdStruct) {
    -        if (bare_type->data.structure.is_invalid)
    -            return ira->codegen->builtin_types.entry_invalid;
    -
             TypeStructField *field = find_struct_type_field(bare_type, field_name);
             if (field) {
                 bool is_packed = (bare_type->data.structure.layout == ContainerLayoutPacked);
    @@ -11476,9 +11589,6 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field
                     field_ptr_instruction, container_ptr, container_type);
             }
         } else if (bare_type->id == TypeTableEntryIdEnum) {
    -        if (bare_type->data.enumeration.is_invalid)
    -            return ira->codegen->builtin_types.entry_invalid;
    -
             TypeEnumField *field = find_enum_type_field(bare_type, field_name);
             if (field) {
                 ir_build_enum_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field);
    @@ -11489,7 +11599,15 @@ static TypeTableEntry *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field
                     field_ptr_instruction, container_ptr, container_type);
             }
         } else if (bare_type->id == TypeTableEntryIdUnion) {
    -        zig_panic("TODO");
    +        TypeUnionField *field = find_union_type_field(bare_type, field_name);
    +        if (field) {
    +            ir_build_union_field_ptr_from(&ira->new_irb, &field_ptr_instruction->base, container_ptr, field);
    +            return get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile,
    +                    get_abi_alignment(ira->codegen, field->type_entry), 0, 0);
    +        } else {
    +            return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
    +                field_ptr_instruction, container_ptr, container_type);
    +        }
         } else {
             zig_unreachable();
         }
    @@ -13033,9 +13151,71 @@ static TypeTableEntry *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionR
         return ir_analyze_ref(ira, &ref_instruction->base, value, ref_instruction->is_const, ref_instruction->is_volatile);
     }
     
    +static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, IrInstruction *instruction,
    +    TypeTableEntry *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields)
    +{
    +    assert(container_type->id == TypeTableEntryIdUnion);
    +
    +    ensure_complete_type(ira->codegen, container_type);
    +
    +    if (instr_field_count != 1) {
    +        ir_add_error(ira, instruction,
    +            buf_sprintf("union initialization expects exactly one field"));
    +        return ira->codegen->builtin_types.entry_invalid;
    +    }
    +
    +    IrInstructionContainerInitFieldsField *field = &fields[0];
    +    IrInstruction *field_value = field->value->other;
    +    if (type_is_invalid(field_value->value.type))
    +        return ira->codegen->builtin_types.entry_invalid;
    +
    +    TypeUnionField *type_field = find_union_type_field(container_type, field->name);
    +    if (!type_field) {
    +        ir_add_error_node(ira, field->source_node,
    +            buf_sprintf("no member named '%s' in union '%s'",
    +                buf_ptr(field->name), buf_ptr(&container_type->name)));
    +        return ira->codegen->builtin_types.entry_invalid;
    +    }
    +
    +    if (type_is_invalid(type_field->type_entry))
    +        return ira->codegen->builtin_types.entry_invalid;
    +
    +    IrInstruction *casted_field_value = ir_implicit_cast(ira, field_value, type_field->type_entry);
    +    if (casted_field_value == ira->codegen->invalid_instruction)
    +        return ira->codegen->builtin_types.entry_invalid;
    +
    +    bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope);
    +    if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime) {
    +        ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk);
    +        if (!field_val)
    +            return ira->codegen->builtin_types.entry_invalid;
    +
    +        ConstExprValue *out_val = ir_build_const_from(ira, instruction);
    +        out_val->data.x_union.payload = field_val;
    +        out_val->data.x_union.tag = type_field->value;
    +
    +        ConstParent *parent = get_const_val_parent(ira->codegen, field_val);
    +        if (parent != nullptr) {
    +            parent->id = ConstParentIdUnion;
    +            parent->data.p_union.union_val = out_val;
    +        }
    +
    +        return container_type;
    +    }
    +
    +    IrInstruction *new_instruction = ir_build_union_init_from(&ira->new_irb, instruction,
    +        container_type, type_field, casted_field_value);
    +
    +    ir_add_alloca(ira, new_instruction, container_type);
    +    return container_type;
    +}
    +
     static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruction *instruction,
         TypeTableEntry *container_type, size_t instr_field_count, IrInstructionContainerInitFieldsField *fields)
     {
    +    if (container_type->id == TypeTableEntryIdUnion) {
    +        return ir_analyze_container_init_fields_union(ira, instruction, container_type, instr_field_count, fields);
    +    }
         if (container_type->id != TypeTableEntryIdStruct || is_slice(container_type)) {
             ir_add_error(ira, instruction,
                 buf_sprintf("type '%s' does not support struct initialization syntax",
    @@ -13043,8 +13223,7 @@ static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstru
             return ira->codegen->builtin_types.entry_invalid;
         }
     
    -    if (!type_is_complete(container_type))
    -        resolve_container_type(ira->codegen, container_type);
    +    ensure_complete_type(ira->codegen, container_type);
     
         size_t actual_field_count = container_type->data.structure.src_field_count;
     
    @@ -13070,7 +13249,7 @@ static TypeTableEntry *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstru
             TypeStructField *type_field = find_struct_type_field(container_type, field->name);
             if (!type_field) {
                 ir_add_error_node(ira, field->source_node,
    -                buf_sprintf("no member named '%s' in '%s'",
    +                buf_sprintf("no member named '%s' in struct '%s'",
                         buf_ptr(field->name), buf_ptr(&container_type->name)));
                 return ira->codegen->builtin_types.entry_invalid;
             }
    @@ -15657,8 +15836,10 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             case IrInstructionIdIntToErr:
             case IrInstructionIdErrToInt:
             case IrInstructionIdStructInit:
    +        case IrInstructionIdUnionInit:
             case IrInstructionIdStructFieldPtr:
             case IrInstructionIdEnumFieldPtr:
    +        case IrInstructionIdUnionFieldPtr:
             case IrInstructionIdInitEnum:
             case IrInstructionIdMaybeWrap:
             case IrInstructionIdErrWrapCode:
    @@ -15968,6 +16149,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
             case IrInstructionIdContainerInitList:
             case IrInstructionIdContainerInitFields:
             case IrInstructionIdStructInit:
    +        case IrInstructionIdUnionInit:
             case IrInstructionIdFieldPtr:
             case IrInstructionIdElemPtr:
             case IrInstructionIdVarPtr:
    @@ -15977,6 +16159,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
             case IrInstructionIdArrayLen:
             case IrInstructionIdStructFieldPtr:
             case IrInstructionIdEnumFieldPtr:
    +        case IrInstructionIdUnionFieldPtr:
             case IrInstructionIdArrayType:
             case IrInstructionIdSliceType:
             case IrInstructionIdSizeOf:
    diff --git a/src/ir_print.cpp b/src/ir_print.cpp
    index 1c60d68628..55ad3ceb6c 100644
    --- a/src/ir_print.cpp
    +++ b/src/ir_print.cpp
    @@ -290,6 +290,15 @@ static void ir_print_struct_init(IrPrint *irp, IrInstructionStructInit *instruct
         fprintf(irp->f, "} // struct init");
     }
     
    +static void ir_print_union_init(IrPrint *irp, IrInstructionUnionInit *instruction) {
    +    Buf *field_name = instruction->field->name;
    +
    +    fprintf(irp->f, "%s {", buf_ptr(&instruction->union_type->name));
    +    fprintf(irp->f, ".%s = ", buf_ptr(field_name));
    +    ir_print_other_instruction(irp, instruction->init_value);
    +    fprintf(irp->f, "} // union init");
    +}
    +
     static void ir_print_unreachable(IrPrint *irp, IrInstructionUnreachable *instruction) {
         fprintf(irp->f, "unreachable");
     }
    @@ -359,6 +368,13 @@ static void ir_print_enum_field_ptr(IrPrint *irp, IrInstructionEnumFieldPtr *ins
         fprintf(irp->f, ")");
     }
     
    +static void ir_print_union_field_ptr(IrPrint *irp, IrInstructionUnionFieldPtr *instruction) {
    +    fprintf(irp->f, "@UnionFieldPtr(&");
    +    ir_print_other_instruction(irp, instruction->union_ptr);
    +    fprintf(irp->f, ".%s", buf_ptr(instruction->field->name));
    +    fprintf(irp->f, ")");
    +}
    +
     static void ir_print_set_debug_safety(IrPrint *irp, IrInstructionSetDebugSafety *instruction) {
         fprintf(irp->f, "@setDebugSafety(");
         ir_print_other_instruction(irp, instruction->scope_value);
    @@ -1023,6 +1039,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
             case IrInstructionIdStructInit:
                 ir_print_struct_init(irp, (IrInstructionStructInit *)instruction);
                 break;
    +        case IrInstructionIdUnionInit:
    +            ir_print_union_init(irp, (IrInstructionUnionInit *)instruction);
    +            break;
             case IrInstructionIdUnreachable:
                 ir_print_unreachable(irp, (IrInstructionUnreachable *)instruction);
                 break;
    @@ -1056,6 +1075,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
             case IrInstructionIdEnumFieldPtr:
                 ir_print_enum_field_ptr(irp, (IrInstructionEnumFieldPtr *)instruction);
                 break;
    +        case IrInstructionIdUnionFieldPtr:
    +            ir_print_union_field_ptr(irp, (IrInstructionUnionFieldPtr *)instruction);
    +            break;
             case IrInstructionIdSetDebugSafety:
                 ir_print_set_debug_safety(irp, (IrInstructionSetDebugSafety *)instruction);
                 break;
    diff --git a/src/main.cpp b/src/main.cpp
    index 13da71f9e8..60d2750bde 100644
    --- a/src/main.cpp
    +++ b/src/main.cpp
    @@ -23,7 +23,7 @@ static int usage(const char *arg0) {
             "  build-exe [source]           create executable from source or object files\n"
             "  build-lib [source]           create library from source or object files\n"
             "  build-obj [source]           create object from source or assembly\n"
    -        "  parsec [source]              convert c code to zig code\n"
    +        "  translate-c [source]         convert c code to zig code\n"
             "  targets                      list available compilation targets\n"
             "  test [source]                create and run a test build\n"
             "  version                      print version number and exit\n"
    @@ -229,7 +229,7 @@ enum Cmd {
         CmdTest,
         CmdVersion,
         CmdZen,
    -    CmdParseC,
    +    CmdTranslateC,
         CmdTargets,
     };
     
    @@ -632,8 +632,8 @@ int main(int argc, char **argv) {
                     cmd = CmdVersion;
                 } else if (strcmp(arg, "zen") == 0) {
                     cmd = CmdZen;
    -            } else if (strcmp(arg, "parsec") == 0) {
    -                cmd = CmdParseC;
    +            } else if (strcmp(arg, "translate-c") == 0) {
    +                cmd = CmdTranslateC;
                 } else if (strcmp(arg, "test") == 0) {
                     cmd = CmdTest;
                     out_type = OutTypeExe;
    @@ -646,7 +646,7 @@ int main(int argc, char **argv) {
             } else {
                 switch (cmd) {
                     case CmdBuild:
    -                case CmdParseC:
    +                case CmdTranslateC:
                     case CmdTest:
                         if (!in_file) {
                             in_file = arg;
    @@ -703,13 +703,13 @@ int main(int argc, char **argv) {
     
         switch (cmd) {
         case CmdBuild:
    -    case CmdParseC:
    +    case CmdTranslateC:
         case CmdTest:
             {
                 if (cmd == CmdBuild && !in_file && objects.length == 0 && asm_files.length == 0) {
                     fprintf(stderr, "Expected source file argument or at least one --object or --assembly argument.\n");
                     return usage(arg0);
    -            } else if ((cmd == CmdParseC || cmd == CmdTest) && !in_file) {
    +            } else if ((cmd == CmdTranslateC || cmd == CmdTest) && !in_file) {
                     fprintf(stderr, "Expected source file argument.\n");
                     return usage(arg0);
                 } else if (cmd == CmdBuild && out_type == OutTypeObj && objects.length != 0) {
    @@ -719,7 +719,7 @@ int main(int argc, char **argv) {
     
                 assert(cmd != CmdBuild || out_type != OutTypeUnknown);
     
    -            bool need_name = (cmd == CmdBuild || cmd == CmdParseC);
    +            bool need_name = (cmd == CmdBuild || cmd == CmdTranslateC);
     
                 Buf *in_file_buf = nullptr;
     
    @@ -742,7 +742,7 @@ int main(int argc, char **argv) {
                     return usage(arg0);
                 }
     
    -            Buf *zig_root_source_file = (cmd == CmdParseC) ? nullptr : in_file_buf;
    +            Buf *zig_root_source_file = (cmd == CmdTranslateC) ? nullptr : in_file_buf;
     
                 Buf *full_cache_dir = buf_alloc();
                 os_path_resolve(buf_create_from_str("."),
    @@ -841,8 +841,8 @@ int main(int argc, char **argv) {
                     if (timing_info)
                         codegen_print_timing_report(g, stdout);
                     return EXIT_SUCCESS;
    -            } else if (cmd == CmdParseC) {
    -                codegen_parsec(g, in_file_buf);
    +            } else if (cmd == CmdTranslateC) {
    +                codegen_translate_c(g, in_file_buf);
                     ast_render(g, stdout, g->root_import->root, 4);
                     if (timing_info)
                         codegen_print_timing_report(g, stdout);
    diff --git a/src/parsec.cpp b/src/translate_c.cpp
    similarity index 67%
    rename from src/parsec.cpp
    rename to src/translate_c.cpp
    index 8053527eb7..dbf9613308 100644
    --- a/src/parsec.cpp
    +++ b/src/translate_c.cpp
    @@ -11,7 +11,7 @@
     #include "error.hpp"
     #include "ir.hpp"
     #include "os.hpp"
    -#include "parsec.hpp"
    +#include "translate_c.hpp"
     #include "parser.hpp"
     
     
    @@ -23,16 +23,52 @@
     
     using namespace clang;
     
    -struct MacroSymbol {
    -    Buf *name;
    -    Buf *value;
    -};
    -
     struct Alias {
         Buf *new_name;
         Buf *canon_name;
     };
     
    +enum TransScopeId {
    +    TransScopeIdSwitch,
    +    TransScopeIdVar,
    +    TransScopeIdBlock,
    +    TransScopeIdRoot,
    +    TransScopeIdWhile,
    +};
    +
    +struct TransScope {
    +    TransScopeId id;
    +    TransScope *parent;
    +};
    +
    +struct TransScopeSwitch {
    +    TransScope base;
    +    AstNode *switch_node;
    +    uint32_t case_index;
    +    bool found_default;
    +    Buf *end_label_name;
    +};
    +
    +struct TransScopeVar {
    +    TransScope base;
    +    Buf *c_name;
    +    Buf *zig_name;
    +};
    +
    +struct TransScopeBlock {
    +    TransScope base;
    +    AstNode *node;
    +};
    +
    +struct TransScopeRoot {
    +    TransScope base;
    +};
    +
    +struct TransScopeWhile {
    +    TransScope base;
    +    AstNode *node;
    +};
    +
     struct Context {
         ImportTableEntry *import;
         ZigList *errors;
    @@ -44,20 +80,47 @@ struct Context {
         HashMap global_table;
         SourceManager *source_manager;
         ZigList aliases;
    -    ZigList macro_symbols;
         AstNode *source_node;
         bool warnings_on;
     
         CodeGen *codegen;
         ASTContext *ctx;
     
    +    TransScopeRoot *global_scope;
         HashMap ptr_params;
     };
     
    +enum ResultUsed {
    +    ResultUsedNo,
    +    ResultUsedYes,
    +};
    +
    +enum TransLRValue {
    +    TransLValue,
    +    TransRValue,
    +};
    +
    +static TransScopeRoot *trans_scope_root_create(Context *c);
    +static TransScopeWhile *trans_scope_while_create(Context *c, TransScope *parent_scope);
    +static TransScopeBlock *trans_scope_block_create(Context *c, TransScope *parent_scope);
    +static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scope, Buf *wanted_name);
    +static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope);
    +
    +static TransScopeBlock *trans_scope_block_find(TransScope *scope);
    +static TransScopeSwitch *trans_scope_switch_find(TransScope *scope);
    +
     static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl);
     static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl);
     static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_decl);
     
    +static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
    +        ResultUsed result_used, TransLRValue lrval,
    +        AstNode **out_node, TransScope **out_child_scope,
    +        TransScope **out_node_scope);
    +static TransScope *trans_stmt(Context *c, TransScope *scope, const Stmt *stmt, AstNode **out_node);
    +static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr, TransLRValue lrval);
    +static AstNode *trans_qual_type(Context *c, QualType qt, const SourceLocation &source_loc);
    +
     
     ATTRIBUTE_PRINTF(3, 4)
     static void emit_warning(Context *c, const SourceLocation &sl, const char *format, ...) {
    @@ -70,7 +133,7 @@ static void emit_warning(Context *c, const SourceLocation &sl, const char *forma
         Buf *msg = buf_vprintf(format, ap);
         va_end(ap);
     
    -    StringRef filename = c->source_manager->getFilename(sl);
    +    StringRef filename = c->source_manager->getFilename(c->source_manager->getSpellingLoc(sl));
         const char *filename_bytes = (const char *)filename.bytes_begin();
         Buf *path;
         if (filename_bytes) {
    @@ -89,6 +152,19 @@ static void add_global_weak_alias(Context *c, Buf *new_name, Buf *canon_name) {
         alias->canon_name = canon_name;
     }
     
    +static Buf *trans_lookup_zig_symbol(Context *c, TransScope *scope, Buf *c_symbol_name) {
    +    while (scope != nullptr) {
    +        if (scope->id == TransScopeIdVar) {
    +            TransScopeVar *var_scope = (TransScopeVar *)scope;
    +            if (buf_eql_buf(var_scope->c_name, c_symbol_name)) {
    +                return var_scope->zig_name;
    +            }
    +        }
    +        scope = scope->parent;
    +    }
    +    return c_symbol_name;
    +}
    +
     static AstNode * trans_create_node(Context *c, NodeType id) {
         AstNode *node = allocate(1);
         node->type = id;
    @@ -165,8 +241,8 @@ static AstNode *trans_create_node_bin_op(Context *c, AstNode *lhs_node, BinOpTyp
         return node;
     }
     
    -static AstNode *maybe_suppress_result(Context *c, bool result_used, AstNode *node) {
    -    if (result_used) return node;
    +static AstNode *maybe_suppress_result(Context *c, ResultUsed result_used, AstNode *node) {
    +    if (result_used == ResultUsedYes) return node;
         return trans_create_node_bin_op(c,
             trans_create_node_symbol_str(c, "_"),
             BinOpTypeAssign,
    @@ -181,6 +257,24 @@ static AstNode *trans_create_node_addr_of(Context *c, bool is_const, bool is_vol
         return node;
     }
     
    +static AstNode *trans_create_node_goto(Context *c, Buf *label_name) {
    +    AstNode *goto_node = trans_create_node(c, NodeTypeGoto);
    +    goto_node->data.goto_expr.name = label_name;
    +    return goto_node;
    +}
    +
    +static AstNode *trans_create_node_label(Context *c, Buf *label_name) {
    +    AstNode *label_node = trans_create_node(c, NodeTypeLabel);
    +    label_node->data.label.name = label_name;
    +    return label_node;
    +}
    +
    +static AstNode *trans_create_node_bool(Context *c, bool value) {
    +    AstNode *bool_node = trans_create_node(c, NodeTypeBoolLiteral);
    +    bool_node->data.bool_literal.value = value;
    +    return bool_node;
    +}
    +
     static AstNode *trans_create_node_str_lit_c(Context *c, Buf *buf) {
         AstNode *node = trans_create_node(c, NodeTypeStringLiteral);
         node->data.string_literal.buf = buf;
    @@ -252,8 +346,7 @@ static AstNode *trans_create_node_var_decl_local(Context *c, bool is_const, Buf
         return trans_create_node_var_decl(c, VisibModPrivate, is_const, var_name, type_node, init_node);
     }
     
    -
    -static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, Buf *var_name, AstNode *src_proto_node) {
    +static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *ref_node, AstNode *src_proto_node) {
         AstNode *fn_def = trans_create_node(c, NodeTypeFnDef);
         AstNode *fn_proto = trans_create_node(c, NodeTypeFnProto);
         fn_proto->data.fn_proto.visib_mod = c->visib_mod;
    @@ -264,7 +357,7 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, Buf *var_n
         fn_def->data.fn_def.fn_proto = fn_proto;
         fn_proto->data.fn_proto.fn_def_node = fn_def;
     
    -    AstNode *unwrap_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, trans_create_node_symbol(c, var_name));
    +    AstNode *unwrap_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, ref_node);
         AstNode *fn_call_node = trans_create_node(c, NodeTypeFnCallExpr);
         fn_call_node->data.fn_call_expr.fn_ref_expr = unwrap_node;
     
    @@ -308,6 +401,9 @@ static AstNode *get_global(Context *c, Buf *name) {
             if (entry)
                 return entry->value;
         }
    +    if (c->codegen->primitive_type_table.maybe_get(name) != nullptr) {
    +        return trans_create_node_symbol(c, name);
    +    }
         return nullptr;
     }
     
    @@ -324,6 +420,10 @@ static AstNode *add_global_var(Context *c, Buf *var_name, AstNode *value_node) {
         return node;
     }
     
    +static Buf *string_ref_to_buf(StringRef string_ref) {
    +    return buf_create_from_mem((const char *)string_ref.bytes_begin(), string_ref.size());
    +}
    +
     static const char *decl_name(const Decl *decl) {
         const NamedDecl *named_decl = static_cast(decl);
         return (const char *)named_decl->getName().bytes_begin();
    @@ -337,32 +437,70 @@ static AstNode *trans_create_node_apint(Context *c, const llvm::APSInt &aps_int)
     
     }
     
    -static AstNode *trans_qual_type(Context *c, QualType qt, const SourceLocation &source_loc);
    +static const Type *qual_type_canon(QualType qt) {
    +    return qt.getCanonicalType().getTypePtr();
    +}
    +
    +static QualType get_expr_qual_type(Context *c, const Expr *expr) {
    +    // String literals in C are `char *` but they should really be `const char *`.
    +    if (expr->getStmtClass() == Stmt::ImplicitCastExprClass) {
    +        const ImplicitCastExpr *cast_expr = static_cast(expr);
    +        if (cast_expr->getCastKind() == CK_ArrayToPointerDecay) {
    +            const Expr *sub_expr = cast_expr->getSubExpr();
    +            if (sub_expr->getStmtClass() == Stmt::StringLiteralClass) {
    +                QualType array_qt = sub_expr->getType();
    +                const ArrayType *array_type = static_cast(array_qt.getTypePtr());
    +                QualType pointee_qt = array_type->getElementType();
    +                pointee_qt.addConst();
    +                return c->ctx->getPointerType(pointee_qt);
    +            }
    +        }
    +    }
    +    return expr->getType();
    +}
    +
    +static AstNode *get_expr_type(Context *c, const Expr *expr) {
    +    return trans_qual_type(c, get_expr_qual_type(c, expr), expr->getLocStart());
    +}
    +
    +static bool qual_types_equal(QualType t1, QualType t2) {
    +    if (t1.isConstQualified() != t2.isConstQualified()) {
    +        return false;
    +    }
    +    if (t1.isVolatileQualified() != t2.isVolatileQualified()) {
    +        return false;
    +    }
    +    if (t1.isRestrictQualified() != t2.isRestrictQualified()) {
    +        return false;
    +    }
    +    return t1.getTypePtr() == t2.getTypePtr();
    +}
     
     static bool is_c_void_type(AstNode *node) {
         return (node->type == NodeTypeSymbol && buf_eql_str(node->data.symbol_expr.symbol, "c_void"));
     }
     
    -static AstNode* trans_c_cast(Context *c, const SourceLocation &source_location, const QualType &qt, AstNode *expr) {
    -    // TODO: maybe widen to increase size
    -    // TODO: maybe bitcast to change sign
    -    // TODO: maybe truncate to reduce size
    -    return trans_create_node_fn_call_1(c, trans_qual_type(c, qt, source_location), expr);
    +static bool expr_types_equal(Context *c, const Expr *expr1, const Expr *expr2) {
    +    QualType t1 = get_expr_qual_type(c, expr1);
    +    QualType t2 = get_expr_qual_type(c, expr2);
    +
    +    return qual_types_equal(t1, t2);
     }
     
    -static bool qual_type_is_fn_ptr(Context *c, const QualType &qt) {
    -    const Type *ty = qt.getTypePtr();
    +static bool qual_type_is_ptr(QualType qt) {
    +    const Type *ty = qual_type_canon(qt);
    +    return ty->getTypeClass() == Type::Pointer;
    +}
    +
    +static bool qual_type_is_fn_ptr(Context *c, QualType qt) {
    +    const Type *ty = qual_type_canon(qt);
         if (ty->getTypeClass() != Type::Pointer) {
             return false;
         }
         const PointerType *pointer_ty = static_cast(ty);
         QualType child_qt = pointer_ty->getPointeeType();
         const Type *child_ty = child_qt.getTypePtr();
    -    if (child_ty->getTypeClass() != Type::Paren) {
    -        return false;
    -    }
    -    const ParenType *paren_ty = static_cast(child_ty);
    -    return paren_ty->getInnerType().getTypePtr()->getTypeClass() == Type::FunctionProto;
    +    return child_ty->getTypeClass() == Type::FunctionProto;
     }
     
     static uint32_t qual_type_int_bit_width(Context *c, const QualType &qt, const SourceLocation &source_loc) {
    @@ -455,17 +593,26 @@ static bool qual_type_child_is_fn_proto(const QualType &qt) {
         return false;
     }
     
    -static QualType resolve_any_typedef(Context *c, QualType qt) {
    -    const Type * ty = qt.getTypePtr();
    -    if (ty->getTypeClass() != Type::Typedef)
    -        return qt;
    -    const TypedefType *typedef_ty = static_cast(ty);
    -    const TypedefNameDecl *typedef_decl = typedef_ty->getDecl();
    -    return typedef_decl->getUnderlyingType();
    +static AstNode* trans_c_cast(Context *c, const SourceLocation &source_location, QualType dest_type,
    +        QualType src_type, AstNode *expr)
    +{
    +    if (qual_types_equal(dest_type, src_type)) {
    +        return expr;
    +    }
    +    if (qual_type_is_ptr(dest_type) && qual_type_is_ptr(src_type)) {
    +        AstNode *ptr_cast_node = trans_create_node_builtin_fn_call_str(c, "ptrCast");
    +        ptr_cast_node->data.fn_call_expr.params.append(trans_qual_type(c, dest_type, source_location));
    +        ptr_cast_node->data.fn_call_expr.params.append(expr);
    +        return ptr_cast_node;
    +    }
    +    // TODO: maybe widen to increase size
    +    // TODO: maybe bitcast to change sign
    +    // TODO: maybe truncate to reduce size
    +    return trans_create_node_fn_call_1(c, trans_qual_type(c, dest_type, source_location), expr);
     }
     
     static bool c_is_signed_integer(Context *c, QualType qt) {
    -    const Type *c_type = resolve_any_typedef(c, qt).getTypePtr();
    +    const Type *c_type = qual_type_canon(qt);
         if (c_type->getTypeClass() != Type::Builtin)
             return false;
         const BuiltinType *builtin_ty = static_cast(c_type);
    @@ -484,7 +631,7 @@ static bool c_is_signed_integer(Context *c, QualType qt) {
     }
     
     static bool c_is_unsigned_integer(Context *c, QualType qt) {
    -    const Type *c_type = resolve_any_typedef(c, qt).getTypePtr();
    +    const Type *c_type = qual_type_canon(qt);
         if (c_type->getTypeClass() != Type::Builtin)
             return false;
         const BuiltinType *builtin_ty = static_cast(c_type);
    @@ -531,18 +678,6 @@ static bool qual_type_has_wrapping_overflow(Context *c, QualType qt) {
         }
     }
     
    -enum TransLRValue {
    -    TransLValue,
    -    TransRValue,
    -};
    -
    -static AstNode *trans_stmt(Context *c, bool result_used, AstNode *block, Stmt *stmt, TransLRValue lrval);
    -static AstNode *const skip_add_to_block_node = (AstNode *) 0x2;
    -
    -static AstNode *trans_expr(Context *c, bool result_used, AstNode *block, Expr *expr, TransLRValue lrval) {
    -    return trans_stmt(c, result_used, block, expr, lrval);
    -}
    -
     static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &source_loc) {
         switch (ty->getTypeClass()) {
             case Type::Builtin:
    @@ -682,11 +817,10 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou
                     const ElaboratedType *elaborated_ty = static_cast(ty);
                     switch (elaborated_ty->getKeyword()) {
                         case ETK_Struct:
    -                        return trans_qual_type(c, elaborated_ty->getNamedType(), source_loc);
                         case ETK_Enum:
    +                    case ETK_Union:
                             return trans_qual_type(c, elaborated_ty->getNamedType(), source_loc);
                         case ETK_Interface:
    -                    case ETK_Union:
                         case ETK_Class:
                         case ETK_Typename:
                         case ETK_None:
    @@ -767,6 +901,11 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou
                             return nullptr;
                         }
                         // convert c_void to actual void (only for return type)
    +                    // we do want to look at the AstNode instead of QualType, because
    +                    // if they do something like:
    +                    //     typedef Foo void;
    +                    //     void foo(void) -> Foo;
    +                    // we want to keep the return type AST node.
                         if (is_c_void_type(proto_node->data.fn_proto.return_type)) {
                             proto_node->data.fn_proto.return_type = nullptr;
                         }
    @@ -884,33 +1023,47 @@ static AstNode *trans_qual_type(Context *c, QualType qt, const SourceLocation &s
         return trans_type(c, qt.getTypePtr(), source_loc);
     }
     
    -static AstNode *trans_compound_stmt(Context *c, AstNode *parent, CompoundStmt *stmt) {
    -    AstNode *child_block = trans_create_node(c, NodeTypeBlock);
    -    for (CompoundStmt::body_iterator it = stmt->body_begin(), end_it = stmt->body_end(); it != end_it; ++it) {
    -        AstNode *child_node = trans_stmt(c, false, child_block, *it, TransRValue);
    -        if (child_node == nullptr)
    -            return nullptr;
    -        if (child_node != skip_add_to_block_node)
    -            child_block->data.block.statements.append(child_node);
    +static int trans_compound_stmt_inline(Context *c, TransScope *scope, const CompoundStmt *stmt,
    +        AstNode *block_node, TransScope **out_node_scope)
    +{
    +    assert(block_node->type == NodeTypeBlock);
    +    for (CompoundStmt::const_body_iterator it = stmt->body_begin(), end_it = stmt->body_end(); it != end_it; ++it) {
    +        AstNode *child_node;
    +        scope = trans_stmt(c, scope, *it, &child_node);
    +        if (scope == nullptr)
    +            return ErrorUnexpected;
    +        if (child_node != nullptr)
    +            block_node->data.block.statements.append(child_node);
         }
    -    return child_block;
    +    if (out_node_scope != nullptr) {
    +        *out_node_scope = scope;
    +    }
    +    return ErrorNone;
     }
     
    -static AstNode *trans_return_stmt(Context *c, AstNode *block, ReturnStmt *stmt) {
    -    Expr *value_expr = stmt->getRetValue();
    -    if (value_expr == nullptr) {
    -        emit_warning(c, stmt->getLocStart(), "TODO handle C return void");
    +static AstNode *trans_compound_stmt(Context *c, TransScope *scope, const CompoundStmt *stmt,
    +        TransScope **out_node_scope)
    +{
    +    TransScopeBlock *child_scope_block = trans_scope_block_create(c, scope);
    +    if (trans_compound_stmt_inline(c, &child_scope_block->base, stmt, child_scope_block->node, out_node_scope))
             return nullptr;
    +    return child_scope_block->node;
    +}
    +
    +static AstNode *trans_return_stmt(Context *c, TransScope *scope, const ReturnStmt *stmt) {
    +    const Expr *value_expr = stmt->getRetValue();
    +    if (value_expr == nullptr) {
    +        return trans_create_node(c, NodeTypeReturnExpr);
         } else {
             AstNode *return_node = trans_create_node(c, NodeTypeReturnExpr);
    -        return_node->data.return_expr.expr = trans_expr(c, true, block, value_expr, TransRValue);
    +        return_node->data.return_expr.expr = trans_expr(c, ResultUsedYes, scope, value_expr, TransRValue);
             if (return_node->data.return_expr.expr == nullptr)
                 return nullptr;
             return return_node;
         }
     }
     
    -static AstNode *trans_integer_literal(Context *c, IntegerLiteral *stmt) {
    +static AstNode *trans_integer_literal(Context *c, const IntegerLiteral *stmt) {
         llvm::APSInt result;
         if (!stmt->EvaluateAsInt(result, *c->ctx)) {
             emit_warning(c, stmt->getLocStart(), "invalid integer literal");
    @@ -919,54 +1072,56 @@ static AstNode *trans_integer_literal(Context *c, IntegerLiteral *stmt) {
         return trans_create_node_apint(c, result);
     }
     
    -static AstNode *trans_conditional_operator(Context *c, bool result_used, AstNode *block, ConditionalOperator *stmt) {
    +static AstNode *trans_conditional_operator(Context *c, ResultUsed result_used, TransScope *scope,
    +        const ConditionalOperator *stmt)
    +{
         AstNode *node = trans_create_node(c, NodeTypeIfBoolExpr);
     
         Expr *cond_expr = stmt->getCond();
         Expr *true_expr = stmt->getTrueExpr();
         Expr *false_expr = stmt->getFalseExpr();
     
    -    node->data.if_bool_expr.condition = trans_expr(c, true, block, cond_expr, TransRValue);
    +    node->data.if_bool_expr.condition = trans_expr(c, ResultUsedYes, scope, cond_expr, TransRValue);
         if (node->data.if_bool_expr.condition == nullptr)
             return nullptr;
     
    -    node->data.if_bool_expr.then_block = trans_expr(c, result_used, block, true_expr, TransRValue);
    +    node->data.if_bool_expr.then_block = trans_expr(c, result_used, scope, true_expr, TransRValue);
         if (node->data.if_bool_expr.then_block == nullptr)
             return nullptr;
     
    -    node->data.if_bool_expr.else_node = trans_expr(c, result_used, block, false_expr, TransRValue);
    +    node->data.if_bool_expr.else_node = trans_expr(c, result_used, scope, false_expr, TransRValue);
         if (node->data.if_bool_expr.else_node == nullptr)
             return nullptr;
     
         return maybe_suppress_result(c, result_used, node);
     }
     
    -static AstNode *trans_create_bin_op(Context *c, AstNode *block, Expr *lhs, BinOpType bin_op, Expr *rhs) {
    +static AstNode *trans_create_bin_op(Context *c, TransScope *scope, Expr *lhs, BinOpType bin_op, Expr *rhs) {
         AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
         node->data.bin_op_expr.bin_op = bin_op;
     
    -    node->data.bin_op_expr.op1 = trans_expr(c, true, block, lhs, TransRValue);
    +    node->data.bin_op_expr.op1 = trans_expr(c, ResultUsedYes, scope, lhs, TransRValue);
         if (node->data.bin_op_expr.op1 == nullptr)
             return nullptr;
     
    -    node->data.bin_op_expr.op2 = trans_expr(c, true, block, rhs, TransRValue);
    +    node->data.bin_op_expr.op2 = trans_expr(c, ResultUsedYes, scope, rhs, TransRValue);
         if (node->data.bin_op_expr.op2 == nullptr)
             return nullptr;
     
         return node;
     }
     
    -static AstNode *trans_create_assign(Context *c, bool result_used, AstNode *block, Expr *lhs, Expr *rhs) {
    -    if (!result_used) {
    +static AstNode *trans_create_assign(Context *c, ResultUsed result_used, TransScope *scope, Expr *lhs, Expr *rhs) {
    +    if (result_used == ResultUsedNo) {
             // common case
             AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
             node->data.bin_op_expr.bin_op = BinOpTypeAssign;
     
    -        node->data.bin_op_expr.op1 = trans_expr(c, true, block, lhs, TransLValue);
    +        node->data.bin_op_expr.op1 = trans_expr(c, ResultUsedYes, scope, lhs, TransLValue);
             if (node->data.bin_op_expr.op1 == nullptr)
                 return nullptr;
     
    -        node->data.bin_op_expr.op2 = trans_expr(c, true, block, rhs, TransRValue);
    +        node->data.bin_op_expr.op2 = trans_expr(c, ResultUsedYes, scope, rhs, TransRValue);
             if (node->data.bin_op_expr.op2 == nullptr)
                 return nullptr;
     
    @@ -980,47 +1135,49 @@ static AstNode *trans_create_assign(Context *c, bool result_used, AstNode *block
             // zig:     _tmp
             // zig: }
     
    -        AstNode *child_block = trans_create_node(c, NodeTypeBlock);
    +        TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
     
             // const _tmp = rhs;
    -        AstNode *rhs_node = trans_expr(c, true, child_block, rhs, TransRValue);
    +        AstNode *rhs_node = trans_expr(c, ResultUsedYes, &child_scope->base, rhs, TransRValue);
             if (rhs_node == nullptr) return nullptr;
             // TODO: avoid name collisions with generated variable names
             Buf* tmp_var_name = buf_create_from_str("_tmp");
             AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, rhs_node);
    -        child_block->data.block.statements.append(tmp_var_decl);
    +        child_scope->node->data.block.statements.append(tmp_var_decl);
     
             // lhs = _tmp;
    -        AstNode *lhs_node = trans_expr(c, true, child_block, lhs, TransLValue);
    +        AstNode *lhs_node = trans_expr(c, ResultUsedYes, &child_scope->base, lhs, TransLValue);
             if (lhs_node == nullptr) return nullptr;
    -        child_block->data.block.statements.append(
    +        child_scope->node->data.block.statements.append(
                 trans_create_node_bin_op(c, lhs_node, BinOpTypeAssign,
                     trans_create_node_symbol(c, tmp_var_name)));
     
             // _tmp
    -        child_block->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name));
    -        child_block->data.block.last_statement_is_result_expression = true;
    +        child_scope->node->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name));
    +        child_scope->node->data.block.last_statement_is_result_expression = true;
     
    -        return child_block;
    +        return child_scope->node;
         }
     }
     
    -static AstNode *trans_create_shift_op(Context *c, AstNode *block, QualType result_type, Expr *lhs_expr, BinOpType bin_op, Expr *rhs_expr) {
    +static AstNode *trans_create_shift_op(Context *c, TransScope *scope, QualType result_type,
    +        Expr *lhs_expr, BinOpType bin_op, Expr *rhs_expr)
    +{
         const SourceLocation &rhs_location = rhs_expr->getLocStart();
         AstNode *rhs_type = qual_type_to_log2_int_ref(c, result_type, rhs_location);
         // lhs >> u5(rh)
     
    -    AstNode *lhs = trans_expr(c, true, block, lhs_expr, TransLValue);
    +    AstNode *lhs = trans_expr(c, ResultUsedYes, scope, lhs_expr, TransLValue);
         if (lhs == nullptr) return nullptr;
     
    -    AstNode *rhs = trans_expr(c, true, block, rhs_expr, TransRValue);
    +    AstNode *rhs = trans_expr(c, ResultUsedYes, scope, rhs_expr, TransRValue);
         if (rhs == nullptr) return nullptr;
         AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs);
     
         return trans_create_node_bin_op(c, lhs, bin_op, coerced_rhs);
     }
     
    -static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *block, BinaryOperator *stmt) {
    +static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransScope *scope, const BinaryOperator *stmt) {
         switch (stmt->getOpcode()) {
             case BO_PtrMemD:
                 emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_PtrMemD");
    @@ -1029,20 +1186,20 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo
                 emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_PtrMemI");
                 return nullptr;
             case BO_Mul:
    -            return trans_create_bin_op(c, block, stmt->getLHS(),
    +            return trans_create_bin_op(c, scope, stmt->getLHS(),
                     qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeMultWrap : BinOpTypeMult,
                     stmt->getRHS());
             case BO_Div:
                 if (qual_type_has_wrapping_overflow(c, stmt->getType())) {
                     // unsigned/float division uses the operator
    -                return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeDiv, stmt->getRHS());
    +                return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeDiv, stmt->getRHS());
                 } else {
                     // signed integer division uses @divTrunc
                     AstNode *fn_call = trans_create_node_builtin_fn_call_str(c, "divTrunc");
    -                AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue);
    +                AstNode *lhs = trans_expr(c, ResultUsedYes, scope, stmt->getLHS(), TransLValue);
                     if (lhs == nullptr) return nullptr;
                     fn_call->data.fn_call_expr.params.append(lhs);
    -                AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransLValue);
    +                AstNode *rhs = trans_expr(c, ResultUsedYes, scope, stmt->getRHS(), TransLValue);
                     if (rhs == nullptr) return nullptr;
                     fn_call->data.fn_call_expr.params.append(rhs);
                     return fn_call;
    @@ -1050,66 +1207,70 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo
             case BO_Rem:
                 if (qual_type_has_wrapping_overflow(c, stmt->getType())) {
                     // unsigned/float division uses the operator
    -                return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeMod, stmt->getRHS());
    +                return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeMod, stmt->getRHS());
                 } else {
                     // signed integer division uses @rem
                     AstNode *fn_call = trans_create_node_builtin_fn_call_str(c, "rem");
    -                AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue);
    +                AstNode *lhs = trans_expr(c, ResultUsedYes, scope, stmt->getLHS(), TransLValue);
                     if (lhs == nullptr) return nullptr;
                     fn_call->data.fn_call_expr.params.append(lhs);
    -                AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransLValue);
    +                AstNode *rhs = trans_expr(c, ResultUsedYes, scope, stmt->getRHS(), TransLValue);
                     if (rhs == nullptr) return nullptr;
                     fn_call->data.fn_call_expr.params.append(rhs);
                     return fn_call;
                 }
             case BO_Add:
    -            return trans_create_bin_op(c, block, stmt->getLHS(),
    +            return trans_create_bin_op(c, scope, stmt->getLHS(),
                     qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeAddWrap : BinOpTypeAdd,
                     stmt->getRHS());
             case BO_Sub:
    -            return trans_create_bin_op(c, block, stmt->getLHS(),
    +            return trans_create_bin_op(c, scope, stmt->getLHS(),
                     qual_type_has_wrapping_overflow(c, stmt->getType()) ? BinOpTypeSubWrap : BinOpTypeSub,
                     stmt->getRHS());
             case BO_Shl:
    -            return trans_create_shift_op(c, block, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftLeft, stmt->getRHS());
    +            return trans_create_shift_op(c, scope, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftLeft, stmt->getRHS());
             case BO_Shr:
    -            return trans_create_shift_op(c, block, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftRight, stmt->getRHS());
    +            return trans_create_shift_op(c, scope, stmt->getType(), stmt->getLHS(), BinOpTypeBitShiftRight, stmt->getRHS());
             case BO_LT:
    -            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpLessThan, stmt->getRHS());
    +            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpLessThan, stmt->getRHS());
             case BO_GT:
    -            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpGreaterThan, stmt->getRHS());
    +            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpGreaterThan, stmt->getRHS());
             case BO_LE:
    -            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpLessOrEq, stmt->getRHS());
    +            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpLessOrEq, stmt->getRHS());
             case BO_GE:
    -            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpGreaterOrEq, stmt->getRHS());
    +            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpGreaterOrEq, stmt->getRHS());
             case BO_EQ:
    -            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpEq, stmt->getRHS());
    +            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpEq, stmt->getRHS());
             case BO_NE:
    -            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeCmpNotEq, stmt->getRHS());
    +            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeCmpNotEq, stmt->getRHS());
             case BO_And:
    -            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBinAnd, stmt->getRHS());
    +            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBinAnd, stmt->getRHS());
             case BO_Xor:
    -            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBinXor, stmt->getRHS());
    +            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBinXor, stmt->getRHS());
             case BO_Or:
    -            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBinOr, stmt->getRHS());
    +            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBinOr, stmt->getRHS());
             case BO_LAnd:
    -            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBoolAnd, stmt->getRHS());
    +            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBoolAnd, stmt->getRHS());
             case BO_LOr:
                 // TODO: int vs bool
    -            return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBoolOr, stmt->getRHS());
    +            return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBoolOr, stmt->getRHS());
             case BO_Assign:
    -            return trans_create_assign(c, result_used, block, stmt->getLHS(), stmt->getRHS());
    +            return trans_create_assign(c, result_used, scope, stmt->getLHS(), stmt->getRHS());
             case BO_Comma:
                 {
    -                block = trans_create_node(c, NodeTypeBlock);
    -                AstNode *lhs = trans_expr(c, false, block, stmt->getLHS(), TransRValue);
    -                if (lhs == nullptr) return nullptr;
    -                block->data.block.statements.append(maybe_suppress_result(c, false, lhs));
    -                AstNode *rhs = trans_expr(c, result_used, block, stmt->getRHS(), TransRValue);
    -                if (rhs == nullptr) return nullptr;
    -                block->data.block.statements.append(maybe_suppress_result(c, result_used, rhs));
    -                block->data.block.last_statement_is_result_expression = true;
    -                return block;
    +                TransScopeBlock *scope_block = trans_scope_block_create(c, scope);
    +                AstNode *lhs = trans_expr(c, ResultUsedNo, &scope_block->base, stmt->getLHS(), TransRValue);
    +                if (lhs == nullptr)
    +                    return nullptr;
    +                scope_block->node->data.block.statements.append(maybe_suppress_result(c, ResultUsedNo, lhs));
    +
    +                AstNode *rhs = trans_expr(c, result_used, &scope_block->base, stmt->getRHS(), TransRValue);
    +                if (rhs == nullptr)
    +                    return nullptr;
    +                scope_block->node->data.block.statements.append(maybe_suppress_result(c, result_used, rhs));
    +
    +                scope_block->node->data.block.last_statement_is_result_expression = true;
    +                return scope_block->node;
                 }
             case BO_MulAssign:
             case BO_DivAssign:
    @@ -1127,18 +1288,20 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo
         zig_unreachable();
     }
     
    -static AstNode *trans_create_compound_assign_shift(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) {
    +static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result_used, TransScope *scope,
    +        const CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op)
    +{
         const SourceLocation &rhs_location = stmt->getRHS()->getLocStart();
         AstNode *rhs_type = qual_type_to_log2_int_ref(c, stmt->getComputationLHSType(), rhs_location);
     
         bool use_intermediate_casts = stmt->getComputationLHSType().getTypePtr() != stmt->getComputationResultType().getTypePtr();
    -    if (!use_intermediate_casts && !result_used) {
    +    if (!use_intermediate_casts && result_used == ResultUsedNo) {
             // simple common case, where the C and Zig are identical:
             // lhs >>= rhs
    -        AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue);
    +        AstNode *lhs = trans_expr(c, ResultUsedYes, scope, stmt->getLHS(), TransLValue);
             if (lhs == nullptr) return nullptr;
     
    -        AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransRValue);
    +        AstNode *rhs = trans_expr(c, ResultUsedYes, scope, stmt->getRHS(), TransRValue);
             if (rhs == nullptr) return nullptr;
             AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs);
     
    @@ -1153,57 +1316,68 @@ static AstNode *trans_create_compound_assign_shift(Context *c, bool result_used,
             // zig: }
             // where u5 is the appropriate type
     
    -        AstNode *child_block = trans_create_node(c, NodeTypeBlock);
    +        TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
     
             // const _ref = &lhs;
    -        AstNode *lhs = trans_expr(c, true, child_block, stmt->getLHS(), TransLValue);
    +        AstNode *lhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getLHS(), TransLValue);
             if (lhs == nullptr) return nullptr;
             AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs);
             // TODO: avoid name collisions with generated variable names
             Buf* tmp_var_name = buf_create_from_str("_ref");
             AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs);
    -        child_block->data.block.statements.append(tmp_var_decl);
    +        child_scope->node->data.block.statements.append(tmp_var_decl);
     
             // *_ref = result_type(operation_type(*_ref) >> u5(rhs));
     
    -        AstNode *rhs = trans_expr(c, true, child_block, stmt->getRHS(), TransRValue);
    +        AstNode *rhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getRHS(), TransRValue);
             if (rhs == nullptr) return nullptr;
             AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs);
     
    +        // operation_type(*_ref)
    +        AstNode *operation_type_cast = trans_c_cast(c, rhs_location,
    +            stmt->getComputationLHSType(),
    +            stmt->getLHS()->getType(),
    +            trans_create_node_prefix_op(c, PrefixOpDereference,
    +                trans_create_node_symbol(c, tmp_var_name)));
    +
    +        // result_type(... >> u5(rhs))
    +        AstNode *result_type_cast = trans_c_cast(c, rhs_location,
    +            stmt->getComputationResultType(),
    +            stmt->getComputationLHSType(),
    +            trans_create_node_bin_op(c,
    +                operation_type_cast,
    +                bin_op,
    +                coerced_rhs));
    +
    +        // *_ref = ...
             AstNode *assign_statement = trans_create_node_bin_op(c,
                 trans_create_node_prefix_op(c, PrefixOpDereference,
                     trans_create_node_symbol(c, tmp_var_name)),
    -            BinOpTypeAssign,
    -            trans_c_cast(c, rhs_location,
    -                stmt->getComputationResultType(),
    -                trans_create_node_bin_op(c,
    -                    trans_c_cast(c, rhs_location,
    -                        stmt->getComputationLHSType(),
    -                        trans_create_node_prefix_op(c, PrefixOpDereference,
    -                            trans_create_node_symbol(c, tmp_var_name))),
    -                    bin_op,
    -                    coerced_rhs)));
    -        child_block->data.block.statements.append(assign_statement);
    +            BinOpTypeAssign, result_type_cast);
     
    -        if (result_used) {
    +        child_scope->node->data.block.statements.append(assign_statement);
    +
    +        if (result_used == ResultUsedYes) {
                 // *_ref
    -            child_block->data.block.statements.append(
    +            child_scope->node->data.block.statements.append(
                     trans_create_node_prefix_op(c, PrefixOpDereference,
                         trans_create_node_symbol(c, tmp_var_name)));
    -            child_block->data.block.last_statement_is_result_expression = true;
    +            child_scope->node->data.block.last_statement_is_result_expression = true;
             }
     
    -        return child_block;
    +        return child_scope->node;
         }
     }
     
    -static AstNode *trans_create_compound_assign(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) {
    -    if (!result_used) {
    +static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used, TransScope *scope,
    +        const CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op)
    +{
    +    if (result_used == ResultUsedNo) {
             // simple common case, where the C and Zig are identical:
             // lhs += rhs
    -        AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue);
    +        AstNode *lhs = trans_expr(c, ResultUsedYes, scope, stmt->getLHS(), TransLValue);
             if (lhs == nullptr) return nullptr;
    -        AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransRValue);
    +        AstNode *rhs = trans_expr(c, ResultUsedYes, scope, stmt->getRHS(), TransRValue);
             if (rhs == nullptr) return nullptr;
             return trans_create_node_bin_op(c, lhs, assign_op, rhs);
         } else {
    @@ -1215,20 +1389,20 @@ static AstNode *trans_create_compound_assign(Context *c, bool result_used, AstNo
             // zig:     *_ref
             // zig: }
     
    -        AstNode *child_block = trans_create_node(c, NodeTypeBlock);
    +        TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
     
             // const _ref = &lhs;
    -        AstNode *lhs = trans_expr(c, true, child_block, stmt->getLHS(), TransLValue);
    +        AstNode *lhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getLHS(), TransLValue);
             if (lhs == nullptr) return nullptr;
             AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs);
             // TODO: avoid name collisions with generated variable names
             Buf* tmp_var_name = buf_create_from_str("_ref");
             AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs);
    -        child_block->data.block.statements.append(tmp_var_decl);
    +        child_scope->node->data.block.statements.append(tmp_var_decl);
     
             // *_ref = *_ref + rhs;
     
    -        AstNode *rhs = trans_expr(c, true, child_block, stmt->getRHS(), TransRValue);
    +        AstNode *rhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getRHS(), TransRValue);
             if (rhs == nullptr) return nullptr;
     
             AstNode *assign_statement = trans_create_node_bin_op(c,
    @@ -1240,26 +1414,28 @@ static AstNode *trans_create_compound_assign(Context *c, bool result_used, AstNo
                         trans_create_node_symbol(c, tmp_var_name)),
                     bin_op,
                     rhs));
    -        child_block->data.block.statements.append(assign_statement);
    +        child_scope->node->data.block.statements.append(assign_statement);
     
             // *_ref
    -        child_block->data.block.statements.append(
    +        child_scope->node->data.block.statements.append(
                 trans_create_node_prefix_op(c, PrefixOpDereference,
                     trans_create_node_symbol(c, tmp_var_name)));
    -        child_block->data.block.last_statement_is_result_expression = true;
    +        child_scope->node->data.block.last_statement_is_result_expression = true;
     
    -        return child_block;
    +        return child_scope->node;
         }
     }
     
     
    -static AstNode *trans_compound_assign_operator(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt) {
    +static AstNode *trans_compound_assign_operator(Context *c, ResultUsed result_used, TransScope *scope,
    +        const CompoundAssignOperator *stmt)
    +{
         switch (stmt->getOpcode()) {
             case BO_MulAssign:
                 if (qual_type_has_wrapping_overflow(c, stmt->getType()))
    -                return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignTimesWrap, BinOpTypeMultWrap);
    +                return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignTimesWrap, BinOpTypeMultWrap);
                 else
    -                return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignTimes, BinOpTypeMult);
    +                return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignTimes, BinOpTypeMult);
             case BO_DivAssign:
                 emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_DivAssign");
                 return nullptr;
    @@ -1268,24 +1444,24 @@ static AstNode *trans_compound_assign_operator(Context *c, bool result_used, Ast
                 return nullptr;
             case BO_AddAssign:
                 if (qual_type_has_wrapping_overflow(c, stmt->getType()))
    -                return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignPlusWrap, BinOpTypeAddWrap);
    +                return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignPlusWrap, BinOpTypeAddWrap);
                 else
    -                return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignPlus, BinOpTypeAdd);
    +                return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignPlus, BinOpTypeAdd);
             case BO_SubAssign:
                 if (qual_type_has_wrapping_overflow(c, stmt->getType()))
    -                return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignMinusWrap, BinOpTypeSubWrap);
    +                return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignMinusWrap, BinOpTypeSubWrap);
                 else
    -                return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignMinus, BinOpTypeSub);
    +                return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignMinus, BinOpTypeSub);
             case BO_ShlAssign:
    -            return trans_create_compound_assign_shift(c, result_used, block, stmt, BinOpTypeAssignBitShiftLeft, BinOpTypeBitShiftLeft);
    +            return trans_create_compound_assign_shift(c, result_used, scope, stmt, BinOpTypeAssignBitShiftLeft, BinOpTypeBitShiftLeft);
             case BO_ShrAssign:
    -            return trans_create_compound_assign_shift(c, result_used, block, stmt, BinOpTypeAssignBitShiftRight, BinOpTypeBitShiftRight);
    +            return trans_create_compound_assign_shift(c, result_used, scope, stmt, BinOpTypeAssignBitShiftRight, BinOpTypeBitShiftRight);
             case BO_AndAssign:
    -            return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignBitAnd, BinOpTypeBinAnd);
    +            return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignBitAnd, BinOpTypeBinAnd);
             case BO_XorAssign:
    -            return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignBitXor, BinOpTypeBinXor);
    +            return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignBitXor, BinOpTypeBinXor);
             case BO_OrAssign:
    -            return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignBitOr, BinOpTypeBinOr);
    +            return trans_create_compound_assign(c, result_used, scope, stmt, BinOpTypeAssignBitOr, BinOpTypeBinOr);
             case BO_PtrMemD:
             case BO_PtrMemI:
             case BO_Assign:
    @@ -1314,32 +1490,37 @@ static AstNode *trans_compound_assign_operator(Context *c, bool result_used, Ast
         zig_unreachable();
     }
     
    -static AstNode *trans_implicit_cast_expr(Context *c, AstNode *block, ImplicitCastExpr *stmt) {
    +static AstNode *trans_implicit_cast_expr(Context *c, TransScope *scope, const ImplicitCastExpr *stmt) {
         switch (stmt->getCastKind()) {
             case CK_LValueToRValue:
    -            return trans_expr(c, true, block, stmt->getSubExpr(), TransRValue);
    +            return trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue);
             case CK_IntegralCast:
                 {
    -                AstNode *target_node = trans_expr(c, true, block, stmt->getSubExpr(), TransRValue);
    +                AstNode *target_node = trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue);
                     if (target_node == nullptr)
                         return nullptr;
    -                return trans_c_cast(c, stmt->getExprLoc(), stmt->getType(), target_node);
    +                return trans_c_cast(c, stmt->getExprLoc(), stmt->getType(),
    +                        stmt->getSubExpr()->getType(), target_node);
                 }
             case CK_FunctionToPointerDecay:
             case CK_ArrayToPointerDecay:
                 {
    -                AstNode *target_node = trans_expr(c, true, block, stmt->getSubExpr(), TransRValue);
    +                AstNode *target_node = trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue);
                     if (target_node == nullptr)
                         return nullptr;
                     return target_node;
                 }
             case CK_BitCast:
                 {
    -                AstNode *target_node = trans_expr(c, true, block, stmt->getSubExpr(), TransRValue);
    +                AstNode *target_node = trans_expr(c, ResultUsedYes, scope, stmt->getSubExpr(), TransRValue);
                     if (target_node == nullptr)
                         return nullptr;
     
    -                AstNode *dest_type_node = trans_qual_type(c, stmt->getType(), stmt->getLocStart());
    +                if (expr_types_equal(c, stmt, stmt->getSubExpr())) {
    +                    return target_node;
    +                }
    +
    +                AstNode *dest_type_node = get_expr_type(c, stmt);
     
                     AstNode *node = trans_create_node_builtin_fn_call_str(c, "ptrCast");
                     node->data.fn_call_expr.params.append(dest_type_node);
    @@ -1508,94 +1689,155 @@ static AstNode *trans_implicit_cast_expr(Context *c, AstNode *block, ImplicitCas
         zig_unreachable();
     }
     
    -static AstNode *trans_decl_ref_expr(Context *c, DeclRefExpr *stmt, TransLRValue lrval) {
    -    ValueDecl *value_decl = stmt->getDecl();
    -    Buf *symbol_name = buf_create_from_str(decl_name(value_decl));
    +static AstNode *trans_decl_ref_expr(Context *c, TransScope *scope, const DeclRefExpr *stmt, TransLRValue lrval) {
    +    const ValueDecl *value_decl = stmt->getDecl();
    +    Buf *c_symbol_name = buf_create_from_str(decl_name(value_decl));
    +    Buf *zig_symbol_name = trans_lookup_zig_symbol(c, scope, c_symbol_name);
         if (lrval == TransLValue) {
    -        c->ptr_params.put(symbol_name, true);
    +        c->ptr_params.put(zig_symbol_name, true);
         }
    -    return trans_create_node_symbol(c, symbol_name);
    +    return trans_create_node_symbol(c, zig_symbol_name);
     }
     
    -static AstNode *trans_create_post_crement(Context *c, bool result_used, AstNode *block, UnaryOperator *stmt, BinOpType assign_op) {
    +static AstNode *trans_create_post_crement(Context *c, ResultUsed result_used, TransScope *scope,
    +        const UnaryOperator *stmt, BinOpType assign_op)
    +{
         Expr *op_expr = stmt->getSubExpr();
     
    -    if (!result_used) {
    +    if (result_used == ResultUsedNo) {
             // common case
             // c: expr++
             // zig: expr += 1
             return trans_create_node_bin_op(c,
    -            trans_expr(c, true, block, op_expr, TransLValue),
    +            trans_expr(c, ResultUsedYes, scope, op_expr, TransLValue),
                 assign_op,
                 trans_create_node_unsigned(c, 1));
    -    } else {
    -        // worst case
    -        // c: expr++
    -        // zig: {
    -        // zig:     const _ref = &expr;
    -        // zig:     const _tmp = *_ref;
    -        // zig:     *_ref += 1;
    -        // zig:     _tmp
    -        // zig: }
    -        AstNode *child_block = trans_create_node(c, NodeTypeBlock);
    -
    -        // const _ref = &expr;
    -        AstNode *expr = trans_expr(c, true, child_block, op_expr, TransLValue);
    -        if (expr == nullptr) return nullptr;
    -        AstNode *addr_of_expr = trans_create_node_addr_of(c, false, false, expr);
    -        // TODO: avoid name collisions with generated variable names
    -        Buf* ref_var_name = buf_create_from_str("_ref");
    -        AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr);
    -        child_block->data.block.statements.append(ref_var_decl);
    -
    -        // const _tmp = *_ref;
    -        Buf* tmp_var_name = buf_create_from_str("_tmp");
    -        AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr,
    -            trans_create_node_prefix_op(c, PrefixOpDereference,
    -                trans_create_node_symbol(c, ref_var_name)));
    -        child_block->data.block.statements.append(tmp_var_decl);
    -
    -        // *_ref += 1;
    -        AstNode *assign_statement = trans_create_node_bin_op(c,
    -            trans_create_node_prefix_op(c, PrefixOpDereference,
    -                trans_create_node_symbol(c, ref_var_name)),
    -            assign_op,
    -            trans_create_node_unsigned(c, 1));
    -        child_block->data.block.statements.append(assign_statement);
    -
    -        // _tmp
    -        child_block->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name));
    -        child_block->data.block.last_statement_is_result_expression = true;
    -
    -        return child_block;
         }
    +    // worst case
    +    // c: expr++
    +    // zig: {
    +    // zig:     const _ref = &expr;
    +    // zig:     const _tmp = *_ref;
    +    // zig:     *_ref += 1;
    +    // zig:     _tmp
    +    // zig: }
    +    TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
    +
    +    // const _ref = &expr;
    +    AstNode *expr = trans_expr(c, ResultUsedYes, &child_scope->base, op_expr, TransLValue);
    +    if (expr == nullptr) return nullptr;
    +    AstNode *addr_of_expr = trans_create_node_addr_of(c, false, false, expr);
    +    // TODO: avoid name collisions with generated variable names
    +    Buf* ref_var_name = buf_create_from_str("_ref");
    +    AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr);
    +    child_scope->node->data.block.statements.append(ref_var_decl);
    +
    +    // const _tmp = *_ref;
    +    Buf* tmp_var_name = buf_create_from_str("_tmp");
    +    AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr,
    +        trans_create_node_prefix_op(c, PrefixOpDereference,
    +            trans_create_node_symbol(c, ref_var_name)));
    +    child_scope->node->data.block.statements.append(tmp_var_decl);
    +
    +    // *_ref += 1;
    +    AstNode *assign_statement = trans_create_node_bin_op(c,
    +        trans_create_node_prefix_op(c, PrefixOpDereference,
    +            trans_create_node_symbol(c, ref_var_name)),
    +        assign_op,
    +        trans_create_node_unsigned(c, 1));
    +    child_scope->node->data.block.statements.append(assign_statement);
    +
    +    // _tmp
    +    child_scope->node->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name));
    +    child_scope->node->data.block.last_statement_is_result_expression = true;
    +
    +    return child_scope->node;
     }
     
    -static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *block, UnaryOperator *stmt) {
    +static AstNode *trans_create_pre_crement(Context *c, ResultUsed result_used, TransScope *scope,
    +        const UnaryOperator *stmt, BinOpType assign_op)
    +{
    +    Expr *op_expr = stmt->getSubExpr();
    +
    +    if (result_used == ResultUsedNo) {
    +        // common case
    +        // c: ++expr
    +        // zig: expr += 1
    +        return trans_create_node_bin_op(c,
    +            trans_expr(c, ResultUsedYes, scope, op_expr, TransLValue),
    +            assign_op,
    +            trans_create_node_unsigned(c, 1));
    +    }
    +    // worst case
    +    // c: ++expr
    +    // zig: {
    +    // zig:     const _ref = &expr;
    +    // zig:     *_ref += 1;
    +    // zig:     *_ref
    +    // zig: }
    +    TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
    +
    +    // const _ref = &expr;
    +    AstNode *expr = trans_expr(c, ResultUsedYes, &child_scope->base, op_expr, TransLValue);
    +    if (expr == nullptr) return nullptr;
    +    AstNode *addr_of_expr = trans_create_node_addr_of(c, false, false, expr);
    +    // TODO: avoid name collisions with generated variable names
    +    Buf* ref_var_name = buf_create_from_str("_ref");
    +    AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr);
    +    child_scope->node->data.block.statements.append(ref_var_decl);
    +
    +    // *_ref += 1;
    +    AstNode *assign_statement = trans_create_node_bin_op(c,
    +        trans_create_node_prefix_op(c, PrefixOpDereference,
    +            trans_create_node_symbol(c, ref_var_name)),
    +        assign_op,
    +        trans_create_node_unsigned(c, 1));
    +    child_scope->node->data.block.statements.append(assign_statement);
    +
    +    // *_ref
    +    AstNode *deref_expr = trans_create_node_prefix_op(c, PrefixOpDereference,
    +            trans_create_node_symbol(c, ref_var_name));
    +    child_scope->node->data.block.statements.append(deref_expr);
    +    child_scope->node->data.block.last_statement_is_result_expression = true;
    +
    +    return child_scope->node;
    +}
    +
    +static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransScope *scope, const UnaryOperator *stmt) {
         switch (stmt->getOpcode()) {
             case UO_PostInc:
                 if (qual_type_has_wrapping_overflow(c, stmt->getType()))
    -                return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignPlusWrap);
    +                return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignPlusWrap);
                 else
    -                return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignPlus);
    +                return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignPlus);
             case UO_PostDec:
                 if (qual_type_has_wrapping_overflow(c, stmt->getType()))
    -                return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignMinusWrap);
    +                return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignMinusWrap);
                 else
    -                return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignMinus);
    +                return trans_create_post_crement(c, result_used, scope, stmt, BinOpTypeAssignMinus);
             case UO_PreInc:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_PreInc");
    -            return nullptr;
    +            if (qual_type_has_wrapping_overflow(c, stmt->getType()))
    +                return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignPlusWrap);
    +            else
    +                return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignPlus);
             case UO_PreDec:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_PreDec");
    -            return nullptr;
    +            if (qual_type_has_wrapping_overflow(c, stmt->getType()))
    +                return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignMinusWrap);
    +            else
    +                return trans_create_pre_crement(c, result_used, scope, stmt, BinOpTypeAssignMinus);
             case UO_AddrOf:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_AddrOf");
    -            return nullptr;
    +            {
    +                AstNode *value_node = trans_expr(c, result_used, scope, stmt->getSubExpr(), TransLValue);
    +                if (value_node == nullptr)
    +                    return value_node;
    +                return trans_create_node_addr_of(c, false, false, value_node);
    +            }
             case UO_Deref:
                 {
    +                AstNode *value_node = trans_expr(c, result_used, scope, stmt->getSubExpr(), TransRValue);
    +                if (value_node == nullptr)
    +                    return nullptr;
                     bool is_fn_ptr = qual_type_is_fn_ptr(c, stmt->getSubExpr()->getType());
    -                AstNode *value_node = trans_expr(c, result_used, block, stmt->getSubExpr(), TransRValue);
                     if (is_fn_ptr)
                         return value_node;
                     AstNode *unwrapped = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, value_node);
    @@ -1611,7 +1853,7 @@ static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *bloc
                         AstNode *node = trans_create_node(c, NodeTypePrefixOpExpr);
                         node->data.prefix_op_expr.prefix_op = PrefixOpNegation;
     
    -                    node->data.prefix_op_expr.primary_expr = trans_expr(c, true, block, op_expr, TransRValue);
    +                    node->data.prefix_op_expr.primary_expr = trans_expr(c, ResultUsedYes, scope, op_expr, TransRValue);
                         if (node->data.prefix_op_expr.primary_expr == nullptr)
                             return nullptr;
     
    @@ -1621,7 +1863,7 @@ static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *bloc
                         AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
                         node->data.bin_op_expr.op1 = trans_create_node_unsigned(c, 0);
     
    -                    node->data.bin_op_expr.op2 = trans_expr(c, true, block, op_expr, TransRValue);
    +                    node->data.bin_op_expr.op2 = trans_expr(c, ResultUsedYes, scope, op_expr, TransRValue);
                         if (node->data.bin_op_expr.op2 == nullptr)
                             return nullptr;
     
    @@ -1633,8 +1875,13 @@ static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *bloc
                     }
                 }
             case UO_Not:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Not");
    -            return nullptr;
    +            {
    +                Expr *op_expr = stmt->getSubExpr();
    +                AstNode *sub_node = trans_expr(c, ResultUsedYes, scope, op_expr, TransRValue);
    +                if (sub_node == nullptr)
    +                    return nullptr;
    +                return trans_create_node_prefix_op(c, PrefixOpBinNot, sub_node);
    +            }
             case UO_LNot:
                 emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_LNot");
                 return nullptr;
    @@ -1654,7 +1901,15 @@ static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *bloc
         zig_unreachable();
     }
     
    -static AstNode *trans_local_declaration(Context *c, AstNode *block, DeclStmt *stmt) {
    +static int trans_local_declaration(Context *c, TransScope *scope, const DeclStmt *stmt,
    +        AstNode **out_node, TransScope **out_scope)
    +{
    +    // declarations are added via the scope
    +    *out_node = nullptr;
    +
    +    TransScopeBlock *scope_block = trans_scope_block_find(scope);
    +    assert(scope_block != nullptr);
    +
         for (auto iter = stmt->decl_begin(); iter != stmt->decl_end(); iter++) {
             Decl *decl = *iter;
             switch (decl->getKind()) {
    @@ -1663,294 +1918,313 @@ static AstNode *trans_local_declaration(Context *c, AstNode *block, DeclStmt *st
                     QualType qual_type = var_decl->getTypeSourceInfo()->getType();
                     AstNode *init_node = nullptr;
                     if (var_decl->hasInit()) {
    -                    init_node = trans_expr(c, true, block, var_decl->getInit(), TransRValue);
    +                    init_node = trans_expr(c, ResultUsedYes, scope, var_decl->getInit(), TransRValue);
                         if (init_node == nullptr)
    -                        return nullptr;
    +                        return ErrorUnexpected;
     
                     }
                     AstNode *type_node = trans_qual_type(c, qual_type, stmt->getLocStart());
                     if (type_node == nullptr)
    -                    return nullptr;
    +                    return ErrorUnexpected;
     
    -                Buf *symbol_name = buf_create_from_str(decl_name(var_decl));
    +                Buf *c_symbol_name = buf_create_from_str(decl_name(var_decl));
    +
    +                TransScopeVar *var_scope = trans_scope_var_create(c, scope, c_symbol_name);
    +                scope = &var_scope->base;
     
                     AstNode *node = trans_create_node_var_decl_local(c, qual_type.isConstQualified(),
    -                        symbol_name, type_node, init_node);
    -                block->data.block.statements.append(node);
    +                        var_scope->zig_name, type_node, init_node);
    +
    +                scope_block->node->data.block.statements.append(node);
                     continue;
                 }
                 case Decl::AccessSpec:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind AccessSpec");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::Block:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Block");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::Captured:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Captured");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ClassScopeFunctionSpecialization:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassScopeFunctionSpecialization");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::Empty:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Empty");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::Export:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Export");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ExternCContext:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ExternCContext");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::FileScopeAsm:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind FileScopeAsm");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::Friend:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Friend");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::FriendTemplate:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind FriendTemplate");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::Import:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Import");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::LinkageSpec:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind LinkageSpec");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::Label:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Label");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::Namespace:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Namespace");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::NamespaceAlias:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind NamespaceAlias");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ObjCCompatibleAlias:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCCompatibleAlias");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ObjCCategory:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCCategory");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ObjCCategoryImpl:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCCategoryImpl");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ObjCImplementation:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCImplementation");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ObjCInterface:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCInterface");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ObjCProtocol:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCProtocol");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ObjCMethod:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCMethod");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ObjCProperty:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCProperty");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::BuiltinTemplate:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind BuiltinTemplate");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ClassTemplate:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassTemplate");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::FunctionTemplate:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind FunctionTemplate");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::TypeAliasTemplate:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TypeAliasTemplate");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::VarTemplate:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind VarTemplate");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::TemplateTemplateParm:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TemplateTemplateParm");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::Enum:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Enum");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::Record:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Record");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::CXXRecord:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXRecord");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ClassTemplateSpecialization:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassTemplateSpecialization");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ClassTemplatePartialSpecialization:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ClassTemplatePartialSpecialization");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::TemplateTypeParm:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TemplateTypeParm");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ObjCTypeParam:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCTypeParam");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::TypeAlias:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TypeAlias");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::Typedef:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Typedef");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::UnresolvedUsingTypename:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UnresolvedUsingTypename");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::Using:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Using");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::UsingDirective:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UsingDirective");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::UsingPack:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UsingPack");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::UsingShadow:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UsingShadow");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ConstructorUsingShadow:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ConstructorUsingShadow");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::Binding:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Binding");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::Field:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Field");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ObjCAtDefsField:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCAtDefsField");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ObjCIvar:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCIvar");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::Function:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Function");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::CXXDeductionGuide:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXDeductionGuide");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::CXXMethod:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXMethod");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::CXXConstructor:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXConstructor");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::CXXConversion:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXConversion");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::CXXDestructor:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind CXXDestructor");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::MSProperty:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind MSProperty");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::NonTypeTemplateParm:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind NonTypeTemplateParm");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::Decomposition:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind Decomposition");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ImplicitParam:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ImplicitParam");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::OMPCapturedExpr:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind OMPCapturedExpr");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ParmVar:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ParmVar");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::VarTemplateSpecialization:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind VarTemplateSpecialization");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::VarTemplatePartialSpecialization:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind VarTemplatePartialSpecialization");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::EnumConstant:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind EnumConstant");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::IndirectField:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind IndirectField");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::OMPDeclareReduction:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind OMPDeclareReduction");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::UnresolvedUsingValue:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind UnresolvedUsingValue");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::OMPThreadPrivate:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind OMPThreadPrivate");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::ObjCPropertyImpl:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind ObjCPropertyImpl");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::PragmaComment:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind PragmaComment");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::PragmaDetectMismatch:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind PragmaDetectMismatch");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::StaticAssert:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind StaticAssert");
    -                return nullptr;
    +                return ErrorUnexpected;
                 case Decl::TranslationUnit:
                     emit_warning(c, stmt->getLocStart(), "TODO handle decl kind TranslationUnit");
    -                return nullptr;
    +                return ErrorUnexpected;
             }
             zig_unreachable();
         }
     
    -    // declarations were already added
    -    return skip_add_to_block_node;
    +    *out_scope = scope;
    +    return ErrorNone;
     }
     
    -static AstNode *trans_while_loop(Context *c, AstNode *block, WhileStmt *stmt) {
    -    AstNode *while_node = trans_create_node(c, NodeTypeWhileExpr);
    +static AstNode *trans_while_loop(Context *c, TransScope *scope, const WhileStmt *stmt) {
    +    TransScopeWhile *while_scope = trans_scope_while_create(c, scope);
     
    -    while_node->data.while_expr.condition = trans_expr(c, true, block, stmt->getCond(), TransRValue);
    -    if (while_node->data.while_expr.condition == nullptr)
    +    while_scope->node->data.while_expr.condition = trans_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue);
    +    if (while_scope->node->data.while_expr.condition == nullptr)
             return nullptr;
     
    -    while_node->data.while_expr.body = trans_stmt(c, false, block, stmt->getBody(), TransRValue);
    -    if (while_node->data.while_expr.body == nullptr)
    +    TransScope *body_scope = trans_stmt(c, &while_scope->base, stmt->getBody(),
    +            &while_scope->node->data.while_expr.body);
    +    if (body_scope == nullptr) 
             return nullptr;
     
    -    return while_node;
    +    return while_scope->node;
     }
     
    -static AstNode *trans_if_statement(Context *c, AstNode *block, IfStmt *stmt) {
    +static AstNode *trans_if_statement(Context *c, TransScope *scope, const IfStmt *stmt) {
         // if (c) t
         // if (c) t else e
         AstNode *if_node = trans_create_node(c, NodeTypeIfBoolExpr);
     
         // TODO: condition != 0
    -    AstNode *condition_node = trans_expr(c, true, block, stmt->getCond(), TransRValue);
    +    AstNode *condition_node = trans_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue);
         if (condition_node == nullptr)
             return nullptr;
         if_node->data.if_bool_expr.condition = condition_node;
     
    -    if_node->data.if_bool_expr.then_block = trans_stmt(c, false, block, stmt->getThen(), TransRValue);
    -    if (if_node->data.if_bool_expr.then_block == nullptr)
    +    TransScope *then_scope = trans_stmt(c, scope, stmt->getThen(), &if_node->data.if_bool_expr.then_block);
    +    if (then_scope == nullptr)
             return nullptr;
     
         if (stmt->getElse() != nullptr) {
    -        if_node->data.if_bool_expr.else_node = trans_stmt(c, false, block, stmt->getElse(), TransRValue);
    -        if (if_node->data.if_bool_expr.else_node == nullptr)
    +        TransScope *else_scope = trans_stmt(c, scope, stmt->getElse(), &if_node->data.if_bool_expr.else_node);
    +        if (else_scope == nullptr)
                 return nullptr;
         }
     
         return if_node;
     }
     
    -static AstNode *trans_call_expr(Context *c, bool result_used, AstNode *block, CallExpr *stmt) {
    +static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope *scope, const CallExpr *stmt) {
         AstNode *node = trans_create_node(c, NodeTypeFnCallExpr);
     
    -    AstNode *callee_raw_node = trans_expr(c, true, block, stmt->getCallee(), TransRValue);
    +    AstNode *callee_raw_node = trans_expr(c, ResultUsedYes, scope, stmt->getCallee(), TransRValue);
         if (callee_raw_node == nullptr)
             return nullptr;
     
    -    AstNode *callee_node;
    +    AstNode *callee_node = nullptr;
         if (qual_type_is_fn_ptr(c, stmt->getCallee()->getType())) {
    -        callee_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, callee_raw_node);
    +        if (stmt->getCallee()->getStmtClass() == Stmt::ImplicitCastExprClass) {
    +            const ImplicitCastExpr *implicit_cast = static_cast(stmt->getCallee());
    +            if (implicit_cast->getCastKind() == CK_FunctionToPointerDecay) {
    +                if (implicit_cast->getSubExpr()->getStmtClass() == Stmt::DeclRefExprClass) {
    +                    const DeclRefExpr *decl_ref = static_cast(implicit_cast->getSubExpr());
    +                    const Decl *decl = decl_ref->getFoundDecl();
    +                    if (decl->getKind() == Decl::Function) {
    +                        callee_node = callee_raw_node;
    +                    }
    +                }
    +            }
    +        }
    +        if (callee_node == nullptr) {
    +            callee_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, callee_raw_node);
    +        }
         } else {
             callee_node = callee_raw_node;
         }
    @@ -1958,9 +2232,9 @@ static AstNode *trans_call_expr(Context *c, bool result_used, AstNode *block, Ca
         node->data.fn_call_expr.fn_ref_expr = callee_node;
     
         unsigned num_args = stmt->getNumArgs();
    -    Expr **args = stmt->getArgs();
    +    const Expr * const* args = stmt->getArgs();
         for (unsigned i = 0; i < num_args; i += 1) {
    -        AstNode *arg_node = trans_expr(c, true, block, args[i], TransRValue);
    +        AstNode *arg_node = trans_expr(c, ResultUsedYes, scope, args[i], TransRValue);
             if (arg_node == nullptr)
                 return nullptr;
     
    @@ -1970,8 +2244,8 @@ static AstNode *trans_call_expr(Context *c, bool result_used, AstNode *block, Ca
         return node;
     }
     
    -static AstNode *trans_member_expr(Context *c, AstNode *block, MemberExpr *stmt) {
    -    AstNode *container_node = trans_expr(c, true, block, stmt->getBase(), TransRValue);
    +static AstNode *trans_member_expr(Context *c, TransScope *scope, const MemberExpr *stmt) {
    +    AstNode *container_node = trans_expr(c, ResultUsedYes, scope, stmt->getBase(), TransRValue);
         if (container_node == nullptr)
             return nullptr;
     
    @@ -1985,12 +2259,12 @@ static AstNode *trans_member_expr(Context *c, AstNode *block, MemberExpr *stmt)
         return node;
     }
     
    -static AstNode *trans_array_subscript_expr(Context *c, AstNode *block, ArraySubscriptExpr *stmt) {
    -    AstNode *container_node = trans_expr(c, true, block, stmt->getBase(), TransRValue);
    +static AstNode *trans_array_subscript_expr(Context *c, TransScope *scope, const ArraySubscriptExpr *stmt) {
    +    AstNode *container_node = trans_expr(c, ResultUsedYes, scope, stmt->getBase(), TransRValue);
         if (container_node == nullptr)
             return nullptr;
     
    -    AstNode *idx_node = trans_expr(c, true, block, stmt->getIdx(), TransRValue);
    +    AstNode *idx_node = trans_expr(c, ResultUsedYes, scope, stmt->getIdx(), TransRValue);
         if (idx_node == nullptr)
             return nullptr;
     
    @@ -2001,17 +2275,19 @@ static AstNode *trans_array_subscript_expr(Context *c, AstNode *block, ArraySubs
         return node;
     }
     
    -static AstNode *trans_c_style_cast_expr(Context *c, bool result_used, AstNode *block,
    -        CStyleCastExpr *stmt, TransLRValue lrvalue)
    +static AstNode *trans_c_style_cast_expr(Context *c, ResultUsed result_used, TransScope *scope,
    +        const CStyleCastExpr *stmt, TransLRValue lrvalue)
     {
    -    AstNode *sub_expr_node = trans_expr(c, result_used, block, stmt->getSubExpr(), lrvalue);
    +    AstNode *sub_expr_node = trans_expr(c, result_used, scope, stmt->getSubExpr(), lrvalue);
         if (sub_expr_node == nullptr)
             return nullptr;
     
    -    return trans_c_cast(c, stmt->getLocStart(), stmt->getType(), sub_expr_node);
    +    return trans_c_cast(c, stmt->getLocStart(), stmt->getType(), stmt->getSubExpr()->getType(), sub_expr_node);
     }
     
    -static AstNode *trans_unary_expr_or_type_trait_expr(Context *c, AstNode *block, UnaryExprOrTypeTraitExpr *stmt) {
    +static AstNode *trans_unary_expr_or_type_trait_expr(Context *c, TransScope *scope,
    +        const UnaryExprOrTypeTraitExpr *stmt)
    +{
         AstNode *type_node = trans_qual_type(c, stmt->getTypeOfArgument(), stmt->getLocStart());
         if (type_node == nullptr)
             return nullptr;
    @@ -2021,17 +2297,13 @@ static AstNode *trans_unary_expr_or_type_trait_expr(Context *c, AstNode *block,
         return node;
     }
     
    -static AstNode *trans_do_loop(Context *c, AstNode *block, DoStmt *stmt) {
    -    stmt->getBody();
    -    stmt->getCond();
    +static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt *stmt) {
    +    TransScopeWhile *while_scope = trans_scope_while_create(c, parent_scope);
     
    -    AstNode *while_node = trans_create_node(c, NodeTypeWhileExpr);
    -
    -    AstNode *true_node = trans_create_node(c, NodeTypeBoolLiteral);
    -    true_node->data.bool_literal.value = true;
    -    while_node->data.while_expr.condition = true_node;
    +    while_scope->node->data.while_expr.condition = trans_create_node_bool(c, true);
     
         AstNode *body_node;
    +    TransScope *child_scope;
         if (stmt->getBody()->getStmtClass() == Stmt::CompoundStmtClass) {
             // there's already a block in C, so we'll append our condition to it.
             // c: do {
    @@ -2043,8 +2315,13 @@ static AstNode *trans_do_loop(Context *c, AstNode *block, DoStmt *stmt) {
             // zig:   b;
             // zig:   if (!cond) break;
             // zig: }
    -        body_node = trans_stmt(c, false, block, stmt->getBody(), TransRValue);
    -        if (body_node == nullptr) return nullptr;
    +
    +        // We call the low level function so that we can set child_scope to the scope of the generated block.
    +        if (trans_stmt_extra(c, &while_scope->base, stmt->getBody(), ResultUsedNo, TransRValue, &body_node,
    +            nullptr, &child_scope))
    +        {
    +            return nullptr;
    +        }
             assert(body_node->type == NodeTypeBlock);
         } else {
             // the C statement is without a block, so we need to create a block to contain it.
    @@ -2055,598 +2332,881 @@ static AstNode *trans_do_loop(Context *c, AstNode *block, DoStmt *stmt) {
             // zig:   a;
             // zig:   if (!cond) break;
             // zig: }
    -        body_node = trans_create_node(c, NodeTypeBlock);
    -        AstNode *child_statement = trans_stmt(c, false, body_node, stmt->getBody(), TransRValue);
    -        if (child_statement == nullptr) return nullptr;
    +        TransScopeBlock *child_block_scope = trans_scope_block_create(c, &while_scope->base);
    +        body_node = child_block_scope->node;
    +        AstNode *child_statement;
    +        child_scope = trans_stmt(c, &child_block_scope->base, stmt->getBody(), &child_statement);
    +        if (child_scope == nullptr) return nullptr;
             body_node->data.block.statements.append(child_statement);
         }
     
         // if (!cond) break;
    -    AstNode *condition_node = trans_expr(c, true, body_node, stmt->getCond(), TransRValue);
    +    AstNode *condition_node = trans_expr(c, ResultUsedYes, child_scope, stmt->getCond(), TransRValue);
         if (condition_node == nullptr) return nullptr;
         AstNode *terminator_node = trans_create_node(c, NodeTypeIfBoolExpr);
         terminator_node->data.if_bool_expr.condition = trans_create_node_prefix_op(c, PrefixOpBoolNot, condition_node);
         terminator_node->data.if_bool_expr.then_block = trans_create_node(c, NodeTypeBreak);
    +
         body_node->data.block.statements.append(terminator_node);
     
    -    while_node->data.while_expr.body = body_node;
    +    while_scope->node->data.while_expr.body = body_node;
     
    -    return while_node;
    +    return while_scope->node;
     }
     
    -static AstNode *trans_stmt(Context *c, bool result_used, AstNode *block, Stmt *stmt, TransLRValue lrvalue) {
    -    Stmt::StmtClass sc = stmt->getStmtClass();
    -    switch (sc) {
    -        case Stmt::ReturnStmtClass:
    -            return trans_return_stmt(c, block, (ReturnStmt *)stmt);
    -        case Stmt::CompoundStmtClass:
    -            return trans_compound_stmt(c, block, (CompoundStmt *)stmt);
    -        case Stmt::IntegerLiteralClass:
    -            return trans_integer_literal(c, (IntegerLiteral *)stmt);
    -        case Stmt::ConditionalOperatorClass:
    -            return trans_conditional_operator(c, result_used, block, (ConditionalOperator *)stmt);
    -        case Stmt::BinaryOperatorClass:
    -            return trans_binary_operator(c, result_used, block, (BinaryOperator *)stmt);
    -        case Stmt::CompoundAssignOperatorClass:
    -            return trans_compound_assign_operator(c, result_used, block, (CompoundAssignOperator *)stmt);
    -        case Stmt::ImplicitCastExprClass:
    -            return trans_implicit_cast_expr(c, block, (ImplicitCastExpr *)stmt);
    -        case Stmt::DeclRefExprClass:
    -            return trans_decl_ref_expr(c, (DeclRefExpr *)stmt, lrvalue);
    -        case Stmt::UnaryOperatorClass:
    -            return trans_unary_operator(c, result_used, block, (UnaryOperator *)stmt);
    -        case Stmt::DeclStmtClass:
    -            return trans_local_declaration(c, block, (DeclStmt *)stmt);
    -        case Stmt::WhileStmtClass:
    -            return trans_while_loop(c, block, (WhileStmt *)stmt);
    -        case Stmt::IfStmtClass:
    -            return trans_if_statement(c, block, (IfStmt *)stmt);
    -        case Stmt::CallExprClass:
    -            return trans_call_expr(c, result_used, block, (CallExpr *)stmt);
    -        case Stmt::NullStmtClass:
    -            return skip_add_to_block_node;
    -        case Stmt::MemberExprClass:
    -            return trans_member_expr(c, block, (MemberExpr *)stmt);
    -        case Stmt::ArraySubscriptExprClass:
    -            return trans_array_subscript_expr(c, block, (ArraySubscriptExpr *)stmt);
    -        case Stmt::CStyleCastExprClass:
    -            return trans_c_style_cast_expr(c, result_used, block, (CStyleCastExpr *)stmt, lrvalue);
    -        case Stmt::UnaryExprOrTypeTraitExprClass:
    -            return trans_unary_expr_or_type_trait_expr(c, block, (UnaryExprOrTypeTraitExpr *)stmt);
    -        case Stmt::DoStmtClass:
    -            return trans_do_loop(c, block, (DoStmt *)stmt);
    -        case Stmt::CaseStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CaseStmtClass");
    -            return nullptr;
    -        case Stmt::DefaultStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C DefaultStmtClass");
    -            return nullptr;
    -        case Stmt::SwitchStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C SwitchStmtClass");
    -            return nullptr;
    -        case Stmt::NoStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C NoStmtClass");
    -            return nullptr;
    -        case Stmt::GCCAsmStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C GCCAsmStmtClass");
    -            return nullptr;
    -        case Stmt::MSAsmStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C MSAsmStmtClass");
    -            return nullptr;
    -        case Stmt::AttributedStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C AttributedStmtClass");
    -            return nullptr;
    -        case Stmt::BreakStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C BreakStmtClass");
    -            return nullptr;
    -        case Stmt::CXXCatchStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXCatchStmtClass");
    -            return nullptr;
    -        case Stmt::CXXForRangeStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXForRangeStmtClass");
    -            return nullptr;
    -        case Stmt::CXXTryStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXTryStmtClass");
    -            return nullptr;
    -        case Stmt::CapturedStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CapturedStmtClass");
    -            return nullptr;
    -        case Stmt::ContinueStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ContinueStmtClass");
    -            return nullptr;
    -        case Stmt::CoreturnStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CoreturnStmtClass");
    -            return nullptr;
    -        case Stmt::CoroutineBodyStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CoroutineBodyStmtClass");
    -            return nullptr;
    -        case Stmt::BinaryConditionalOperatorClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C BinaryConditionalOperatorClass");
    -            return nullptr;
    -        case Stmt::AddrLabelExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C AddrLabelExprClass");
    -            return nullptr;
    -        case Stmt::ArrayInitIndexExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ArrayInitIndexExprClass");
    -            return nullptr;
    -        case Stmt::ArrayInitLoopExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ArrayInitLoopExprClass");
    -            return nullptr;
    -        case Stmt::ArrayTypeTraitExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ArrayTypeTraitExprClass");
    -            return nullptr;
    -        case Stmt::AsTypeExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C AsTypeExprClass");
    -            return nullptr;
    -        case Stmt::AtomicExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C AtomicExprClass");
    -            return nullptr;
    -        case Stmt::BlockExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C BlockExprClass");
    -            return nullptr;
    -        case Stmt::CXXBindTemporaryExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXBindTemporaryExprClass");
    -            return nullptr;
    -        case Stmt::CXXBoolLiteralExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXBoolLiteralExprClass");
    -            return nullptr;
    -        case Stmt::CXXConstructExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXConstructExprClass");
    -            return nullptr;
    -        case Stmt::CXXTemporaryObjectExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXTemporaryObjectExprClass");
    -            return nullptr;
    -        case Stmt::CXXDefaultArgExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDefaultArgExprClass");
    -            return nullptr;
    -        case Stmt::CXXDefaultInitExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDefaultInitExprClass");
    -            return nullptr;
    -        case Stmt::CXXDeleteExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDeleteExprClass");
    -            return nullptr;
    -        case Stmt::CXXDependentScopeMemberExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDependentScopeMemberExprClass");
    -            return nullptr;
    -        case Stmt::CXXFoldExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXFoldExprClass");
    -            return nullptr;
    -        case Stmt::CXXInheritedCtorInitExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXInheritedCtorInitExprClass");
    -            return nullptr;
    -        case Stmt::CXXNewExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXNewExprClass");
    -            return nullptr;
    -        case Stmt::CXXNoexceptExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXNoexceptExprClass");
    -            return nullptr;
    -        case Stmt::CXXNullPtrLiteralExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXNullPtrLiteralExprClass");
    -            return nullptr;
    -        case Stmt::CXXPseudoDestructorExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXPseudoDestructorExprClass");
    -            return nullptr;
    -        case Stmt::CXXScalarValueInitExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXScalarValueInitExprClass");
    -            return nullptr;
    -        case Stmt::CXXStdInitializerListExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXStdInitializerListExprClass");
    -            return nullptr;
    -        case Stmt::CXXThisExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXThisExprClass");
    -            return nullptr;
    -        case Stmt::CXXThrowExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXThrowExprClass");
    -            return nullptr;
    -        case Stmt::CXXTypeidExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXTypeidExprClass");
    -            return nullptr;
    -        case Stmt::CXXUnresolvedConstructExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXUnresolvedConstructExprClass");
    -            return nullptr;
    -        case Stmt::CXXUuidofExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXUuidofExprClass");
    -            return nullptr;
    -        case Stmt::CUDAKernelCallExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CUDAKernelCallExprClass");
    -            return nullptr;
    -        case Stmt::CXXMemberCallExprClass:
    -            (void)result_used;
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXMemberCallExprClass");
    -            return nullptr;
    -        case Stmt::CXXOperatorCallExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXOperatorCallExprClass");
    -            return nullptr;
    -        case Stmt::UserDefinedLiteralClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C UserDefinedLiteralClass");
    -            return nullptr;
    -        case Stmt::CXXFunctionalCastExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXFunctionalCastExprClass");
    -            return nullptr;
    -        case Stmt::CXXConstCastExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXConstCastExprClass");
    -            return nullptr;
    -        case Stmt::CXXDynamicCastExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDynamicCastExprClass");
    -            return nullptr;
    -        case Stmt::CXXReinterpretCastExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXReinterpretCastExprClass");
    -            return nullptr;
    -        case Stmt::CXXStaticCastExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXStaticCastExprClass");
    -            return nullptr;
    -        case Stmt::ObjCBridgedCastExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCBridgedCastExprClass");
    -            return nullptr;
    -        case Stmt::CharacterLiteralClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CharacterLiteralClass");
    -            return nullptr;
    -        case Stmt::ChooseExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ChooseExprClass");
    -            return nullptr;
    -        case Stmt::CompoundLiteralExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CompoundLiteralExprClass");
    -            return nullptr;
    -        case Stmt::ConvertVectorExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ConvertVectorExprClass");
    -            return nullptr;
    -        case Stmt::CoawaitExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CoawaitExprClass");
    -            return nullptr;
    -        case Stmt::CoyieldExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C CoyieldExprClass");
    -            return nullptr;
    -        case Stmt::DependentCoawaitExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C DependentCoawaitExprClass");
    -            return nullptr;
    -        case Stmt::DependentScopeDeclRefExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C DependentScopeDeclRefExprClass");
    -            return nullptr;
    -        case Stmt::DesignatedInitExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C DesignatedInitExprClass");
    -            return nullptr;
    -        case Stmt::DesignatedInitUpdateExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C DesignatedInitUpdateExprClass");
    -            return nullptr;
    -        case Stmt::ExprWithCleanupsClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ExprWithCleanupsClass");
    -            return nullptr;
    -        case Stmt::ExpressionTraitExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ExpressionTraitExprClass");
    -            return nullptr;
    -        case Stmt::ExtVectorElementExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ExtVectorElementExprClass");
    -            return nullptr;
    -        case Stmt::FloatingLiteralClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C FloatingLiteralClass");
    -            return nullptr;
    -        case Stmt::FunctionParmPackExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C FunctionParmPackExprClass");
    -            return nullptr;
    -        case Stmt::GNUNullExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C GNUNullExprClass");
    -            return nullptr;
    -        case Stmt::GenericSelectionExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C GenericSelectionExprClass");
    -            return nullptr;
    -        case Stmt::ImaginaryLiteralClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ImaginaryLiteralClass");
    -            return nullptr;
    -        case Stmt::ImplicitValueInitExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ImplicitValueInitExprClass");
    -            return nullptr;
    -        case Stmt::InitListExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C InitListExprClass");
    -            return nullptr;
    -        case Stmt::LambdaExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C LambdaExprClass");
    -            return nullptr;
    -        case Stmt::MSPropertyRefExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C MSPropertyRefExprClass");
    -            return nullptr;
    -        case Stmt::MSPropertySubscriptExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C MSPropertySubscriptExprClass");
    -            return nullptr;
    -        case Stmt::MaterializeTemporaryExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C MaterializeTemporaryExprClass");
    -            return nullptr;
    -        case Stmt::NoInitExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C NoInitExprClass");
    -            return nullptr;
    -        case Stmt::OMPArraySectionExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPArraySectionExprClass");
    -            return nullptr;
    -        case Stmt::ObjCArrayLiteralClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCArrayLiteralClass");
    -            return nullptr;
    -        case Stmt::ObjCAvailabilityCheckExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAvailabilityCheckExprClass");
    -            return nullptr;
    -        case Stmt::ObjCBoolLiteralExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCBoolLiteralExprClass");
    -            return nullptr;
    -        case Stmt::ObjCBoxedExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCBoxedExprClass");
    -            return nullptr;
    -        case Stmt::ObjCDictionaryLiteralClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCDictionaryLiteralClass");
    -            return nullptr;
    -        case Stmt::ObjCEncodeExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCEncodeExprClass");
    -            return nullptr;
    -        case Stmt::ObjCIndirectCopyRestoreExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCIndirectCopyRestoreExprClass");
    -            return nullptr;
    -        case Stmt::ObjCIsaExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCIsaExprClass");
    -            return nullptr;
    -        case Stmt::ObjCIvarRefExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCIvarRefExprClass");
    -            return nullptr;
    -        case Stmt::ObjCMessageExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCMessageExprClass");
    -            return nullptr;
    -        case Stmt::ObjCPropertyRefExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCPropertyRefExprClass");
    -            return nullptr;
    -        case Stmt::ObjCProtocolExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCProtocolExprClass");
    -            return nullptr;
    -        case Stmt::ObjCSelectorExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCSelectorExprClass");
    -            return nullptr;
    -        case Stmt::ObjCStringLiteralClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCStringLiteralClass");
    -            return nullptr;
    -        case Stmt::ObjCSubscriptRefExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCSubscriptRefExprClass");
    -            return nullptr;
    -        case Stmt::OffsetOfExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OffsetOfExprClass");
    -            return nullptr;
    -        case Stmt::OpaqueValueExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OpaqueValueExprClass");
    -            return nullptr;
    -        case Stmt::UnresolvedLookupExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C UnresolvedLookupExprClass");
    -            return nullptr;
    -        case Stmt::UnresolvedMemberExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C UnresolvedMemberExprClass");
    -            return nullptr;
    -        case Stmt::PackExpansionExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C PackExpansionExprClass");
    -            return nullptr;
    -        case Stmt::ParenExprClass:
    -            return trans_expr(c, result_used, block, ((ParenExpr*)stmt)->getSubExpr(), lrvalue);
    -        case Stmt::ParenListExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ParenListExprClass");
    -            return nullptr;
    -        case Stmt::PredefinedExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C PredefinedExprClass");
    -            return nullptr;
    -        case Stmt::PseudoObjectExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C PseudoObjectExprClass");
    -            return nullptr;
    -        case Stmt::ShuffleVectorExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ShuffleVectorExprClass");
    -            return nullptr;
    -        case Stmt::SizeOfPackExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C SizeOfPackExprClass");
    -            return nullptr;
    -        case Stmt::StmtExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C StmtExprClass");
    -            return nullptr;
    -        case Stmt::StringLiteralClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C StringLiteralClass");
    -            return nullptr;
    -        case Stmt::SubstNonTypeTemplateParmExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C SubstNonTypeTemplateParmExprClass");
    -            return nullptr;
    -        case Stmt::SubstNonTypeTemplateParmPackExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C SubstNonTypeTemplateParmPackExprClass");
    -            return nullptr;
    -        case Stmt::TypeTraitExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C TypeTraitExprClass");
    -            return nullptr;
    -        case Stmt::TypoExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C TypoExprClass");
    -            return nullptr;
    -        case Stmt::VAArgExprClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C VAArgExprClass");
    -            return nullptr;
    -        case Stmt::ForStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ForStmtClass");
    -            return nullptr;
    -        case Stmt::GotoStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C GotoStmtClass");
    -            return nullptr;
    -        case Stmt::IndirectGotoStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C IndirectGotoStmtClass");
    -            return nullptr;
    -        case Stmt::LabelStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C LabelStmtClass");
    -            return nullptr;
    -        case Stmt::MSDependentExistsStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C MSDependentExistsStmtClass");
    -            return nullptr;
    -        case Stmt::OMPAtomicDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPAtomicDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPBarrierDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPBarrierDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPCancelDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPCancelDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPCancellationPointDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPCancellationPointDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPCriticalDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPCriticalDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPFlushDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPFlushDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPDistributeDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPDistributeParallelForDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeParallelForDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPDistributeParallelForSimdDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeParallelForSimdDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPDistributeSimdDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeSimdDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPForDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPForDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPForSimdDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPForSimdDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPParallelForDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelForDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPParallelForSimdDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelForSimdDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPSimdDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSimdDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTargetParallelForSimdDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetParallelForSimdDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTargetSimdDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetSimdDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTargetTeamsDistributeDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeParallelForDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeParallelForSimdDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeSimdDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTaskLoopDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskLoopDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTaskLoopSimdDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskLoopSimdDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTeamsDistributeDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTeamsDistributeParallelForDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeParallelForDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeParallelForSimdDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTeamsDistributeSimdDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeSimdDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPMasterDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPMasterDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPOrderedDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPOrderedDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPParallelDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPParallelSectionsDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelSectionsDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPSectionDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSectionDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPSectionsDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSectionsDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPSingleDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSingleDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTargetDataDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetDataDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTargetDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTargetEnterDataDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetEnterDataDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTargetExitDataDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetExitDataDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTargetParallelDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetParallelDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTargetParallelForDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetParallelForDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTargetTeamsDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTargetUpdateDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetUpdateDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTaskDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTaskgroupDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskgroupDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTaskwaitDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskwaitDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTaskyieldDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskyieldDirectiveClass");
    -            return nullptr;
    -        case Stmt::OMPTeamsDirectiveClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDirectiveClass");
    -            return nullptr;
    -        case Stmt::ObjCAtCatchStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtCatchStmtClass");
    -            return nullptr;
    -        case Stmt::ObjCAtFinallyStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtFinallyStmtClass");
    -            return nullptr;
    -        case Stmt::ObjCAtSynchronizedStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtSynchronizedStmtClass");
    -            return nullptr;
    -        case Stmt::ObjCAtThrowStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtThrowStmtClass");
    -            return nullptr;
    -        case Stmt::ObjCAtTryStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtTryStmtClass");
    -            return nullptr;
    -        case Stmt::ObjCAutoreleasePoolStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAutoreleasePoolStmtClass");
    -            return nullptr;
    -        case Stmt::ObjCForCollectionStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCForCollectionStmtClass");
    -            return nullptr;
    -        case Stmt::SEHExceptStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C SEHExceptStmtClass");
    -            return nullptr;
    -        case Stmt::SEHFinallyStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C SEHFinallyStmtClass");
    -            return nullptr;
    -        case Stmt::SEHLeaveStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C SEHLeaveStmtClass");
    -            return nullptr;
    -        case Stmt::SEHTryStmtClass:
    -            emit_warning(c, stmt->getLocStart(), "TODO handle C SEHTryStmtClass");
    +static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const SwitchStmt *stmt) {
    +    TransScopeBlock *block_scope = trans_scope_block_create(c, parent_scope);
    +
    +    TransScopeSwitch *switch_scope;
    +
    +    const DeclStmt *var_decl_stmt = stmt->getConditionVariableDeclStmt();
    +    if (var_decl_stmt == nullptr) {
    +        switch_scope = trans_scope_switch_create(c, &block_scope->base);
    +    } else {
    +        AstNode *vars_node;
    +        TransScope *var_scope = trans_stmt(c, &block_scope->base, var_decl_stmt, &vars_node);
    +        if (var_scope == nullptr)
    +            return nullptr;
    +        if (vars_node != nullptr)
    +            block_scope->node->data.block.statements.append(vars_node);
    +        switch_scope = trans_scope_switch_create(c, var_scope);
    +    }
    +    block_scope->node->data.block.statements.append(switch_scope->switch_node);
    +
    +    // TODO avoid name collisions
    +    Buf *end_label_name = buf_create_from_str("end");
    +    switch_scope->end_label_name = end_label_name;
    +
    +    const Expr *cond_expr = stmt->getCond();
    +    assert(cond_expr != nullptr);
    +
    +    AstNode *expr_node = trans_expr(c, ResultUsedYes, &block_scope->base, cond_expr, TransRValue);
    +    if (expr_node == nullptr)
    +        return nullptr;
    +    switch_scope->switch_node->data.switch_expr.expr = expr_node;
    +
    +    AstNode *body_node;
    +    const Stmt *body_stmt = stmt->getBody();
    +    if (body_stmt->getStmtClass() == Stmt::CompoundStmtClass) {
    +        if (trans_compound_stmt_inline(c, &switch_scope->base, (const CompoundStmt *)body_stmt,
    +            block_scope->node, nullptr))
    +        {
    +            return nullptr;
    +        }
    +    } else {
    +        TransScope *body_scope = trans_stmt(c, &switch_scope->base, body_stmt, &body_node);
    +        if (body_scope == nullptr)
    +            return nullptr;
    +        if (body_node != nullptr)
    +            block_scope->node->data.block.statements.append(body_node);
    +    }
    +
    +    if (!switch_scope->found_default && !stmt->isAllEnumCasesCovered()) {
    +        AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
    +        prong_node->data.switch_prong.expr = trans_create_node_goto(c, end_label_name);
    +        switch_scope->switch_node->data.switch_expr.prongs.append(prong_node);
    +    }
    +
    +    // This is necessary if the last switch case "falls through" the end of the switch block
    +    block_scope->node->data.block.statements.append(trans_create_node_goto(c, end_label_name));
    +
    +    block_scope->node->data.block.statements.append(trans_create_node_label(c, end_label_name));
    +
    +    return block_scope->node;
    +}
    +
    +static int trans_switch_case(Context *c, TransScope *parent_scope, const CaseStmt *stmt, AstNode **out_node,
    +        TransScope **out_scope)
    +{
    +    *out_node = nullptr;
    +
    +    if (stmt->getRHS() != nullptr) {
    +        emit_warning(c, stmt->getLocStart(), "TODO support GNU switch case a ... b extension");
    +        return ErrorUnexpected;
    +    }
    +
    +    TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope);
    +    assert(switch_scope != nullptr);
    +
    +    Buf *label_name = buf_sprintf("case_%" PRIu32, switch_scope->case_index);
    +    switch_scope->case_index += 1;
    +
    +    {
    +        // Add the prong
    +        AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
    +        AstNode *item_node = trans_expr(c, ResultUsedYes, &switch_scope->base, stmt->getLHS(), TransRValue);
    +        if (item_node == nullptr)
    +            return ErrorUnexpected;
    +        prong_node->data.switch_prong.items.append(item_node);
    +
    +        prong_node->data.switch_prong.expr = trans_create_node_goto(c, label_name);
    +
    +        switch_scope->switch_node->data.switch_expr.prongs.append(prong_node);
    +    }
    +
    +    TransScopeBlock *scope_block = trans_scope_block_find(parent_scope);
    +    scope_block->node->data.block.statements.append(trans_create_node_label(c, label_name));
    +
    +    AstNode *sub_stmt_node;
    +    TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node);
    +    if (new_scope == nullptr)
    +        return ErrorUnexpected;
    +    if (sub_stmt_node != nullptr)
    +        scope_block->node->data.block.statements.append(sub_stmt_node);
    +
    +    *out_scope = new_scope;
    +    return ErrorNone;
    +}
    +
    +static int trans_switch_default(Context *c, TransScope *parent_scope, const DefaultStmt *stmt, AstNode **out_node,
    +        TransScope **out_scope)
    +{
    +    *out_node = nullptr;
    +
    +    TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope);
    +    assert(switch_scope != nullptr);
    +
    +    Buf *label_name = buf_sprintf("default");
    +
    +    {
    +        // Add the prong
    +        AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
    +
    +        prong_node->data.switch_prong.expr = trans_create_node_goto(c, label_name);
    +
    +        switch_scope->switch_node->data.switch_expr.prongs.append(prong_node);
    +        switch_scope->found_default = true;
    +    }
    +
    +    TransScopeBlock *scope_block = trans_scope_block_find(parent_scope);
    +    scope_block->node->data.block.statements.append(trans_create_node_label(c, label_name));
    +
    +
    +    AstNode *sub_stmt_node;
    +    TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node);
    +    if (new_scope == nullptr)
    +        return ErrorUnexpected;
    +    if (sub_stmt_node != nullptr)
    +        scope_block->node->data.block.statements.append(sub_stmt_node);
    +
    +    *out_scope = new_scope;
    +    return ErrorNone;
    +}
    +
    +static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForStmt *stmt) {
    +    AstNode *loop_block_node;
    +    TransScopeWhile *while_scope;
    +    TransScope *cond_scope;
    +    const Stmt *init_stmt = stmt->getInit();
    +    if (init_stmt == nullptr) {
    +        while_scope = trans_scope_while_create(c, parent_scope);
    +        loop_block_node = while_scope->node;
    +        cond_scope = parent_scope;
    +    } else {
    +        TransScopeBlock *child_scope = trans_scope_block_create(c, parent_scope);
    +        loop_block_node = child_scope->node;
    +
    +        AstNode *vars_node;
    +        cond_scope = trans_stmt(c, &child_scope->base, init_stmt, &vars_node);
    +        if (cond_scope == nullptr)
    +            return nullptr;
    +        if (vars_node != nullptr)
    +            child_scope->node->data.block.statements.append(vars_node);
    +
    +        while_scope = trans_scope_while_create(c, cond_scope);
    +
    +        child_scope->node->data.block.statements.append(while_scope->node);
    +    }
    +
    +    const Stmt *cond_stmt = stmt->getCond();
    +    if (cond_stmt == nullptr) {
    +        while_scope->node->data.while_expr.condition = trans_create_node_bool(c, true);
    +    } else {
    +        TransScope *end_cond_scope = trans_stmt(c, cond_scope, cond_stmt,
    +                &while_scope->node->data.while_expr.condition);
    +        if (end_cond_scope == nullptr)
    +            return nullptr;
    +    }
    +
    +    const Stmt *inc_stmt = stmt->getInc();
    +    if (inc_stmt != nullptr) {
    +        AstNode *inc_node;
    +        TransScope *inc_scope = trans_stmt(c, cond_scope, inc_stmt, &inc_node);
    +        if (inc_scope == nullptr)
    +            return nullptr;
    +        while_scope->node->data.while_expr.continue_expr = inc_node;
    +    }
    +
    +    AstNode *body_statement;
    +    TransScope *body_scope = trans_stmt(c, &while_scope->base, stmt->getBody(), &body_statement);
    +    if (body_scope == nullptr)
    +        return nullptr;
    +    while_scope->node->data.while_expr.body = body_statement;
    +
    +    return loop_block_node;
    +}
    +
    +static AstNode *trans_string_literal(Context *c, TransScope *scope, const StringLiteral *stmt) {
    +    switch (stmt->getKind()) {
    +        case StringLiteral::Ascii:
    +        case StringLiteral::UTF8:
    +            return trans_create_node_str_lit_c(c, string_ref_to_buf(stmt->getString()));
    +        case StringLiteral::UTF16:
    +            emit_warning(c, stmt->getLocStart(), "TODO support UTF16 string literals");
    +            return nullptr;
    +        case StringLiteral::UTF32:
    +            emit_warning(c, stmt->getLocStart(), "TODO support UTF32 string literals");
    +            return nullptr;
    +        case StringLiteral::Wide:
    +            emit_warning(c, stmt->getLocStart(), "TODO support wide string literals");
                 return nullptr;
         }
         zig_unreachable();
     }
     
    +static AstNode *trans_break_stmt(Context *c, TransScope *scope, const BreakStmt *stmt) {
    +    TransScope *cur_scope = scope;
    +    while (cur_scope != nullptr) {
    +        if (cur_scope->id == TransScopeIdWhile) {
    +            return trans_create_node(c, NodeTypeBreak);
    +        } else if (cur_scope->id == TransScopeIdSwitch) {
    +            TransScopeSwitch *switch_scope = (TransScopeSwitch *)cur_scope;
    +            return trans_create_node_goto(c, switch_scope->end_label_name);
    +        }
    +        cur_scope = cur_scope->parent;
    +    }
    +    zig_unreachable();
    +}
    +
    +static AstNode *trans_continue_stmt(Context *c, TransScope *scope, const ContinueStmt *stmt) {
    +    return trans_create_node(c, NodeTypeContinue);
    +}
    +
    +static int wrap_stmt(AstNode **out_node, TransScope **out_scope, TransScope *in_scope, AstNode *result_node) {
    +    if (result_node == nullptr)
    +        return ErrorUnexpected;
    +    *out_node = result_node;
    +    if (out_scope != nullptr)
    +        *out_scope = in_scope;
    +    return ErrorNone;
    +}
    +
    +static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
    +        ResultUsed result_used, TransLRValue lrvalue,
    +        AstNode **out_node, TransScope **out_child_scope,
    +        TransScope **out_node_scope)
    +{
    +    Stmt::StmtClass sc = stmt->getStmtClass();
    +    switch (sc) {
    +        case Stmt::ReturnStmtClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_return_stmt(c, scope, (const ReturnStmt *)stmt));
    +        case Stmt::CompoundStmtClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_compound_stmt(c, scope, (const CompoundStmt *)stmt, out_node_scope));
    +        case Stmt::IntegerLiteralClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_integer_literal(c, (const IntegerLiteral *)stmt));
    +        case Stmt::ConditionalOperatorClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_conditional_operator(c, result_used, scope, (const ConditionalOperator *)stmt));
    +        case Stmt::BinaryOperatorClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_binary_operator(c, result_used, scope, (const BinaryOperator *)stmt));
    +        case Stmt::CompoundAssignOperatorClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_compound_assign_operator(c, result_used, scope, (const CompoundAssignOperator *)stmt));
    +        case Stmt::ImplicitCastExprClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_implicit_cast_expr(c, scope, (const ImplicitCastExpr *)stmt));
    +        case Stmt::DeclRefExprClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_decl_ref_expr(c, scope, (const DeclRefExpr *)stmt, lrvalue));
    +        case Stmt::UnaryOperatorClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_unary_operator(c, result_used, scope, (const UnaryOperator *)stmt));
    +        case Stmt::DeclStmtClass:
    +            return trans_local_declaration(c, scope, (const DeclStmt *)stmt, out_node, out_child_scope);
    +        case Stmt::WhileStmtClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_while_loop(c, scope, (const WhileStmt *)stmt));
    +        case Stmt::IfStmtClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_if_statement(c, scope, (const IfStmt *)stmt));
    +        case Stmt::CallExprClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_call_expr(c, result_used, scope, (const CallExpr *)stmt));
    +        case Stmt::NullStmtClass:
    +            *out_node = nullptr;
    +            *out_child_scope = scope;
    +            return ErrorNone;
    +        case Stmt::MemberExprClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_member_expr(c, scope, (const MemberExpr *)stmt));
    +        case Stmt::ArraySubscriptExprClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_array_subscript_expr(c, scope, (const ArraySubscriptExpr *)stmt));
    +        case Stmt::CStyleCastExprClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_c_style_cast_expr(c, result_used, scope, (const CStyleCastExpr *)stmt, lrvalue));
    +        case Stmt::UnaryExprOrTypeTraitExprClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_unary_expr_or_type_trait_expr(c, scope, (const UnaryExprOrTypeTraitExpr *)stmt));
    +        case Stmt::DoStmtClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_do_loop(c, scope, (const DoStmt *)stmt));
    +        case Stmt::ForStmtClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_for_loop(c, scope, (const ForStmt *)stmt));
    +        case Stmt::StringLiteralClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_string_literal(c, scope, (const StringLiteral *)stmt));
    +        case Stmt::BreakStmtClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_break_stmt(c, scope, (const BreakStmt *)stmt));
    +        case Stmt::ContinueStmtClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_continue_stmt(c, scope, (const ContinueStmt *)stmt));
    +        case Stmt::ParenExprClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_expr(c, result_used, scope, ((const ParenExpr*)stmt)->getSubExpr(), lrvalue));
    +        case Stmt::SwitchStmtClass:
    +            return wrap_stmt(out_node, out_child_scope, scope,
    +                    trans_switch_stmt(c, scope, (const SwitchStmt *)stmt));
    +        case Stmt::CaseStmtClass:
    +            return trans_switch_case(c, scope, (const CaseStmt *)stmt, out_node, out_child_scope);
    +        case Stmt::DefaultStmtClass:
    +            return trans_switch_default(c, scope, (const DefaultStmt *)stmt, out_node, out_child_scope);
    +        case Stmt::NoStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C NoStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::GCCAsmStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C GCCAsmStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::MSAsmStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C MSAsmStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::AttributedStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C AttributedStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXCatchStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXCatchStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXForRangeStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXForRangeStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXTryStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXTryStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::CapturedStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CapturedStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::CoreturnStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CoreturnStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::CoroutineBodyStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CoroutineBodyStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::BinaryConditionalOperatorClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C BinaryConditionalOperatorClass");
    +            return ErrorUnexpected;
    +        case Stmt::AddrLabelExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C AddrLabelExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ArrayInitIndexExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ArrayInitIndexExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ArrayInitLoopExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ArrayInitLoopExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ArrayTypeTraitExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ArrayTypeTraitExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::AsTypeExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C AsTypeExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::AtomicExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C AtomicExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::BlockExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C BlockExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXBindTemporaryExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXBindTemporaryExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXBoolLiteralExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXBoolLiteralExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXConstructExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXConstructExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXTemporaryObjectExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXTemporaryObjectExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXDefaultArgExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDefaultArgExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXDefaultInitExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDefaultInitExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXDeleteExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDeleteExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXDependentScopeMemberExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDependentScopeMemberExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXFoldExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXFoldExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXInheritedCtorInitExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXInheritedCtorInitExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXNewExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXNewExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXNoexceptExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXNoexceptExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXNullPtrLiteralExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXNullPtrLiteralExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXPseudoDestructorExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXPseudoDestructorExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXScalarValueInitExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXScalarValueInitExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXStdInitializerListExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXStdInitializerListExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXThisExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXThisExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXThrowExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXThrowExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXTypeidExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXTypeidExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXUnresolvedConstructExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXUnresolvedConstructExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXUuidofExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXUuidofExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CUDAKernelCallExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CUDAKernelCallExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXMemberCallExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXMemberCallExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXOperatorCallExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXOperatorCallExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::UserDefinedLiteralClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C UserDefinedLiteralClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXFunctionalCastExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXFunctionalCastExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXConstCastExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXConstCastExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXDynamicCastExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXDynamicCastExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXReinterpretCastExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXReinterpretCastExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CXXStaticCastExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CXXStaticCastExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCBridgedCastExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCBridgedCastExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CharacterLiteralClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CharacterLiteralClass");
    +            return ErrorUnexpected;
    +        case Stmt::ChooseExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ChooseExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CompoundLiteralExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CompoundLiteralExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ConvertVectorExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ConvertVectorExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CoawaitExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CoawaitExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::CoyieldExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C CoyieldExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::DependentCoawaitExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C DependentCoawaitExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::DependentScopeDeclRefExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C DependentScopeDeclRefExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::DesignatedInitExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C DesignatedInitExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::DesignatedInitUpdateExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C DesignatedInitUpdateExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ExprWithCleanupsClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ExprWithCleanupsClass");
    +            return ErrorUnexpected;
    +        case Stmt::ExpressionTraitExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ExpressionTraitExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ExtVectorElementExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ExtVectorElementExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::FloatingLiteralClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C FloatingLiteralClass");
    +            return ErrorUnexpected;
    +        case Stmt::FunctionParmPackExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C FunctionParmPackExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::GNUNullExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C GNUNullExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::GenericSelectionExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C GenericSelectionExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ImaginaryLiteralClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ImaginaryLiteralClass");
    +            return ErrorUnexpected;
    +        case Stmt::ImplicitValueInitExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ImplicitValueInitExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::InitListExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C InitListExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::LambdaExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C LambdaExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::MSPropertyRefExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C MSPropertyRefExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::MSPropertySubscriptExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C MSPropertySubscriptExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::MaterializeTemporaryExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C MaterializeTemporaryExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::NoInitExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C NoInitExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPArraySectionExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPArraySectionExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCArrayLiteralClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCArrayLiteralClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCAvailabilityCheckExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAvailabilityCheckExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCBoolLiteralExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCBoolLiteralExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCBoxedExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCBoxedExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCDictionaryLiteralClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCDictionaryLiteralClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCEncodeExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCEncodeExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCIndirectCopyRestoreExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCIndirectCopyRestoreExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCIsaExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCIsaExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCIvarRefExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCIvarRefExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCMessageExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCMessageExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCPropertyRefExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCPropertyRefExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCProtocolExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCProtocolExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCSelectorExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCSelectorExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCStringLiteralClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCStringLiteralClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCSubscriptRefExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCSubscriptRefExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::OffsetOfExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OffsetOfExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::OpaqueValueExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OpaqueValueExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::UnresolvedLookupExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C UnresolvedLookupExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::UnresolvedMemberExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C UnresolvedMemberExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::PackExpansionExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C PackExpansionExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ParenListExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ParenListExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::PredefinedExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C PredefinedExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::PseudoObjectExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C PseudoObjectExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::ShuffleVectorExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ShuffleVectorExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::SizeOfPackExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C SizeOfPackExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::StmtExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C StmtExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::SubstNonTypeTemplateParmExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C SubstNonTypeTemplateParmExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::SubstNonTypeTemplateParmPackExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C SubstNonTypeTemplateParmPackExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::TypeTraitExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C TypeTraitExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::TypoExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C TypoExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::VAArgExprClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C VAArgExprClass");
    +            return ErrorUnexpected;
    +        case Stmt::GotoStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C GotoStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::IndirectGotoStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C IndirectGotoStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::LabelStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C LabelStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::MSDependentExistsStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C MSDependentExistsStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPAtomicDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPAtomicDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPBarrierDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPBarrierDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPCancelDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPCancelDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPCancellationPointDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPCancellationPointDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPCriticalDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPCriticalDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPFlushDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPFlushDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPDistributeDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPDistributeParallelForDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeParallelForDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPDistributeParallelForSimdDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeParallelForSimdDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPDistributeSimdDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPDistributeSimdDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPForDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPForDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPForSimdDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPForSimdDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPParallelForDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelForDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPParallelForSimdDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelForSimdDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPSimdDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSimdDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTargetParallelForSimdDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetParallelForSimdDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTargetSimdDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetSimdDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTargetTeamsDistributeDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeParallelForDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeParallelForSimdDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDistributeSimdDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTaskLoopDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskLoopDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTaskLoopSimdDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskLoopSimdDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTeamsDistributeDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTeamsDistributeParallelForDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeParallelForDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeParallelForSimdDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTeamsDistributeSimdDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDistributeSimdDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPMasterDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPMasterDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPOrderedDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPOrderedDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPParallelDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPParallelSectionsDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPParallelSectionsDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPSectionDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSectionDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPSectionsDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSectionsDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPSingleDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPSingleDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTargetDataDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetDataDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTargetDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTargetEnterDataDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetEnterDataDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTargetExitDataDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetExitDataDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTargetParallelDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetParallelDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTargetParallelForDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetParallelForDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTargetTeamsDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetTeamsDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTargetUpdateDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTargetUpdateDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTaskDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTaskgroupDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskgroupDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTaskwaitDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskwaitDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTaskyieldDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTaskyieldDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::OMPTeamsDirectiveClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C OMPTeamsDirectiveClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCAtCatchStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtCatchStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCAtFinallyStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtFinallyStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCAtSynchronizedStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtSynchronizedStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCAtThrowStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtThrowStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCAtTryStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAtTryStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCAutoreleasePoolStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCAutoreleasePoolStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::ObjCForCollectionStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C ObjCForCollectionStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::SEHExceptStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C SEHExceptStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::SEHFinallyStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C SEHFinallyStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::SEHLeaveStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C SEHLeaveStmtClass");
    +            return ErrorUnexpected;
    +        case Stmt::SEHTryStmtClass:
    +            emit_warning(c, stmt->getLocStart(), "TODO handle C SEHTryStmtClass");
    +            return ErrorUnexpected;
    +    }
    +    zig_unreachable();
    +}
    +
    +// Returns null if there was an error
    +static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr,
    +        TransLRValue lrval)
    +{
    +    AstNode *result_node;
    +    TransScope *result_scope;
    +    if (trans_stmt_extra(c, scope, expr, result_used, lrval, &result_node, &result_scope, nullptr)) {
    +        return nullptr;
    +    }
    +    return result_node;
    +}
    +
    +// Statements have no result and no concept of L or R value.
    +// Returns child scope, or null if there was an error
    +static TransScope *trans_stmt(Context *c, TransScope *scope, const Stmt *stmt, AstNode **out_node) {
    +    TransScope *child_scope;
    +    if (trans_stmt_extra(c, scope, stmt, ResultUsedNo, TransRValue, out_node, &child_scope, nullptr)) {
    +        return nullptr;
    +    }
    +    return child_scope;
    +}
    +
     static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {
         Buf *fn_name = buf_create_from_str(decl_name(fn_decl));
     
    @@ -2677,10 +3237,13 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {
             return;
         }
     
    +    TransScope *scope = &c->global_scope->base;
    +
         for (size_t i = 0; i < proto_node->data.fn_proto.params.length; i += 1) {
             AstNode *param_node = proto_node->data.fn_proto.params.at(i);
             const ParmVarDecl *param = fn_decl->getParamDecl(i);
             const char *name = decl_name(param);
    +
             Buf *proto_param_name;
             if (strlen(name) != 0) {
                 proto_param_name = buf_create_from_str(name);
    @@ -2690,7 +3253,11 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {
                     proto_param_name = buf_sprintf("arg%" ZIG_PRI_usize "", i);
                 }
             }
    -        param_node->data.param_decl.name = proto_param_name;
    +
    +        TransScopeVar *scope_var = trans_scope_var_create(c, scope, proto_param_name);
    +        scope = &scope_var->base;
    +
    +        param_node->data.param_decl.name = scope_var->zig_name;
         }
     
         if (!fn_decl->hasBody()) {
    @@ -2702,16 +3269,17 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {
         // actual function definition with body
         c->ptr_params.clear();
         Stmt *body = fn_decl->getBody();
    -    AstNode *actual_body_node = trans_stmt(c, false, nullptr, body, TransRValue);
    -    assert(actual_body_node != skip_add_to_block_node);
    -    if (actual_body_node == nullptr) {
    +    AstNode *actual_body_node;
    +    TransScope *result_scope = trans_stmt(c, scope, body, &actual_body_node);
    +    if (result_scope == nullptr) {
             emit_warning(c, fn_decl->getLocation(), "unable to translate function");
             return;
         }
    +    assert(actual_body_node != nullptr);
    +    assert(actual_body_node->type == NodeTypeBlock);
     
         // it worked
     
    -    assert(actual_body_node->type == NodeTypeBlock);
         AstNode *body_node_with_param_inits = trans_create_node(c, NodeTypeBlock);
     
         for (size_t i = 0; i < proto_node->data.fn_proto.params.length; i += 1) {
    @@ -2948,16 +3516,24 @@ static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl) {
         }
     
         const char *raw_name = decl_name(record_decl);
    -
    -    if (!record_decl->isStruct()) {
    -        emit_warning(c, record_decl->getLocation(), "skipping record %s, not a struct", raw_name);
    +    const char *container_kind_name;
    +    ContainerKind container_kind;
    +    if (record_decl->isUnion()) {
    +        container_kind_name = "union";
    +        container_kind = ContainerKindUnion;
    +    } else if (record_decl->isStruct()) {
    +        container_kind_name = "struct";
    +        container_kind = ContainerKindStruct;
    +    } else {
    +        emit_warning(c, record_decl->getLocation(), "skipping record %s, not a struct or union", raw_name);
             c->decl_table.put(record_decl->getCanonicalDecl(), nullptr);
             return nullptr;
         }
     
         bool is_anonymous = record_decl->isAnonymousStructOrUnion() || raw_name[0] == 0;
         Buf *bare_name = is_anonymous ? nullptr : buf_create_from_str(raw_name);
    -    Buf *full_type_name = (bare_name == nullptr) ? nullptr : buf_sprintf("struct_%s", buf_ptr(bare_name));
    +    Buf *full_type_name = (bare_name == nullptr) ?
    +        nullptr : buf_sprintf("%s_%s", container_kind_name, buf_ptr(bare_name));
     
         RecordDecl *record_def = record_decl->getDefinition();
         if (record_def == nullptr) {
    @@ -2973,14 +3549,15 @@ static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl) {
             const FieldDecl *field_decl = *it;
     
             if (field_decl->isBitField()) {
    -            emit_warning(c, field_decl->getLocation(), "struct %s demoted to opaque type - has bitfield",
    +            emit_warning(c, field_decl->getLocation(), "%s %s demoted to opaque type - has bitfield",
    +                    container_kind_name,
                         is_anonymous ? "(anon)" : buf_ptr(bare_name));
                 return demote_struct_to_opaque(c, record_decl, full_type_name, bare_name);
             }
         }
     
         AstNode *struct_node = trans_create_node(c, NodeTypeContainerDecl);
    -    struct_node->data.container_decl.kind = ContainerKindStruct;
    +    struct_node->data.container_decl.kind = container_kind;
         struct_node->data.container_decl.layout = ContainerLayoutExtern;
     
         // TODO handle attribute packed
    @@ -3007,7 +3584,8 @@ static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl) {
     
             if (field_node->data.struct_field.type == nullptr) {
                 emit_warning(c, field_decl->getLocation(),
    -                    "struct %s demoted to opaque type - unresolved type",
    +                    "%s %s demoted to opaque type - unresolved type",
    +                    container_kind_name,
                         is_anonymous ? "(anon)" : buf_ptr(bare_name));
     
                 return demote_struct_to_opaque(c, record_decl, full_type_name, bare_name);
    @@ -3025,6 +3603,93 @@ static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl) {
         }
     }
     
    +static AstNode *trans_ap_value(Context *c, APValue *ap_value, QualType qt, const SourceLocation &source_loc) {
    +    switch (ap_value->getKind()) {
    +        case APValue::Int:
    +            return trans_create_node_apint(c, ap_value->getInt());
    +        case APValue::Uninitialized:
    +            return trans_create_node(c, NodeTypeUndefinedLiteral);
    +        case APValue::Array: {
    +            emit_warning(c, source_loc, "TODO add a test case for this code");
    +
    +            unsigned init_count = ap_value->getArrayInitializedElts();
    +            unsigned all_count = ap_value->getArraySize();
    +            unsigned leftover_count = all_count - init_count;
    +            AstNode *init_node = trans_create_node(c, NodeTypeContainerInitExpr);
    +            AstNode *arr_type_node = trans_qual_type(c, qt, source_loc);
    +            init_node->data.container_init_expr.type = arr_type_node;
    +            init_node->data.container_init_expr.kind = ContainerInitKindArray;
    +
    +            QualType child_qt = qt.getTypePtr()->getLocallyUnqualifiedSingleStepDesugaredType();
    +
    +            for (size_t i = 0; i < init_count; i += 1) {
    +                APValue &elem_ap_val = ap_value->getArrayInitializedElt(i);
    +                AstNode *elem_node = trans_ap_value(c, &elem_ap_val, child_qt, source_loc);
    +                if (elem_node == nullptr)
    +                    return nullptr;
    +                init_node->data.container_init_expr.entries.append(elem_node);
    +            }
    +            if (leftover_count == 0) {
    +                return init_node;
    +            }
    +
    +            APValue &filler_ap_val = ap_value->getArrayFiller();
    +            AstNode *filler_node = trans_ap_value(c, &filler_ap_val, child_qt, source_loc);
    +            if (filler_node == nullptr)
    +                return nullptr;
    +
    +            AstNode *filler_arr_1 = trans_create_node(c, NodeTypeContainerInitExpr);
    +            init_node->data.container_init_expr.type = arr_type_node;
    +            init_node->data.container_init_expr.kind = ContainerInitKindArray;
    +            init_node->data.container_init_expr.entries.append(filler_node);
    +
    +            AstNode *rhs_node;
    +            if (leftover_count == 1) {
    +                rhs_node = filler_arr_1;
    +            } else {
    +                AstNode *amt_node = trans_create_node_unsigned(c, leftover_count);
    +                rhs_node = trans_create_node_bin_op(c, filler_arr_1, BinOpTypeArrayMult, amt_node);
    +            }
    +
    +            return trans_create_node_bin_op(c, init_node, BinOpTypeArrayCat, rhs_node);
    +        }
    +        case APValue::LValue: {
    +            const APValue::LValueBase lval_base = ap_value->getLValueBase();
    +            if (const Expr *expr = lval_base.dyn_cast()) {
    +                return trans_expr(c, ResultUsedYes, &c->global_scope->base, expr, TransRValue);
    +            }
    +            //const ValueDecl *value_decl = lval_base.get();
    +            emit_warning(c, source_loc, "TODO handle initializer LValue ValueDecl");
    +            return nullptr;
    +        }
    +        case APValue::Float:
    +            emit_warning(c, source_loc, "unsupported initializer value kind: Float");
    +            return nullptr;
    +        case APValue::ComplexInt:
    +            emit_warning(c, source_loc, "unsupported initializer value kind: ComplexInt");
    +            return nullptr;
    +        case APValue::ComplexFloat:
    +            emit_warning(c, source_loc, "unsupported initializer value kind: ComplexFloat");
    +            return nullptr;
    +        case APValue::Vector:
    +            emit_warning(c, source_loc, "unsupported initializer value kind: Vector");
    +            return nullptr;
    +        case APValue::Struct:
    +            emit_warning(c, source_loc, "unsupported initializer value kind: Struct");
    +            return nullptr;
    +        case APValue::Union:
    +            emit_warning(c, source_loc, "unsupported initializer value kind: Union");
    +            return nullptr;
    +        case APValue::MemberPointer:
    +            emit_warning(c, source_loc, "unsupported initializer value kind: MemberPointer");
    +            return nullptr;
    +        case APValue::AddrLabelDiff:
    +            emit_warning(c, source_loc, "unsupported initializer value kind: AddrLabelDiff");
    +            return nullptr;
    +    }
    +    zig_unreachable();
    +}
    +
     static void visit_var_decl(Context *c, const VarDecl *var_decl) {
         Buf *name = buf_create_from_str(decl_name(var_decl));
     
    @@ -3061,27 +3726,9 @@ static void visit_var_decl(Context *c, const VarDecl *var_decl) {
                             "ignoring variable '%s' - unable to evaluate initializer", buf_ptr(name));
                     return;
                 }
    -            switch (ap_value->getKind()) {
    -                case APValue::Int:
    -                    init_node = trans_create_node_apint(c, ap_value->getInt());
    -                    break;
    -                case APValue::Uninitialized:
    -                    init_node = trans_create_node(c, NodeTypeUndefinedLiteral);
    -                    break;
    -                case APValue::Float:
    -                case APValue::ComplexInt:
    -                case APValue::ComplexFloat:
    -                case APValue::LValue:
    -                case APValue::Vector:
    -                case APValue::Array:
    -                case APValue::Struct:
    -                case APValue::Union:
    -                case APValue::MemberPointer:
    -                case APValue::AddrLabelDiff:
    -                    emit_warning(c, var_decl->getLocation(),
    -                            "ignoring variable '%s' - unrecognized initializer value kind", buf_ptr(name));
    -                    return;
    -            }
    +            init_node = trans_ap_value(c, ap_value, qt, var_decl->getLocation());
    +            if (init_node == nullptr)
    +                return;
             } else {
                 init_node = trans_create_node(c, NodeTypeUndefinedLiteral);
             }
    @@ -3129,20 +3776,183 @@ static bool decl_visitor(void *context, const Decl *decl) {
         return true;
     }
     
    -static bool name_exists(Context *c, Buf *name) {
    +static bool name_exists_global(Context *c, Buf *name) {
         return get_global(c, name) != nullptr;
     }
     
    +static bool name_exists_scope(Context *c, Buf *name, TransScope *scope) {
    +    while (scope != nullptr) {
    +        if (scope->id == TransScopeIdVar) {
    +            TransScopeVar *var_scope = (TransScopeVar *)scope;
    +            if (buf_eql_buf(name, var_scope->zig_name)) {
    +                return true;
    +            }
    +        }
    +        scope = scope->parent;
    +    }
    +    return name_exists_global(c, name);
    +}
    +
    +static Buf *get_unique_name(Context *c, Buf *name, TransScope *scope) {
    +    Buf *proposed_name = name;
    +    int count = 0;
    +    while (name_exists_scope(c, proposed_name, scope)) {
    +        if (proposed_name == name) {
    +            proposed_name = buf_alloc();
    +        }
    +        buf_resize(proposed_name, 0);
    +        buf_appendf(proposed_name, "%s_%d", buf_ptr(name), count);
    +        count += 1;
    +    }
    +    return proposed_name;
    +}
    +
    +static TransScopeRoot *trans_scope_root_create(Context *c) {
    +    TransScopeRoot *result = allocate(1);
    +    result->base.id = TransScopeIdRoot;
    +    return result;
    +}
    +
    +static TransScopeWhile *trans_scope_while_create(Context *c, TransScope *parent_scope) {
    +    TransScopeWhile *result = allocate(1);
    +    result->base.id = TransScopeIdWhile;
    +    result->base.parent = parent_scope;
    +    result->node = trans_create_node(c, NodeTypeWhileExpr);
    +    return result;
    +}
    +
    +static TransScopeBlock *trans_scope_block_create(Context *c, TransScope *parent_scope) {
    +    TransScopeBlock *result = allocate(1);
    +    result->base.id = TransScopeIdBlock;
    +    result->base.parent = parent_scope;
    +    result->node = trans_create_node(c, NodeTypeBlock);
    +    return result;
    +}
    +
    +static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scope, Buf *wanted_name) {
    +    TransScopeVar *result = allocate(1);
    +    result->base.id = TransScopeIdVar;
    +    result->base.parent = parent_scope;
    +    result->c_name = wanted_name;
    +    result->zig_name = get_unique_name(c, wanted_name, parent_scope);
    +    return result;
    +}
    +
    +static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope) {
    +    TransScopeSwitch *result = allocate(1);
    +    result->base.id = TransScopeIdSwitch;
    +    result->base.parent = parent_scope;
    +    result->switch_node = trans_create_node(c, NodeTypeSwitchExpr);
    +    return result;
    +}
    +
    +static TransScopeBlock *trans_scope_block_find(TransScope *scope) {
    +    while (scope != nullptr) {
    +        if (scope->id == TransScopeIdBlock) {
    +            return (TransScopeBlock *)scope;
    +        }
    +        scope = scope->parent;
    +    }
    +    return nullptr;
    +}
    +
    +static TransScopeSwitch *trans_scope_switch_find(TransScope *scope) {
    +    while (scope != nullptr) {
    +        if (scope->id == TransScopeIdSwitch) {
    +            return (TransScopeSwitch *)scope;
    +        }
    +        scope = scope->parent;
    +    }
    +    return nullptr;
    +}
    +
     static void render_aliases(Context *c) {
         for (size_t i = 0; i < c->aliases.length; i += 1) {
             Alias *alias = &c->aliases.at(i);
    -        if (name_exists(c, alias->new_name))
    +        if (name_exists_global(c, alias->new_name))
                 continue;
     
             add_global_var(c, alias->new_name, trans_create_node_symbol(c, alias->canon_name));
         }
     }
     
    +static AstNode *trans_lookup_ast_container_typeof(Context *c, AstNode *ref_node);
    +
    +static AstNode *trans_lookup_ast_container(Context *c, AstNode *type_node) {
    +    if (type_node == nullptr) {
    +        return nullptr;
    +    } else if (type_node->type == NodeTypeContainerDecl) {
    +        return type_node;
    +    } else if (type_node->type == NodeTypePrefixOpExpr) {
    +        return type_node;
    +    } else if (type_node->type == NodeTypeSymbol) {
    +        AstNode *existing_node = get_global(c, type_node->data.symbol_expr.symbol);
    +        if (existing_node == nullptr)
    +            return nullptr;
    +        if (existing_node->type != NodeTypeVariableDeclaration)
    +            return nullptr;
    +        return trans_lookup_ast_container(c, existing_node->data.variable_declaration.expr);
    +    } else if (type_node->type == NodeTypeFieldAccessExpr) {
    +        AstNode *container_node = trans_lookup_ast_container_typeof(c, type_node->data.field_access_expr.struct_expr);
    +        if (container_node == nullptr)
    +            return nullptr;
    +        if (container_node->type != NodeTypeContainerDecl)
    +            return container_node;
    +
    +        for (size_t i = 0; i < container_node->data.container_decl.fields.length; i += 1) {
    +            AstNode *field_node = container_node->data.container_decl.fields.items[i];
    +            if (buf_eql_buf(field_node->data.struct_field.name, type_node->data.field_access_expr.field_name)) {
    +                return trans_lookup_ast_container(c, field_node->data.struct_field.type);
    +            }
    +        }
    +        return nullptr;
    +    } else {
    +        return nullptr;
    +    }
    +}
    +
    +static AstNode *trans_lookup_ast_container_typeof(Context *c, AstNode *ref_node) {
    +    if (ref_node->type == NodeTypeSymbol) {
    +        AstNode *existing_node = get_global(c, ref_node->data.symbol_expr.symbol);
    +        if (existing_node == nullptr)
    +            return nullptr;
    +        if (existing_node->type != NodeTypeVariableDeclaration)
    +            return nullptr;
    +        return trans_lookup_ast_container(c, existing_node->data.variable_declaration.type);
    +    } else if (ref_node->type == NodeTypeFieldAccessExpr) {
    +        AstNode *container_node = trans_lookup_ast_container_typeof(c, ref_node->data.field_access_expr.struct_expr);
    +        if (container_node == nullptr)
    +            return nullptr;
    +        if (container_node->type != NodeTypeContainerDecl)
    +            return container_node;
    +        for (size_t i = 0; i < container_node->data.container_decl.fields.length; i += 1) {
    +            AstNode *field_node = container_node->data.container_decl.fields.items[i];
    +            if (buf_eql_buf(field_node->data.struct_field.name, ref_node->data.field_access_expr.field_name)) {
    +                return trans_lookup_ast_container(c, field_node->data.struct_field.type);
    +            }
    +        }
    +        return nullptr;
    +    } else {
    +        return nullptr;
    +    }
    +}
    +
    +static AstNode *trans_lookup_ast_maybe_fn(Context *c, AstNode *ref_node) {
    +    AstNode *prefix_node = trans_lookup_ast_container_typeof(c, ref_node);
    +    if (prefix_node == nullptr)
    +        return nullptr;
    +    if (prefix_node->type != NodeTypePrefixOpExpr)
    +        return nullptr;
    +    if (prefix_node->data.prefix_op_expr.prefix_op != PrefixOpMaybe)
    +        return nullptr;
    +
    +    AstNode *fn_proto_node = prefix_node->data.prefix_op_expr.primary_expr;
    +    if (fn_proto_node->type != NodeTypeFnProto)
    +        return nullptr;
    +
    +    return fn_proto_node;
    +}
    +
     static void render_macros(Context *c) {
         auto it = c->macro_table.entry_iterator();
         for (;;) {
    @@ -3150,9 +3960,16 @@ static void render_macros(Context *c) {
             if (!entry)
                 break;
     
    +        AstNode *proto_node;
             AstNode *value_node = entry->value;
             if (value_node->type == NodeTypeFnDef) {
                 add_top_level_decl(c, value_node->data.fn_def.fn_proto->data.fn_proto.name, value_node);
    +        } else if ((proto_node = trans_lookup_ast_maybe_fn(c, value_node))) {
    +            // If a macro aliases a global variable which is a function pointer, we conclude that
    +            // the macro is intended to represent a function that assumes the function pointer
    +            // variable is non-null and calls it.
    +            AstNode *inline_fn_node = trans_create_node_inline_fn(c, entry->key, value_node, proto_node);
    +            add_top_level_decl(c, entry->key, inline_fn_node);
             } else {
                 add_global_var(c, entry->key, value_node);
             }
    @@ -3203,9 +4020,33 @@ static AstNode *parse_ctok(Context *c, CTokenize *ctok, size_t *tok_i) {
                 return parse_ctok_num_lit(c, ctok, tok_i, false);
             case CTokIdSymbol:
                 {
    -                *tok_i += 1;
    +                bool need_symbol = false;
    +                CTokId curr_id = CTokIdSymbol;
                     Buf *symbol_name = buf_create_from_buf(&tok->data.symbol);
    -                return trans_create_node_symbol(c, symbol_name);
    +                AstNode *curr_node = trans_create_node_symbol(c, symbol_name);
    +                AstNode *parent_node = curr_node;
    +                do {
    +                    *tok_i += 1;
    +                    CTok* curr_tok = &ctok->tokens.at(*tok_i);
    +                    if (need_symbol) {
    +                        if (curr_tok->id == CTokIdSymbol) {
    +                            symbol_name = buf_create_from_buf(&curr_tok->data.symbol);
    +                            curr_node = trans_create_node_field_access(c, parent_node, buf_create_from_buf(symbol_name));
    +                            parent_node = curr_node;
    +                            need_symbol = false;
    +                        } else {
    +                            return nullptr;
    +                        }
    +                    } else {
    +                        if (curr_tok->id == CTokIdDot) {
    +                            need_symbol = true;
    +                            continue;
    +                        } else {
    +                            break;
    +                        }
    +                    }
    +                } while (curr_id != CTokIdEOF);
    +                return curr_node;
                 }
             case CTokIdLParen:
                 {
    @@ -3219,6 +4060,7 @@ static AstNode *parse_ctok(Context *c, CTokenize *ctok, size_t *tok_i) {
                     *tok_i += 1;
                     return inner_node;
                 }
    +        case CTokIdDot:
             case CTokIdEOF:
             case CTokIdRParen:
                 // not able to make sense of this
    @@ -3254,40 +4096,8 @@ static void process_macro(Context *c, CTokenize *ctok, Buf *name, const char *ch
             if (buf_eql_buf(name, symbol_name)) {
                 return;
             }
    -        c->macro_symbols.append({name, symbol_name});
    -    } else {
    -        c->macro_table.put(name, result_node);
    -    }
    -}
    -
    -static void process_symbol_macros(Context *c) {
    -    for (size_t i = 0; i < c->macro_symbols.length; i += 1) {
    -        MacroSymbol ms = c->macro_symbols.at(i);
    -
    -        // Check if this macro aliases another top level declaration
    -        AstNode *existing_node = get_global(c, ms.value);
    -        if (!existing_node || name_exists(c, ms.name))
    -            continue;
    -
    -        // If a macro aliases a global variable which is a function pointer, we conclude that
    -        // the macro is intended to represent a function that assumes the function pointer
    -        // variable is non-null and calls it.
    -        if (existing_node->type == NodeTypeVariableDeclaration) {
    -            AstNode *var_type = existing_node->data.variable_declaration.type;
    -            if (var_type != nullptr && var_type->type == NodeTypePrefixOpExpr &&
    -                var_type->data.prefix_op_expr.prefix_op == PrefixOpMaybe)
    -            {
    -                AstNode *fn_proto_node = var_type->data.prefix_op_expr.primary_expr;
    -                if (fn_proto_node->type == NodeTypeFnProto) {
    -                    AstNode *inline_fn_node = trans_create_node_inline_fn(c, ms.name, ms.value, fn_proto_node);
    -                    c->macro_table.put(ms.name, inline_fn_node);
    -                    continue;
    -                }
    -            }
    -        }
    -
    -        add_global_var(c, ms.name, trans_create_node_symbol(c, ms.value));
         }
    +    c->macro_table.put(name, result_node);
     }
     
     static void process_preprocessor_entities(Context *c, ASTUnit &unit) {
    @@ -3315,7 +4125,7 @@ static void process_preprocessor_entities(Context *c, ASTUnit &unit) {
                             continue;
                         }
                         Buf *name = buf_create_from_str(raw_name);
    -                    if (name_exists(c, name)) {
    +                    if (name_exists_global(c, name)) {
                             continue;
                         }
     
    @@ -3363,6 +4173,7 @@ int parse_h_file(ImportTableEntry *import, ZigList *errors, const ch
         c->ptr_params.init(8);
         c->codegen = codegen;
         c->source_node = source_node;
    +    c->global_scope = trans_scope_root_create(c);
     
         ZigList clang_argv = {0};
     
    @@ -3467,7 +4278,7 @@ int parse_h_file(ImportTableEntry *import, ZigList *errors, const ch
                         break;
                 }
                 StringRef msg_str_ref = it->getMessage();
    -            Buf *msg = buf_create_from_str((const char *)msg_str_ref.bytes_begin());
    +            Buf *msg = string_ref_to_buf(msg_str_ref);
                 FullSourceLoc fsl = it->getLocation();
                 if (fsl.hasManager()) {
                     FileID file_id = fsl.getFileID();
    @@ -3480,7 +4291,7 @@ int parse_h_file(ImportTableEntry *import, ZigList *errors, const ch
                     if (filename.empty()) {
                         path = buf_alloc();
                     } else {
    -                    path = buf_create_from_mem((const char *)filename.bytes_begin(), filename.size());
    +                    path = string_ref_to_buf(filename);
                     }
     
                     ErrorMsg *err_msg = err_msg_create_with_offset(path, line, column, offset, source, msg);
    @@ -3504,7 +4315,6 @@ int parse_h_file(ImportTableEntry *import, ZigList *errors, const ch
     
         process_preprocessor_entities(c, *ast_unit);
     
    -    process_symbol_macros(c);
         render_macros(c);
         render_aliases(c);
     
    diff --git a/src/parsec.hpp b/src/translate_c.hpp
    similarity index 100%
    rename from src/parsec.hpp
    rename to src/translate_c.hpp
    diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp
    index 46ac45caff..a74a683358 100644
    --- a/src/zig_llvm.cpp
    +++ b/src/zig_llvm.cpp
    @@ -403,6 +403,10 @@ unsigned ZigLLVMTag_DW_structure_type(void) {
         return dwarf::DW_TAG_structure_type;
     }
     
    +unsigned ZigLLVMTag_DW_union_type(void) {
    +    return dwarf::DW_TAG_union_type;
    +}
    +
     ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved) {
         DIBuilder *di_builder = new DIBuilder(*unwrap(module), allow_unresolved);
         return reinterpret_cast(di_builder);
    diff --git a/src/zig_llvm.hpp b/src/zig_llvm.hpp
    index c7bb211960..2f742566f4 100644
    --- a/src/zig_llvm.hpp
    +++ b/src/zig_llvm.hpp
    @@ -117,6 +117,7 @@ unsigned ZigLLVMEncoding_DW_ATE_signed_char(void);
     unsigned ZigLLVMLang_DW_LANG_C99(void);
     unsigned ZigLLVMTag_DW_variable(void);
     unsigned ZigLLVMTag_DW_structure_type(void);
    +unsigned ZigLLVMTag_DW_union_type(void);
     
     ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved);
     void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module);
    diff --git a/std/base64.zig b/std/base64.zig
    index 5a57e7777f..25e438c4fb 100644
    --- a/std/base64.zig
    +++ b/std/base64.zig
    @@ -1,186 +1,485 @@
     const assert = @import("debug.zig").assert;
     const mem = @import("mem.zig");
     
    -pub const standard_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    +pub const standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    +pub const standard_pad_char = '=';
    +pub const standard_encoder = Base64Encoder.init(standard_alphabet_chars, standard_pad_char);
     
    -pub fn encode(dest: []u8, source: []const u8) -> []u8 {
    -    return encodeWithAlphabet(dest, source, standard_alphabet);
    -}
    +pub const Base64Encoder = struct {
    +    alphabet_chars: []const u8,
    +    pad_char: u8,
     
    -/// invalid characters in source are allowed, but they cause the value of dest to be undefined.
    -pub fn decode(dest: []u8, source: []const u8) -> []u8 {
    -    return decodeWithAlphabet(dest, source, standard_alphabet);
    -}
    +    /// a bunch of assertions, then simply pass the data right through.
    +    pub fn init(alphabet_chars: []const u8, pad_char: u8) -> Base64Encoder {
    +        assert(alphabet_chars.len == 64);
    +        var char_in_alphabet = []bool{false} ** 256;
    +        for (alphabet_chars) |c| {
    +            assert(!char_in_alphabet[c]);
    +            assert(c != pad_char);
    +            char_in_alphabet[c] = true;
    +        }
     
    -pub fn encodeWithAlphabet(dest: []u8, source: []const u8, alphabet: []const u8) -> []u8 {
    -    assert(alphabet.len == 65);
    -    assert(dest.len >= calcEncodedSize(source.len));
    -
    -    var i: usize = 0;
    -    var out_index: usize = 0;
    -    while (i + 2 < source.len) : (i += 3) {
    -        dest[out_index] = alphabet[(source[i] >> 2) & 0x3f];
    -        out_index += 1;
    -
    -        dest[out_index] = alphabet[((source[i] & 0x3) << 4) |
    -                          ((source[i + 1] & 0xf0) >> 4)];
    -        out_index += 1;
    -
    -        dest[out_index] = alphabet[((source[i + 1] & 0xf) << 2) |
    -                          ((source[i + 2] & 0xc0) >> 6)];
    -        out_index += 1;
    -
    -        dest[out_index] = alphabet[source[i + 2] & 0x3f];
    -        out_index += 1;
    +        return Base64Encoder{
    +            .alphabet_chars = alphabet_chars,
    +            .pad_char = pad_char,
    +        };
         }
     
    -    if (i < source.len) {
    -        dest[out_index] = alphabet[(source[i] >> 2) & 0x3f];
    -        out_index += 1;
    +    /// ceil(source_len * 4/3)
    +    pub fn calcSize(source_len: usize) -> usize {
    +        return @divTrunc(source_len + 2, 3) * 4;
    +    }
     
    -        if (i + 1 == source.len) {
    -            dest[out_index] = alphabet[(source[i] & 0x3) << 4];
    +    /// dest.len must be what you get from ::calcSize.
    +    pub fn encode(encoder: &const Base64Encoder, dest: []u8, source: []const u8) {
    +        assert(dest.len == Base64Encoder.calcSize(source.len));
    +
    +        var i: usize = 0;
    +        var out_index: usize = 0;
    +        while (i + 2 < source.len) : (i += 3) {
    +            dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f];
                 out_index += 1;
     
    -            dest[out_index] = alphabet[64];
    -            out_index += 1;
    -        } else {
    -            dest[out_index] = alphabet[((source[i] & 0x3) << 4) |
    +            dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) |
                                   ((source[i + 1] & 0xf0) >> 4)];
                 out_index += 1;
     
    -            dest[out_index] = alphabet[(source[i + 1] & 0xf) << 2];
    +            dest[out_index] = encoder.alphabet_chars[((source[i + 1] & 0xf) << 2) |
    +                              ((source[i + 2] & 0xc0) >> 6)];
    +            out_index += 1;
    +
    +            dest[out_index] = encoder.alphabet_chars[source[i + 2] & 0x3f];
                 out_index += 1;
             }
     
    -        dest[out_index] = alphabet[64];
    -        out_index += 1;
    +        if (i < source.len) {
    +            dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f];
    +            out_index += 1;
    +
    +            if (i + 1 == source.len) {
    +                dest[out_index] = encoder.alphabet_chars[(source[i] & 0x3) << 4];
    +                out_index += 1;
    +
    +                dest[out_index] = encoder.pad_char;
    +                out_index += 1;
    +            } else {
    +                dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) |
    +                                  ((source[i + 1] & 0xf0) >> 4)];
    +                out_index += 1;
    +
    +                dest[out_index] = encoder.alphabet_chars[(source[i + 1] & 0xf) << 2];
    +                out_index += 1;
    +            }
    +
    +            dest[out_index] = encoder.pad_char;
    +            out_index += 1;
    +        }
    +    }
    +};
    +
    +pub const standard_decoder = Base64Decoder.init(standard_alphabet_chars, standard_pad_char);
    +error InvalidPadding;
    +error InvalidCharacter;
    +
    +pub const Base64Decoder = struct {
    +    /// e.g. 'A' => 0.
    +    /// undefined for any value not in the 64 alphabet chars.
    +    char_to_index: [256]u8,
    +    /// true only for the 64 chars in the alphabet, not the pad char.
    +    char_in_alphabet: [256]bool,
    +    pad_char: u8,
    +
    +    pub fn init(alphabet_chars: []const u8, pad_char: u8) -> Base64Decoder {
    +        assert(alphabet_chars.len == 64);
    +
    +        var result = Base64Decoder{
    +            .char_to_index = undefined,
    +            .char_in_alphabet = []bool{false} ** 256,
    +            .pad_char = pad_char,
    +        };
    +
    +        for (alphabet_chars) |c, i| {
    +            assert(!result.char_in_alphabet[c]);
    +            assert(c != pad_char);
    +
    +            result.char_to_index[c] = u8(i);
    +            result.char_in_alphabet[c] = true;
    +        }
    +
    +        return result;
         }
     
    -    return dest[0..out_index];
    -}
    -
    -/// invalid characters in source are allowed, but they cause the value of dest to be undefined.
    -pub fn decodeWithAlphabet(dest: []u8, source: []const u8, alphabet: []const u8) -> []u8 {
    -    assert(alphabet.len == 65);
    -
    -    var ascii6 = []u8{64} ** 256;
    -    for (alphabet) |c, i| {
    -        ascii6[c] = u8(i);
    +    /// If the encoded buffer is detected to be invalid, returns error.InvalidPadding.
    +    pub fn calcSize(decoder: &const Base64Decoder, source: []const u8) -> %usize {
    +        if (source.len % 4 != 0) return error.InvalidPadding;
    +        return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
         }
     
    -    return decodeWithAscii6BitMap(dest, source, ascii6[0..], alphabet[64]);
    -}
    +    /// dest.len must be what you get from ::calcSize.
    +    /// invalid characters result in error.InvalidCharacter.
    +    /// invalid padding results in error.InvalidPadding.
    +    pub fn decode(decoder: &const Base64Decoder, dest: []u8, source: []const u8) -> %void {
    +        assert(dest.len == %%decoder.calcSize(source));
    +        assert(source.len % 4 == 0);
     
    -pub fn decodeWithAscii6BitMap(dest: []u8, source: []const u8, ascii6: []const u8, pad_char: u8) -> []u8 {
    -    assert(ascii6.len == 256);
    -    assert(dest.len >= calcExactDecodedSizeWithPadChar(source, pad_char));
    +        var src_cursor: usize = 0;
    +        var dest_cursor: usize = 0;
     
    -    var src_index: usize = 0;
    -    var dest_index: usize = 0;
    -    var in_buf_len: usize = source.len;
    +        while (src_cursor < source.len) : (src_cursor += 4) {
    +            if (!decoder.char_in_alphabet[source[src_cursor + 0]]) return error.InvalidCharacter;
    +            if (!decoder.char_in_alphabet[source[src_cursor + 1]]) return error.InvalidCharacter;
    +            if (src_cursor < source.len - 4 or source[src_cursor + 3] != decoder.pad_char) {
    +                // common case
    +                if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;
    +                if (!decoder.char_in_alphabet[source[src_cursor + 3]]) return error.InvalidCharacter;
    +                dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 |
    +                                        decoder.char_to_index[source[src_cursor + 1]] >> 4;
    +                dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 |
    +                                        decoder.char_to_index[source[src_cursor + 2]] >> 2;
    +                dest[dest_cursor + 2] = decoder.char_to_index[source[src_cursor + 2]] << 6 |
    +                                        decoder.char_to_index[source[src_cursor + 3]];
    +                dest_cursor += 3;
    +            } else if (source[src_cursor + 2] != decoder.pad_char) {
    +                // one pad char
    +                if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;
    +                dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 |
    +                                        decoder.char_to_index[source[src_cursor + 1]] >> 4;
    +                dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 |
    +                                        decoder.char_to_index[source[src_cursor + 2]] >> 2;
    +                if (decoder.char_to_index[source[src_cursor + 2]] << 6 != 0) return error.InvalidPadding;
    +                dest_cursor += 2;
    +            } else {
    +                // two pad chars
    +                dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 |
    +                                        decoder.char_to_index[source[src_cursor + 1]] >> 4;
    +                if (decoder.char_to_index[source[src_cursor + 1]] << 4 != 0) return error.InvalidPadding;
    +                dest_cursor += 1;
    +            }
    +        }
     
    -    while (in_buf_len > 0 and source[in_buf_len - 1] == pad_char) {
    -        in_buf_len -= 1;
    +        assert(src_cursor == source.len);
    +        assert(dest_cursor == dest.len);
    +    }
    +};
    +
    +error OutputTooSmall;
    +
    +pub const Base64DecoderWithIgnore = struct {
    +    decoder: Base64Decoder,
    +    char_is_ignored: [256]bool,
    +    pub fn init(alphabet_chars: []const u8, pad_char: u8, ignore_chars: []const u8) -> Base64DecoderWithIgnore {
    +        var result = Base64DecoderWithIgnore {
    +            .decoder = Base64Decoder.init(alphabet_chars, pad_char),
    +            .char_is_ignored = []bool{false} ** 256,
    +        };
    +
    +        for (ignore_chars) |c| {
    +            assert(!result.decoder.char_in_alphabet[c]);
    +            assert(!result.char_is_ignored[c]);
    +            assert(result.decoder.pad_char != c);
    +            result.char_is_ignored[c] = true;
    +        }
    +
    +        return result;
         }
     
    -    while (in_buf_len > 4) {
    -        dest[dest_index] = ascii6[source[src_index + 0]] << 2 |
    -                   ascii6[source[src_index + 1]] >> 4;
    -        dest_index += 1;
    -
    -        dest[dest_index] = ascii6[source[src_index + 1]] << 4 |
    -                   ascii6[source[src_index + 2]] >> 2;
    -        dest_index += 1;
    -
    -        dest[dest_index] = ascii6[source[src_index + 2]] << 6 |
    -                   ascii6[source[src_index + 3]];
    -        dest_index += 1;
    -
    -        src_index += 4;
    -        in_buf_len -= 4;
    +    /// If no characters end up being ignored or padding, this will be the exact decoded size.
    +    pub fn calcSizeUpperBound(encoded_len: usize) -> %usize {
    +        return @divTrunc(encoded_len, 4) * 3;
         }
     
    -    if (in_buf_len > 1) {
    -        dest[dest_index] = ascii6[source[src_index + 0]] << 2 |
    -                   ascii6[source[src_index + 1]] >> 4;
    -        dest_index += 1;
    +    /// Invalid characters that are not ignored result in error.InvalidCharacter.
    +    /// Invalid padding results in error.InvalidPadding.
    +    /// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcSizeUpperBound.
    +    /// Returns the number of bytes writen to dest.
    +    pub fn decode(decoder_with_ignore: &const Base64DecoderWithIgnore, dest: []u8, source: []const u8) -> %usize {
    +        const decoder = &const decoder_with_ignore.decoder;
    +
    +        var src_cursor: usize = 0;
    +        var dest_cursor: usize = 0;
    +
    +        while (true) {
    +            // get the next 4 chars, if available
    +            var next_4_chars: [4]u8 = undefined;
    +            var available_chars: usize = 0;
    +            var pad_char_count: usize = 0;
    +            while (available_chars < 4 and src_cursor < source.len) {
    +                var c = source[src_cursor];
    +                src_cursor += 1;
    +
    +                if (decoder.char_in_alphabet[c]) {
    +                    // normal char
    +                    next_4_chars[available_chars] = c;
    +                    available_chars += 1;
    +                } else if (decoder_with_ignore.char_is_ignored[c]) {
    +                    // we're told to skip this one
    +                    continue;
    +                } else if (c == decoder.pad_char) {
    +                    // the padding has begun. count the pad chars.
    +                    pad_char_count += 1;
    +                    while (src_cursor < source.len) {
    +                        c = source[src_cursor];
    +                        src_cursor += 1;
    +                        if (c == decoder.pad_char) {
    +                            pad_char_count += 1;
    +                            if (pad_char_count > 2) return error.InvalidCharacter;
    +                        } else if (decoder_with_ignore.char_is_ignored[c]) {
    +                            // we can even ignore chars during the padding
    +                            continue;
    +                        } else return error.InvalidCharacter;
    +                    }
    +                    break;
    +                } else return error.InvalidCharacter;
    +            }
    +
    +            switch (available_chars) {
    +                4 => {
    +                    // common case
    +                    if (dest_cursor + 3 > dest.len) return error.OutputTooSmall;
    +                    assert(pad_char_count == 0);
    +                    dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 |
    +                                            decoder.char_to_index[next_4_chars[1]] >> 4;
    +                    dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 |
    +                                            decoder.char_to_index[next_4_chars[2]] >> 2;
    +                    dest[dest_cursor + 2] = decoder.char_to_index[next_4_chars[2]] << 6 |
    +                                            decoder.char_to_index[next_4_chars[3]];
    +                    dest_cursor += 3;
    +                    continue;
    +                },
    +                3 => {
    +                    if (dest_cursor + 2 > dest.len) return error.OutputTooSmall;
    +                    if (pad_char_count != 1) return error.InvalidPadding;
    +                    dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 |
    +                                            decoder.char_to_index[next_4_chars[1]] >> 4;
    +                    dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 |
    +                                            decoder.char_to_index[next_4_chars[2]] >> 2;
    +                    if (decoder.char_to_index[next_4_chars[2]] << 6 != 0) return error.InvalidPadding;
    +                    dest_cursor += 2;
    +                    break;
    +                },
    +                2 => {
    +                    if (dest_cursor + 1 > dest.len) return error.OutputTooSmall;
    +                    if (pad_char_count != 2) return error.InvalidPadding;
    +                    dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 |
    +                                            decoder.char_to_index[next_4_chars[1]] >> 4;
    +                    if (decoder.char_to_index[next_4_chars[1]] << 4 != 0) return error.InvalidPadding;
    +                    dest_cursor += 1;
    +                    break;
    +                },
    +                1 => {
    +                    return error.InvalidPadding;
    +                },
    +                0 => {
    +                    if (pad_char_count != 0) return error.InvalidPadding;
    +                    break;
    +                },
    +                else => unreachable,
    +            }
    +        }
    +
    +        assert(src_cursor == source.len);
    +
    +        return dest_cursor;
         }
    -    if (in_buf_len > 2) {
    -        dest[dest_index] = ascii6[source[src_index + 1]] << 4 |
    -                   ascii6[source[src_index + 2]] >> 2;
    -        dest_index += 1;
    -    }
    -    if (in_buf_len > 3) {
    -        dest[dest_index] = ascii6[source[src_index + 2]] << 6 |
    -                   ascii6[source[src_index + 3]];
    -        dest_index += 1;
    +};
    +
    +
    +pub const standard_decoder_unsafe = Base64DecoderUnsafe.init(standard_alphabet_chars, standard_pad_char);
    +
    +pub const Base64DecoderUnsafe = struct {
    +    /// e.g. 'A' => 0.
    +    /// undefined for any value not in the 64 alphabet chars.
    +    char_to_index: [256]u8,
    +    pad_char: u8,
    +
    +    pub fn init(alphabet_chars: []const u8, pad_char: u8) -> Base64DecoderUnsafe {
    +        assert(alphabet_chars.len == 64);
    +        var result = Base64DecoderUnsafe {
    +            .char_to_index = undefined,
    +            .pad_char = pad_char,
    +        };
    +        for (alphabet_chars) |c, i| {
    +            assert(c != pad_char);
    +            result.char_to_index[c] = u8(i);
    +        }
    +        return result;
         }
     
    -    return dest[0..dest_index];
    -}
    -
    -pub fn calcEncodedSize(source_len: usize) -> usize {
    -    return (((source_len * 4) / 3 + 3) / 4) * 4;
    -}
    -
    -/// Computes the upper bound of the decoded size based only on the encoded length.
    -/// To compute the exact decoded size, see ::calcExactDecodedSize
    -pub fn calcMaxDecodedSize(encoded_len: usize) -> usize {
    -    return @divExact(encoded_len * 3,  4);
    -}
    -
    -/// Computes the number of decoded bytes there will be. This function must
    -/// be given the encoded buffer because there might be padding
    -/// bytes at the end ('=' in the standard alphabet)
    -pub fn calcExactDecodedSize(encoded: []const u8) -> usize {
    -    return calcExactDecodedSizeWithAlphabet(encoded, standard_alphabet);
    -}
    -
    -pub fn calcExactDecodedSizeWithAlphabet(encoded: []const u8, alphabet: []const u8) -> usize {
    -    assert(alphabet.len == 65);
    -    return calcExactDecodedSizeWithPadChar(encoded, alphabet[64]);
    -}
    -
    -pub fn calcExactDecodedSizeWithPadChar(encoded: []const u8, pad_char: u8) -> usize {
    -    var buf_len = encoded.len;
    -
    -    while (buf_len > 0 and encoded[buf_len - 1] == pad_char) {
    -        buf_len -= 1;
    +    /// The source buffer must be valid.
    +    pub fn calcSize(decoder: &const Base64DecoderUnsafe, source: []const u8) -> usize {
    +        return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
         }
     
    -    return (buf_len * 3) / 4;
    +    /// dest.len must be what you get from ::calcDecodedSizeExactUnsafe.
    +    /// invalid characters or padding will result in undefined values.
    +    pub fn decode(decoder: &const Base64DecoderUnsafe, dest: []u8, source: []const u8) {
    +        assert(dest.len == decoder.calcSize(source));
    +
    +        var src_index: usize = 0;
    +        var dest_index: usize = 0;
    +        var in_buf_len: usize = source.len;
    +
    +        while (in_buf_len > 0 and source[in_buf_len - 1] == decoder.pad_char) {
    +            in_buf_len -= 1;
    +        }
    +
    +        while (in_buf_len > 4) {
    +            dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 |
    +                               decoder.char_to_index[source[src_index + 1]] >> 4;
    +            dest_index += 1;
    +
    +            dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 |
    +                               decoder.char_to_index[source[src_index + 2]] >> 2;
    +            dest_index += 1;
    +
    +            dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 |
    +                               decoder.char_to_index[source[src_index + 3]];
    +            dest_index += 1;
    +
    +            src_index += 4;
    +            in_buf_len -= 4;
    +        }
    +
    +        if (in_buf_len > 1) {
    +            dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 |
    +                               decoder.char_to_index[source[src_index + 1]] >> 4;
    +            dest_index += 1;
    +        }
    +        if (in_buf_len > 2) {
    +            dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 |
    +                               decoder.char_to_index[source[src_index + 2]] >> 2;
    +            dest_index += 1;
    +        }
    +        if (in_buf_len > 3) {
    +            dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 |
    +                               decoder.char_to_index[source[src_index + 3]];
    +            dest_index += 1;
    +        }
    +    }
    +};
    +
    +fn calcDecodedSizeExactUnsafe(source: []const u8, pad_char: u8) -> usize {
    +    if (source.len == 0) return 0;
    +    var result = @divExact(source.len, 4) * 3;
    +    if (source[source.len - 1] == pad_char) {
    +        result -= 1;
    +        if (source[source.len - 2] == pad_char) {
    +            result -= 1;
    +        }
    +    }
    +    return result;
     }
     
    +
     test "base64" {
    -    testBase64();
    -    comptime testBase64();
    +    @setEvalBranchQuota(5000);
    +    %%testBase64();
    +    comptime %%testBase64();
     }
     
    -fn testBase64() {
    -    testBase64Case("", "");
    -    testBase64Case("f", "Zg==");
    -    testBase64Case("fo", "Zm8=");
    -    testBase64Case("foo", "Zm9v");
    -    testBase64Case("foob", "Zm9vYg==");
    -    testBase64Case("fooba", "Zm9vYmE=");
    -    testBase64Case("foobar", "Zm9vYmFy");
    +fn testBase64() -> %void {
    +    %return testAllApis("",       "");
    +    %return testAllApis("f",      "Zg==");
    +    %return testAllApis("fo",     "Zm8=");
    +    %return testAllApis("foo",    "Zm9v");
    +    %return testAllApis("foob",   "Zm9vYg==");
    +    %return testAllApis("fooba",  "Zm9vYmE=");
    +    %return testAllApis("foobar", "Zm9vYmFy");
    +
    +    %return testDecodeIgnoreSpace("",       " ");
    +    %return testDecodeIgnoreSpace("f",      "Z g= =");
    +    %return testDecodeIgnoreSpace("fo",     "    Zm8=");
    +    %return testDecodeIgnoreSpace("foo",    "Zm9v    ");
    +    %return testDecodeIgnoreSpace("foob",   "Zm9vYg = = ");
    +    %return testDecodeIgnoreSpace("fooba",  "Zm9v YmE=");
    +    %return testDecodeIgnoreSpace("foobar", " Z m 9 v Y m F y ");
    +
    +    // test getting some api errors
    +    %return testError("A",    error.InvalidPadding);
    +    %return testError("AA",   error.InvalidPadding);
    +    %return testError("AAA",  error.InvalidPadding);
    +    %return testError("A..A", error.InvalidCharacter);
    +    %return testError("AA=A", error.InvalidCharacter);
    +    %return testError("AA/=", error.InvalidPadding);
    +    %return testError("A/==", error.InvalidPadding);
    +    %return testError("A===", error.InvalidCharacter);
    +    %return testError("====", error.InvalidCharacter);
    +
    +    %return testOutputTooSmallError("AA==");
    +    %return testOutputTooSmallError("AAA=");
    +    %return testOutputTooSmallError("AAAA");
    +    %return testOutputTooSmallError("AAAAAA==");
     }
     
    -fn testBase64Case(expected_decoded: []const u8, expected_encoded: []const u8) {
    -    const calculated_decoded_len = calcExactDecodedSize(expected_encoded);
    -    assert(calculated_decoded_len == expected_decoded.len);
    +fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) -> %void {
    +    // Base64Encoder
    +    {
    +        var buffer: [0x100]u8 = undefined;
    +        var encoded = buffer[0..Base64Encoder.calcSize(expected_decoded.len)];
    +        standard_encoder.encode(encoded, expected_decoded);
    +        assert(mem.eql(u8, encoded, expected_encoded));
    +    }
     
    -    const calculated_encoded_len = calcEncodedSize(expected_decoded.len);
    -    assert(calculated_encoded_len == expected_encoded.len);
    +    // Base64Decoder
    +    {
    +        var buffer: [0x100]u8 = undefined;
    +        var decoded = buffer[0..%return standard_decoder.calcSize(expected_encoded)];
    +        %return standard_decoder.decode(decoded, expected_encoded);
    +        assert(mem.eql(u8, decoded, expected_decoded));
    +    }
     
    -    var buf: [100]u8 = undefined;
    +    // Base64DecoderWithIgnore
    +    {
    +        const standard_decoder_ignore_nothing = Base64DecoderWithIgnore.init(
    +            standard_alphabet_chars, standard_pad_char, "");
    +        var buffer: [0x100]u8 = undefined;
    +        var decoded = buffer[0..%return Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)];
    +        var written = %return standard_decoder_ignore_nothing.decode(decoded, expected_encoded);
    +        assert(written <= decoded.len);
    +        assert(mem.eql(u8, decoded[0..written], expected_decoded));
    +    }
     
    -    const actual_decoded = decode(buf[0..], expected_encoded);
    -    assert(actual_decoded.len == expected_decoded.len);
    -    assert(mem.eql(u8, expected_decoded, actual_decoded));
    -
    -    const actual_encoded = encode(buf[0..], expected_decoded);
    -    assert(actual_encoded.len == expected_encoded.len);
    -    assert(mem.eql(u8, expected_encoded, actual_encoded));
    +    // Base64DecoderUnsafe
    +    {
    +        var buffer: [0x100]u8 = undefined;
    +        var decoded = buffer[0..standard_decoder_unsafe.calcSize(expected_encoded)];
    +        standard_decoder_unsafe.decode(decoded, expected_encoded);
    +        assert(mem.eql(u8, decoded, expected_decoded));
    +    }
    +}
    +
    +fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) -> %void {
    +    const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
    +        standard_alphabet_chars, standard_pad_char, " ");
    +    var buffer: [0x100]u8 = undefined;
    +    var decoded = buffer[0..%return Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)];
    +    var written = %return standard_decoder_ignore_space.decode(decoded, encoded);
    +    assert(mem.eql(u8, decoded[0..written], expected_decoded));
    +}
    +
    +error ExpectedError;
    +fn testError(encoded: []const u8, expected_err: error) -> %void {
    +    const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
    +        standard_alphabet_chars, standard_pad_char, " ");
    +    var buffer: [0x100]u8 = undefined;
    +    if (standard_decoder.calcSize(encoded)) |decoded_size| {
    +        var decoded = buffer[0..decoded_size];
    +        if (standard_decoder.decode(decoded, encoded)) |_| {
    +            return error.ExpectedError;
    +        } else |err| if (err != expected_err) return err;
    +    } else |err| if (err != expected_err) return err;
    +
    +    if (standard_decoder_ignore_space.decode(buffer[0..], encoded)) |_| {
    +        return error.ExpectedError;
    +    } else |err| if (err != expected_err) return err;
    +}
    +
    +fn testOutputTooSmallError(encoded: []const u8) -> %void {
    +    const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(
    +        standard_alphabet_chars, standard_pad_char, " ");
    +    var buffer: [0x100]u8 = undefined;
    +    var decoded = buffer[0..calcDecodedSizeExactUnsafe(encoded, standard_pad_char) - 1];
    +    if (standard_decoder_ignore_space.decode(decoded, encoded)) |_| {
    +        return error.ExpectedError;
    +    } else |err| if (err != error.OutputTooSmall) return err;
     }
    diff --git a/std/os/index.zig b/std/os/index.zig
    index c3f33c4ccd..e6a5fc4d15 100644
    --- a/std/os/index.zig
    +++ b/std/os/index.zig
    @@ -31,6 +31,8 @@ pub const windowsWaitSingle = windows_util.windowsWaitSingle;
     pub const windowsWrite = windows_util.windowsWrite;
     pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;
     pub const windowsOpen = windows_util.windowsOpen;
    +pub const windowsLoadDll = windows_util.windowsLoadDll;
    +pub const windowsUnloadDll = windows_util.windowsUnloadDll; 
     pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
     
     pub const FileHandle = if (is_windows) windows.HANDLE else i32;
    @@ -620,7 +622,9 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:
     }
     
     // here we replace the standard +/ with -_ so that it can be used in a file name
    -const b64_fs_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=";
    +const b64_fs_encoder = base64.Base64Encoder.init(
    +    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
    +    base64.standard_pad_char);
     
     pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) -> %void {
         if (symLink(allocator, existing_path, new_path)) {
    @@ -632,12 +636,12 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:
         }
     
         var rand_buf: [12]u8 = undefined;
    -    const tmp_path = %return allocator.alloc(u8, new_path.len + base64.calcEncodedSize(rand_buf.len));
    +    const tmp_path = %return allocator.alloc(u8, new_path.len + base64.Base64Encoder.calcSize(rand_buf.len));
         defer allocator.free(tmp_path);
         mem.copy(u8, tmp_path[0..], new_path);
         while (true) {
             %return getRandomBytes(rand_buf[0..]);
    -        _ = base64.encodeWithAlphabet(tmp_path[new_path.len..], rand_buf, b64_fs_alphabet);
    +        b64_fs_encoder.encode(tmp_path[new_path.len..], rand_buf);
             if (symLink(allocator, existing_path, tmp_path)) {
                 return rename(allocator, tmp_path, new_path);
             } else |err| {
    @@ -715,11 +719,11 @@ pub fn copyFile(allocator: &Allocator, source_path: []const u8, dest_path: []con
     /// Guaranteed to be atomic.
     pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: []const u8, mode: usize) -> %void {
         var rand_buf: [12]u8 = undefined;
    -    const tmp_path = %return allocator.alloc(u8, dest_path.len + base64.calcEncodedSize(rand_buf.len));
    +    const tmp_path = %return allocator.alloc(u8, dest_path.len + base64.Base64Encoder.calcSize(rand_buf.len));
         defer allocator.free(tmp_path);
         mem.copy(u8, tmp_path[0..], dest_path);
         %return getRandomBytes(rand_buf[0..]);
    -    _ = base64.encodeWithAlphabet(tmp_path[dest_path.len..], rand_buf, b64_fs_alphabet);
    +    b64_fs_encoder.encode(tmp_path[dest_path.len..], rand_buf);
     
         var out_file = %return io.File.openWriteMode(tmp_path, mode, allocator);
         defer out_file.close();
    diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig
    index 913cc79801..0ce3794cdc 100644
    --- a/std/os/windows/index.zig
    +++ b/std/os/windows/index.zig
    @@ -84,6 +84,11 @@ pub extern "kernel32" stdcallcc fn WriteFile(in_hFile: HANDLE, in_lpBuffer: &con
         in_nNumberOfBytesToWrite: DWORD, out_lpNumberOfBytesWritten: ?&DWORD,
         in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL;
     
    +//TODO: call unicode versions instead of relying on ANSI code page
    +pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) -> ?HMODULE;
    +
    +pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) -> BOOL;   
    +
     pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) -> c_int;
     
     pub const PROV_RSA_FULL = 1;
    @@ -97,6 +102,7 @@ pub const FLOAT = f32;
     pub const HANDLE = &c_void;
     pub const HCRYPTPROV = ULONG_PTR;
     pub const HINSTANCE = &@OpaqueType();
    +pub const HMODULE = &@OpaqueType();
     pub const INT = c_int;
     pub const LPBYTE = &BYTE;
     pub const LPCH = &CHAR;
    diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig
    index de2babe8d7..b3fc095d43 100644
    --- a/std/os/windows/util.zig
    +++ b/std/os/windows/util.zig
    @@ -4,6 +4,7 @@ const windows = std.os.windows;
     const assert = std.debug.assert;
     const mem = std.mem;
     const BufMap = std.BufMap;
    +const cstr = std.cstr;
     
     error WaitAbandoned;
     error WaitTimeOut;
    @@ -149,3 +150,25 @@ pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap)
         result[i] = 0;
         return result;
     }
    +
    +error DllNotFound;
    +pub fn windowsLoadDll(allocator: &mem.Allocator, dll_path: []const u8) -> %windows.HMODULE {
    +    const padded_buff = %return cstr.addNullByte(allocator, dll_path);
    +    defer allocator.free(padded_buff);
    +    return windows.LoadLibraryA(padded_buff.ptr) ?? error.DllNotFound;
    +}
    +
    +pub fn windowsUnloadDll(hModule: windows.HMODULE) {
    +    assert(windows.FreeLibrary(hModule)!= 0);
    +}
    +
    +
    +test "InvalidDll" {
    +    const DllName = "asdf.dll";
    +    const allocator = std.debug.global_allocator;
    +    const handle = os.windowsLoadDll(allocator, DllName) %%  |err| {
    +        assert(err == error.DllNotFound);
    +        return;
    +    };
    +}
    +
    diff --git a/test/cases/union.zig b/test/cases/union.zig
    index 4b8ccb7245..4044721582 100644
    --- a/test/cases/union.zig
    +++ b/test/cases/union.zig
    @@ -31,3 +31,47 @@ test "unions embedded in aggregate types" {
             else => unreachable,
         }
     }
    +
    +
    +const Foo = union {
    +    float: f64,
    +    int: i32,
    +};
    +
    +test "basic unions" {
    +    var foo = Foo { .int = 1 };
    +    assert(foo.int == 1);
    +    foo = Foo {.float = 12.34};
    +    assert(foo.float == 12.34);
    +}
    +
    +test "init union with runtime value" {
    +    var foo: Foo = undefined;
    +
    +    setFloat(&foo, 12.34);
    +    assert(foo.float == 12.34);
    +
    +    setInt(&foo, 42);
    +    assert(foo.int == 42);
    +}
    +
    +fn setFloat(foo: &Foo, x: f64) {
    +    *foo = Foo { .float = x };
    +}
    +
    +fn setInt(foo: &Foo, x: i32) {
    +    *foo = Foo { .int = x };
    +}
    +
    +const FooExtern = extern union {
    +    float: f64,
    +    int: i32,
    +};
    +
    +test "basic extern unions" {
    +    var foo = FooExtern { .int = 1 };
    +    assert(foo.int == 1);
    +    foo.float = 12.34;
    +    assert(foo.float == 12.34);
    +}
    +
    diff --git a/test/compile_errors.zig b/test/compile_errors.zig
    index fa90661158..3ef4a63e5f 100644
    --- a/test/compile_errors.zig
    +++ b/test/compile_errors.zig
    @@ -389,8 +389,8 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
             \\    const y = a.bar;
             \\}
         ,
    -            ".tmp_source.zig:4:6: error: no member named 'foo' in 'A'",
    -            ".tmp_source.zig:5:16: error: no member named 'bar' in 'A'");
    +            ".tmp_source.zig:4:6: error: no member named 'foo' in struct 'A'",
    +            ".tmp_source.zig:5:16: error: no member named 'bar' in struct 'A'");
     
         cases.add("redefinition of struct",
             \\const A = struct { x : i32, };
    @@ -454,7 +454,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
             \\        .foo = 42,
             \\    };
             \\}
    -    , ".tmp_source.zig:10:9: error: no member named 'foo' in 'A'");
    +    , ".tmp_source.zig:10:9: error: no member named 'foo' in struct 'A'");
     
         cases.add("invalid break expression",
             \\export fn f() {
    @@ -2343,4 +2343,23 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
             \\pub extern fn foo(format: &const u8, ...);
         ,
             ".tmp_source.zig:2:9: error: expected type '&const u8', found '[5]u8'");
    +
    +    cases.add("constant inside comptime function has compile error",
    +        \\const ContextAllocator = MemoryPool(usize);
    +        \\
    +        \\pub fn MemoryPool(comptime T: type) -> type {
    +        \\    const free_list_t = @compileError("aoeu");
    +        \\
    +        \\    struct {
    +        \\        free_list: free_list_t,
    +        \\    }
    +        \\}
    +        \\
    +        \\export fn entry() {
    +        \\    var allocator: ContextAllocator = undefined;
    +        \\}
    +    ,
    +        ".tmp_source.zig:4:25: error: aoeu",
    +        ".tmp_source.zig:1:36: note: called from here",
    +        ".tmp_source.zig:12:20: note: referenced here");
     }
    diff --git a/test/debug_safety.zig b/test/debug_safety.zig
    index 9e9ff98349..36f8d020c3 100644
    --- a/test/debug_safety.zig
    +++ b/test/debug_safety.zig
    @@ -260,4 +260,24 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
             \\    return int_slice[0];
             \\}
         );
    +
    +    cases.addDebugSafety("bad union field access",
    +        \\pub fn panic(message: []const u8) -> noreturn {
    +        \\    @import("std").os.exit(126);
    +        \\}
    +        \\
    +        \\const Foo = union {
    +        \\    float: f32,
    +        \\    int: u32,
    +        \\};
    +        \\
    +        \\pub fn main() -> %void {
    +        \\    var f = Foo { .int = 42 };
    +        \\    bar(&f);
    +        \\}
    +        \\
    +        \\fn bar(f: &Foo) {
    +        \\    f.float = 12.34;
    +        \\}
    +    );
     }
    diff --git a/test/tests.zig b/test/tests.zig
    index 20b57c7573..73d9646552 100644
    --- a/test/tests.zig
    +++ b/test/tests.zig
    @@ -18,7 +18,7 @@ const build_examples = @import("build_examples.zig");
     const compile_errors = @import("compile_errors.zig");
     const assemble_and_link = @import("assemble_and_link.zig");
     const debug_safety = @import("debug_safety.zig");
    -const parsec = @import("parsec.zig");
    +const translate_c = @import("translate_c.zig");
     
     const TestTarget = struct {
         os: builtin.Os,
    @@ -123,16 +123,16 @@ pub fn addAssembleAndLinkTests(b: &build.Builder, test_filter: ?[]const u8) -> &
         return cases.step;
     }
     
    -pub fn addParseCTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
    -    const cases = %%b.allocator.create(ParseCContext);
    -    *cases = ParseCContext {
    +pub fn addTranslateCTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
    +    const cases = %%b.allocator.create(TranslateCContext);
    +    *cases = TranslateCContext {
             .b = b,
    -        .step = b.step("test-parsec", "Run the C header file parsing tests"),
    +        .step = b.step("test-translate-c", "Run the C header file parsing tests"),
             .test_index = 0,
             .test_filter = test_filter,
         };
     
    -    parsec.addCases(cases);
    +    translate_c.addCases(cases);
     
         return cases.step;
     }
    @@ -770,7 +770,7 @@ pub const BuildExamplesContext = struct {
         }
     };
     
    -pub const ParseCContext = struct {
    +pub const TranslateCContext = struct {
         b: &build.Builder,
         step: &build.Step,
         test_index: usize,
    @@ -799,17 +799,17 @@ pub const ParseCContext = struct {
             }
         };
     
    -    const ParseCCmpOutputStep = struct {
    +    const TranslateCCmpOutputStep = struct {
             step: build.Step,
    -        context: &ParseCContext,
    +        context: &TranslateCContext,
             name: []const u8,
             test_index: usize,
             case: &const TestCase,
     
    -        pub fn create(context: &ParseCContext, name: []const u8, case: &const TestCase) -> &ParseCCmpOutputStep {
    +        pub fn create(context: &TranslateCContext, name: []const u8, case: &const TestCase) -> &TranslateCCmpOutputStep {
                 const allocator = context.b.allocator;
    -            const ptr = %%allocator.create(ParseCCmpOutputStep);
    -            *ptr = ParseCCmpOutputStep {
    +            const ptr = %%allocator.create(TranslateCCmpOutputStep);
    +            *ptr = TranslateCCmpOutputStep {
                     .step = build.Step.init("ParseCCmpOutput", allocator, make),
                     .context = context,
                     .name = name,
    @@ -821,7 +821,7 @@ pub const ParseCContext = struct {
             }
     
             fn make(step: &build.Step) -> %void {
    -            const self = @fieldParentPtr(ParseCCmpOutputStep, "step", step);
    +            const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step);
                 const b = self.context.b;
     
                 const root_src = %%os.path.join(b.allocator, b.cache_root, self.case.sources.items[0].filename);
    @@ -829,7 +829,7 @@ pub const ParseCContext = struct {
                 var zig_args = ArrayList([]const u8).init(b.allocator);
                 %%zig_args.append(b.zig_exe);
     
    -            %%zig_args.append("parsec");
    +            %%zig_args.append("translate-c");
                 %%zig_args.append(b.pathFromRoot(root_src));
     
                 warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
    @@ -882,7 +882,7 @@ pub const ParseCContext = struct {
     
                 if (stderr.len != 0 and !self.case.allow_warnings) {
                     warn(
    -                    \\====== parsec emitted warnings: ============
    +                    \\====== translate-c emitted warnings: =======
                         \\{}
                         \\============================================
                         \\
    @@ -914,7 +914,7 @@ pub const ParseCContext = struct {
             warn("\n");
         }
     
    -    pub fn create(self: &ParseCContext, allow_warnings: bool, filename: []const u8, name: []const u8,
    +    pub fn create(self: &TranslateCContext, allow_warnings: bool, filename: []const u8, name: []const u8,
             source: []const u8, expected_lines: ...) -> &TestCase
         {
             const tc = %%self.b.allocator.create(TestCase);
    @@ -932,37 +932,37 @@ pub const ParseCContext = struct {
             return tc;
         }
     
    -    pub fn add(self: &ParseCContext, name: []const u8, source: []const u8, expected_lines: ...) {
    +    pub fn add(self: &TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) {
             const tc = self.create(false, "source.h", name, source, expected_lines);
             self.addCase(tc);
         }
     
    -    pub fn addC(self: &ParseCContext, name: []const u8, source: []const u8, expected_lines: ...) {
    +    pub fn addC(self: &TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) {
             const tc = self.create(false, "source.c", name, source, expected_lines);
             self.addCase(tc);
         }
     
    -    pub fn addAllowWarnings(self: &ParseCContext, name: []const u8, source: []const u8, expected_lines: ...) {
    +    pub fn addAllowWarnings(self: &TranslateCContext, name: []const u8, source: []const u8, expected_lines: ...) {
             const tc = self.create(true, "source.h", name, source, expected_lines);
             self.addCase(tc);
         }
     
    -    pub fn addCase(self: &ParseCContext, case: &const TestCase) {
    +    pub fn addCase(self: &TranslateCContext, case: &const TestCase) {
             const b = self.b;
     
    -        const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "parsec {}", case.name);
    +        const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "translate-c {}", case.name);
             if (self.test_filter) |filter| {
                 if (mem.indexOf(u8, annotated_case_name, filter) == null)
                     return;
             }
     
    -        const parsec_and_cmp = ParseCCmpOutputStep.create(self, annotated_case_name, case);
    -        self.step.dependOn(&parsec_and_cmp.step);
    +        const translate_c_and_cmp = TranslateCCmpOutputStep.create(self, annotated_case_name, case);
    +        self.step.dependOn(&translate_c_and_cmp.step);
     
             for (case.sources.toSliceConst()) |src_file| {
                 const expanded_src_path = %%os.path.join(b.allocator, b.cache_root, src_file.filename);
                 const write_src = b.addWriteFile(expanded_src_path, src_file.source);
    -            parsec_and_cmp.step.dependOn(&write_src.step);
    +            translate_c_and_cmp.step.dependOn(&write_src.step);
             }
         }
     };
    diff --git a/test/parsec.zig b/test/translate_c.zig
    similarity index 72%
    rename from test/parsec.zig
    rename to test/translate_c.zig
    index f9e90cb705..90b99b5faf 100644
    --- a/test/parsec.zig
    +++ b/test/translate_c.zig
    @@ -1,6 +1,6 @@
     const tests = @import("tests.zig");
     
    -pub fn addCases(cases: &tests.ParseCContext) {
    +pub fn addCases(cases: &tests.TranslateCContext) {
         cases.addAllowWarnings("simple data types",
             \\#include 
             \\int foo(char a, unsigned char b, signed char c);
    @@ -677,12 +677,12 @@ pub fn addCases(cases: &tests.ParseCContext) {
             \\    };
             \\    a >>= @import("std").math.Log2Int(c_int)({
             \\        const _ref = &a;
    -        \\        (*_ref) = c_int(c_int(*_ref) >> @import("std").math.Log2Int(c_int)(1));
    +        \\        (*_ref) = ((*_ref) >> @import("std").math.Log2Int(c_int)(1));
             \\        *_ref
             \\    });
             \\    a <<= @import("std").math.Log2Int(c_int)({
             \\        const _ref = &a;
    -        \\        (*_ref) = c_int(c_int(*_ref) << @import("std").math.Log2Int(c_int)(1));
    +        \\        (*_ref) = ((*_ref) << @import("std").math.Log2Int(c_int)(1));
             \\        *_ref
             \\    });
             \\}
    @@ -735,12 +735,12 @@ pub fn addCases(cases: &tests.ParseCContext) {
             \\    };
             \\    a >>= @import("std").math.Log2Int(c_uint)({
             \\        const _ref = &a;
    -        \\        (*_ref) = c_uint(c_uint(*_ref) >> @import("std").math.Log2Int(c_uint)(1));
    +        \\        (*_ref) = ((*_ref) >> @import("std").math.Log2Int(c_uint)(1));
             \\        *_ref
             \\    });
             \\    a <<= @import("std").math.Log2Int(c_uint)({
             \\        const _ref = &a;
    -        \\        (*_ref) = c_uint(c_uint(*_ref) << @import("std").math.Log2Int(c_uint)(1));
    +        \\        (*_ref) = ((*_ref) << @import("std").math.Log2Int(c_uint)(1));
             \\        *_ref
             \\    });
             \\}
    @@ -805,6 +805,50 @@ pub fn addCases(cases: &tests.ParseCContext) {
             \\}
         );
     
    +    cases.addC("pre increment/decrement",
    +        \\void foo(void) {
    +        \\    int i = 0;
    +        \\    unsigned u = 0;
    +        \\    ++i;
    +        \\    --i;
    +        \\    ++u;
    +        \\    --u;
    +        \\    i = ++i;
    +        \\    i = --i;
    +        \\    u = ++u;
    +        \\    u = --u;
    +        \\}
    +    ,
    +        \\export fn foo() {
    +        \\    var i: c_int = 0;
    +        \\    var u: c_uint = c_uint(0);
    +        \\    i += 1;
    +        \\    i -= 1;
    +        \\    u +%= 1;
    +        \\    u -%= 1;
    +        \\    i = {
    +        \\        const _ref = &i;
    +        \\        (*_ref) += 1;
    +        \\        *_ref
    +        \\    };
    +        \\    i = {
    +        \\        const _ref = &i;
    +        \\        (*_ref) -= 1;
    +        \\        *_ref
    +        \\    };
    +        \\    u = {
    +        \\        const _ref = &u;
    +        \\        (*_ref) +%= 1;
    +        \\        *_ref
    +        \\    };
    +        \\    u = {
    +        \\        const _ref = &u;
    +        \\        (*_ref) -%= 1;
    +        \\        *_ref
    +        \\    };
    +        \\}
    +    );
    +
         cases.addC("do loop",
             \\void foo(void) {
             \\    int a = 2;
    @@ -834,17 +878,21 @@ pub fn addCases(cases: &tests.ParseCContext) {
     
         cases.addC("deref function pointer",
             \\void foo(void) {}
    +        \\void baz(void) {}
             \\void bar(void) {
             \\    void(*f)(void) = foo;
             \\    f();
             \\    (*(f))();
    +        \\    baz();
             \\}
         ,
             \\export fn foo() {}
    +        \\export fn baz() {}
             \\export fn bar() {
             \\    var f: ?extern fn() = foo;
             \\    (??f)();
             \\    (??f)();
    +        \\    baz();
             \\}
         );
     
    @@ -857,15 +905,277 @@ pub fn addCases(cases: &tests.ParseCContext) {
             \\    (*(??x)) = 1;
             \\}
         );
    +
    +    cases.add("simple union",
    +        \\union Foo {
    +        \\    int x;
    +        \\    double y;
    +        \\};
    +    ,
    +        \\pub const union_Foo = extern union {
    +        \\    x: c_int,
    +        \\    y: f64,
    +        \\};
    +    ,
    +        \\pub const Foo = union_Foo;
    +    );
    +
    +    cases.add("address of operator",
    +        \\int foo(void) {
    +        \\    int x = 1234;
    +        \\    int *ptr = &x;
    +        \\    return *ptr;
    +        \\}
    +    ,
    +        \\pub fn foo() -> c_int {
    +        \\    var x: c_int = 1234;
    +        \\    var ptr: ?&c_int = &x;
    +        \\    return *(??ptr);
    +        \\}
    +    );
    +
    +    cases.add("string literal",
    +        \\const char *foo(void) {
    +        \\    return "bar";
    +        \\}
    +    ,
    +        \\pub fn foo() -> ?&const u8 {
    +        \\    return c"bar";
    +        \\}
    +    );
    +
    +    cases.add("return void",
    +        \\void foo(void) {
    +        \\    return;
    +        \\}
    +    ,
    +        \\pub fn foo() {
    +        \\    return;
    +        \\}
    +    );
    +
    +    cases.add("for loop",
    +        \\void foo(void) {
    +        \\    for (int i = 0; i < 10; i += 1) { }
    +        \\}
    +    ,
    +        \\pub fn foo() {
    +        \\    {
    +        \\        var i: c_int = 0;
    +        \\        while (i < 10) : (i += 1) {};
    +        \\    };
    +        \\}
    +    );
    +
    +    cases.add("empty for loop",
    +        \\void foo(void) {
    +        \\    for (;;) { }
    +        \\}
    +    ,
    +        \\pub fn foo() {
    +        \\    while (true) {};
    +        \\}
    +    );
    +
    +    cases.add("break statement",
    +        \\void foo(void) {
    +        \\    for (;;) {
    +        \\        break;
    +        \\    }
    +        \\}
    +    ,
    +        \\pub fn foo() {
    +        \\    while (true) {
    +        \\        break;
    +        \\    };
    +        \\}
    +    );
    +
    +    cases.add("continue statement",
    +        \\void foo(void) {
    +        \\    for (;;) {
    +        \\        continue;
    +        \\    }
    +        \\}
    +    ,
    +        \\pub fn foo() {
    +        \\    while (true) {
    +        \\        continue;
    +        \\    };
    +        \\}
    +    );
    +
    +    cases.add("switch statement",
    +        \\int foo(int x) {
    +        \\    switch (x) {
    +        \\        case 1:
    +        \\            x += 1;
    +        \\        case 2:
    +        \\            break;
    +        \\        case 3:
    +        \\        case 4:
    +        \\            return x + 1;
    +        \\        default:
    +        \\            return 10;
    +        \\    }
    +        \\    return x + 13;
    +        \\}
    +    ,
    +        \\fn foo(_arg_x: c_int) -> c_int {
    +        \\    var x = _arg_x;
    +        \\    {
    +        \\        switch (x) {
    +        \\            1 => goto case_0,
    +        \\            2 => goto case_1,
    +        \\            3 => goto case_2,
    +        \\            4 => goto case_3,
    +        \\            else => goto default,
    +        \\        };
    +        \\    case_0:
    +        \\        x += 1;
    +        \\    case_1:
    +        \\        goto end;
    +        \\    case_2:
    +        \\    case_3:
    +        \\        return x + 1;
    +        \\    default:
    +        \\        return 10;
    +        \\        goto end;
    +        \\    end:
    +        \\    };
    +        \\    return x + 13;
    +        \\}
    +    );
    +    
    +    cases.add("macros with field targets",
    +        \\typedef unsigned int GLbitfield;
    +        \\typedef void (*PFNGLCLEARPROC) (GLbitfield mask);
    +        \\typedef void(*OpenGLProc)(void);
    +        \\union OpenGLProcs {
    +        \\    OpenGLProc ptr[1];
    +        \\    struct {
    +        \\        PFNGLCLEARPROC Clear;
    +        \\    } gl;
    +        \\};
    +        \\extern union OpenGLProcs glProcs;
    +        \\#define glClearUnion glProcs.gl.Clear
    +        \\#define glClearPFN PFNGLCLEARPROC
    +    ,
    +        \\pub const GLbitfield = c_uint;
    +    ,
    +        \\pub const PFNGLCLEARPROC = ?extern fn(GLbitfield);
    +    ,
    +        \\pub const OpenGLProc = ?extern fn();
    +    ,
    +        \\pub const union_OpenGLProcs = extern union {
    +        \\    ptr: [1]OpenGLProc,
    +        \\    gl: extern struct {
    +        \\        Clear: PFNGLCLEARPROC,
    +        \\    },
    +        \\};
    +    ,
    +        \\pub extern var glProcs: union_OpenGLProcs;
    +    ,
    +        \\pub const glClearPFN = PFNGLCLEARPROC;
    +    ,
    +        \\pub inline fn glClearUnion(arg0: GLbitfield) {
    +        \\    (??glProcs.gl.Clear)(arg0)
    +        \\}
    +    ,
    +        \\pub const OpenGLProcs = union_OpenGLProcs;
    +    );
    +
    +    cases.add("switch statement with no default",
    +        \\int foo(int x) {
    +        \\    switch (x) {
    +        \\        case 1:
    +        \\            x += 1;
    +        \\        case 2:
    +        \\            break;
    +        \\        case 3:
    +        \\        case 4:
    +        \\            return x + 1;
    +        \\    }
    +        \\    return x + 13;
    +        \\}
    +    ,
    +        \\fn foo(_arg_x: c_int) -> c_int {
    +        \\    var x = _arg_x;
    +        \\    {
    +        \\        switch (x) {
    +        \\            1 => goto case_0,
    +        \\            2 => goto case_1,
    +        \\            3 => goto case_2,
    +        \\            4 => goto case_3,
    +        \\            else => goto end,
    +        \\        };
    +        \\    case_0:
    +        \\        x += 1;
    +        \\    case_1:
    +        \\        goto end;
    +        \\    case_2:
    +        \\    case_3:
    +        \\        return x + 1;
    +        \\        goto end;
    +        \\    end:
    +        \\    };
    +        \\    return x + 13;
    +        \\}
    +    );
    +
    +    cases.add("variable name shadowing",
    +        \\int foo(void) {
    +        \\    int x = 1;
    +        \\    {
    +        \\        int x = 2;
    +        \\        x += 1;
    +        \\    }
    +        \\    return x;
    +        \\}
    +    ,
    +        \\pub fn foo() -> c_int {
    +        \\    var x: c_int = 1;
    +        \\    {
    +        \\        var x_0: c_int = 2;
    +        \\        x_0 += 1;
    +        \\    };
    +        \\    return x;
    +        \\}
    +    );
    +
    +    cases.add("pointer casting",
    +        \\float *ptrcast(int *a) {
    +        \\    return (float *)a;
    +        \\}
    +    ,
    +        \\fn ptrcast(a: ?&c_int) -> ?&f32 {
    +        \\    return @ptrCast(?&f32, a);
    +        \\}
    +    );
    +
    +    cases.add("bin not",
    +        \\int foo(int x) {
    +        \\    return ~x;
    +        \\}
    +    ,
    +        \\pub fn foo(x: c_int) -> c_int {
    +        \\    return ~x;
    +        \\}
    +    );
    +
    +    cases.add("primitive types included in defined symbols",
    +        \\int foo(int u32) {
    +        \\    return u32;
    +        \\}
    +    ,
    +        \\pub fn foo(u32_0: c_int) -> c_int {
    +        \\    return u32_0;
    +        \\}
    +    );
    +
    +    cases.add("const ptr initializer",
    +        \\static const char *v0 = "0.0.0";
    +    ,
    +        \\pub var v0: ?&const u8 = c"0.0.0";
    +    );
     }
    -
    -
    -
    -// TODO
    -//float *ptrcast(int *a) {
    -//    return (float *)a;
    -//}
    -// should translate to
    -// fn ptrcast(a: ?&c_int) -> ?&f32 {
    -//     return @ptrCast(?&f32, a);
    -// }