Commit b97bfc3ecb

Andrew Kelley <superjoe30@gmail.com>
2016-09-20 22:10:34
fix error when switch prong has implicit cast
closes #194
1 parent 158225a
src/analyze.cpp
@@ -2540,8 +2540,8 @@ static const char *err_container_init_syntax_name(ContainerInitKind kind) {
     zig_unreachable();
 }
 
-static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-        AstNode *node)
+static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry *import,
+    BlockContext *context, AstNode *node)
 {
     assert(node->type == NodeTypeContainerInitExpr);
 
@@ -6279,13 +6279,15 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import,
         BlockContext *child_context = prong_node->data.switch_prong.block_context;
         child_context->codegen_excluded = expr_val->ok && (*const_chosen_prong_index != prong_i);
 
-        peer_nodes[prong_i] = prong_node->data.switch_prong.expr;
         if (child_context->codegen_excluded) {
             peer_types[prong_i] = g->builtin_types.entry_unreachable;
         } else {
             peer_types[prong_i] = analyze_expression(g, import, child_context, expected_type,
                     prong_node->data.switch_prong.expr);
         }
+        // This must go after the analyze_expression for
+        // prong_node->data.switch_prong.expr because of AST rewriting.
+        peer_nodes[prong_i] = prong_node->data.switch_prong.expr;
     }
 
     if (expr_type->id == TypeTableEntryIdEnum && !else_prong) {
std/debug.zig
@@ -3,6 +3,7 @@ const io = @import("io.zig");
 const os = @import("os.zig");
 const elf = @import("elf.zig");
 const DW = @import("dwarf.zig");
+const List = @import("list.zig").List;
 
 pub error MissingDebugInfo;
 pub error InvalidDebugInfo;
@@ -29,6 +30,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream) -> %void {
 
             st.aranges = %return st.elf.findSection(".debug_aranges");
             st.debug_info = (%return st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo;
+            st.debug_abbrev = (%return st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo;
 
             var maybe_fp: ?&const u8 = @frameAddress();
             while (true) {
@@ -62,6 +64,140 @@ struct ElfStackTrace {
     elf: elf.Elf,
     aranges: ?&elf.SectionHeader,
     debug_info: &elf.SectionHeader,
+    debug_abbrev: &elf.SectionHeader,
+}
+
+enum FormValue {
+    Address: u64,
+    Block: []u8,
+    Const: Constant,
+    ExprLoc: []u8,
+    Flag: bool,
+    SecOffset: u64,
+    Ref: []u8,
+    RefAddr: u64,
+    RefSig8: u64,
+    String: []u8,
+    StrPtr: u64,
+}
+
+struct Constant {
+    payload: []u8,
+    signed: bool,
+}
+
+
+fn readAllocBytes(in_stream: &io.InStream, size: usize) -> %[]u8 {
+    const buf = %return global_allocator.alloc(u8, size);
+    %defer global_allocator.free(u8, buf);
+    %return in_stream.read(buf);
+    return buf;
+}
+
+fn parseFormValueBlockLen(in_stream: &io.InStream, size: usize) -> %FormValue {
+    const buf = %return readAllocBytes(in_stream, size);
+    return FormValue.Block { buf };
+}
+
+fn parseFormValueBlock(in_stream: &io.InStream, inline T: type) -> %FormValue {
+    const block_len = %return in_stream.readIntLe(T);
+    return parseFormValueBlockLen(in_stream, block_len);
+}
+
+fn parseFormValueConstantLen(in_stream: &io.InStream, signed: bool, size: usize) -> %FormValue {
+    const buf = %return readAllocBytes(in_stream, size);
+    return FormValue.Const { Constant {
+        .signed = signed,
+        .payload = buf,
+    }};
+}
+
+fn parseFormValueConstant(in_stream: &io.InStream, signed: bool, inline T: type) -> %FormValue {
+    const block_len = %return in_stream.readIntLe(T);
+    return parseFormValueConstantLen(in_stream, signed, block_len);
+}
+
+fn parseFormValueAddrSize(in_stream: &io.InStream, is_64: bool) -> %u64 {
+    return if (is_64) {
+        %return in_stream.readIntLe(u64)
+    } else {
+        u64(%return in_stream.readIntLe(u32))
+    };
+}
+
+fn parseFormValueRefLen(in_stream: &io.InStream, size: usize) -> %FormValue {
+    const buf = %return readAllocBytes(in_stream, size);
+    return FormValue.Ref { buf };
+}
+
+fn parseFormValueRef(in_stream: &io.InStream, inline T: type) -> %FormValue {
+    const block_len = %return in_stream.readIntLe(T);
+    return parseFormValueRefLen(in_stream, block_len);
+}
+
+fn parseFormValue(in_stream: &io.InStream, form_id: u64, is_64: bool) -> %FormValue {
+    return switch (form_id) {
+        DW.FORM_addr => FormValue.Address {
+            %return parseFormValueAddrSize(in_stream, is_64)
+        },
+        DW.FORM_block1 => parseFormValueBlock(in_stream, u8),
+        DW.FORM_block2 => parseFormValueBlock(in_stream, u16),
+        DW.FORM_block4 => parseFormValueBlock(in_stream, u32),
+        DW.FORM_block => {
+            const block_len = %return readULeb128(in_stream);
+            parseFormValueBlockLen(in_stream, block_len)
+        },
+        DW.FORM_data1 => parseFormValueConstant(in_stream, false, u8),
+        DW.FORM_data2 => parseFormValueConstant(in_stream, false, u16),
+        DW.FORM_data4 => parseFormValueConstant(in_stream, false, u32),
+        DW.FORM_data8 => parseFormValueConstant(in_stream, false, u64),
+        DW.FORM_udata, DW.FORM_sdata => {
+            const block_len = %return readULeb128(in_stream);
+            const signed = form_id == DW.FORM_sdata;
+            parseFormValueConstantLen(in_stream, signed, block_len)
+        },
+        DW.FORM_exprloc => {
+            const size = %return readULeb128(in_stream);
+            const buf = %return readAllocBytes(in_stream, size);
+            return FormValue.ExprLoc { buf };
+        },
+        DW.FORM_flag => FormValue.Flag { (%return in_stream.readByte()) != 0 },
+        DW.FORM_flag_present => FormValue.Flag { true },
+        DW.FORM_sec_offset => FormValue.SecOffset {
+            %return parseFormValueAddrSize(in_stream, is_64)
+        },
+
+        DW.FORM_ref1 => parseFormValueRef(in_stream, u8),
+        DW.FORM_ref2 => parseFormValueRef(in_stream, u16),
+        DW.FORM_ref4 => parseFormValueRef(in_stream, u32),
+        DW.FORM_ref8 => parseFormValueRef(in_stream, u64),
+        DW.FORM_ref_udata => {
+            const ref_len = %return readULeb128(in_stream);
+            parseFormValueRefLen(in_stream, ref_len)
+        },
+
+        DW.FORM_ref_addr => FormValue.RefAddr { %return parseFormValueAddrSize(in_stream, is_64) },
+        DW.FORM_ref_sig8 => FormValue.RefSig8 { %return in_stream.readIntLe(u64) },
+
+        DW.FORM_string => {
+            var buf: List(u8) = undefined; 
+            buf.init(&global_allocator);
+            while (true) {
+                const byte = %return in_stream.readByte();
+                if (byte == 0)
+                    break;
+                %return buf.append(byte);
+            }
+
+            FormValue.String { buf.items }
+        },
+        DW.FORM_strp => FormValue.StrPtr { %return parseFormValueAddrSize(in_stream, is_64) },
+        DW.FORM_indirect => {
+            const child_form_id = %return readULeb128(in_stream);
+            parseFormValue(in_stream, child_form_id, is_64)
+        },
+        else => return error.InvalidDebugInfo,
+    }
 }
 
 fn findCompileUnitOffset(st: &ElfStackTrace, target_address: usize) -> %u64 {
@@ -72,8 +208,78 @@ fn findCompileUnitOffset(st: &ElfStackTrace, target_address: usize) -> %u64 {
     %return st.elf.seekToSection(st.debug_info);
 
     while (true) {
-        const tag_id = %return st.self_exe_stream.readByte();
-        // TODO iterate until we find the relevant compile unit
+        var is_64: bool = undefined;
+        const unit_length = %return readInitialLength(&st.self_exe_stream, &is_64);
+
+        const version = %return st.self_exe_stream.readInt(st.elf.is_big_endian, u16);
+        if (version != 4) return error.InvalidDebugInfo;
+
+        const debug_abbrev_offset = if (is_64) {
+            %return st.self_exe_stream.readInt(st.elf.is_big_endian, u64)
+        } else {
+            %return st.self_exe_stream.readInt(st.elf.is_big_endian, u32)
+        };
+
+        const address_size = %return st.self_exe_stream.readByte();
+        if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
+
+        const abbrev_tag_id = %return st.self_exe_stream.readByte();
+
+
+    }
+}
+
+fn readInitialLength(in_stream: &io.InStream, is_64: &bool) -> %u64 {
+    const first_32_bits = %return in_stream.readIntLe(u32);
+    *is_64 = (first_32_bits == 0xffffffff);
+    return if (*is_64) {
+        %return in_stream.readIntLe(u64)
+    } else {
+        if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo;
+        u64(first_32_bits)
+    };
+}
+
+fn readULeb128(in_stream: &io.InStream) -> %u64 {
+    var result: u64 = 0;
+    var shift: u64 = 0;
+
+    while (true) {
+        const byte = %return in_stream.readByte();
+        var operand: u64 = undefined;
+
+        if (@shlWithOverflow(u64, byte & 0b01111111, shift, &operand))
+            return error.InvalidDebugInfo;
+
+        result |= operand;
+
+        if ((byte & 0b10000000) == 0)
+            return result;
+
+        shift += 7;
+    }
+}
+
+fn readILeb128(in_stream: &io.InStream) -> %i64 {
+    var result: i64 = 0;
+    var shift: i64 = 0;
+
+    while (true) {
+        const byte = %return in_stream.readByte();
+        var operand: i64 = undefined;
+
+        if (@shlWithOverflow(i64, byte & 0b01111111, shift, &operand))
+            return error.InvalidDebugInfo;
+
+        result |= operand;
+        shift += 7;
+
+        if ((byte & 0b10000000) == 0) {
+            if (shift < @sizeOf(i64) * 8 && (byte & 0b01000000) != 0)
+                result |= -(i64(1) << shift);
+
+            return result;
+        }
     }
 }
 
@@ -84,13 +290,8 @@ fn arangesOffset(st: &ElfStackTrace, target_address: usize) -> %?u64 {
     %return st.elf.seekToSection(aranges);
 
     const first_32_bits = %return st.self_exe_stream.readIntLe(u32);
-    const is_64 = (first_32_bits == 0xffffffff);
-    const unit_length = if (is_64) {
-        %return st.self_exe_stream.readIntLe(u64)
-    } else {
-        if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo;
-        first_32_bits
-    };
+    var is_64: bool = undefined;
+    const unit_length = %return readInitialLength(&st.self_exe_stream, &is_64);
     var unit_index: u64 = 0;
 
     while (unit_index < unit_length) {
std/dwarf.zig
@@ -127,8 +127,6 @@ pub const FORM_ref4 = 0x13;
 pub const FORM_ref8 = 0x14;
 pub const FORM_ref_udata = 0x15;
 pub const FORM_indirect = 0x16;
-
-// DWARF 4.
 pub const FORM_sec_offset = 0x17;
 pub const FORM_exprloc = 0x18;
 pub const FORM_flag_present = 0x19;
std/mem.zig
@@ -35,6 +35,7 @@ pub struct Allocator {
         ([]T)(%return self.reallocFn(self, ([]u8)(old_mem), byte_count))
     }
 
+    // TODO mem: []var and get rid of 2nd param
     fn free(self: &Allocator, inline T: type, mem: []T) {
         self.freeFn(self, ([]u8)(mem));
     }
test/cases/switch_prong_implicit_cast.zig
@@ -0,0 +1,26 @@
+const assert = @import("std").debug.assert;
+
+enum FormValue {
+    One,
+    Two: bool,
+}
+
+error Whatever;
+
+#static_eval_enable(false)
+fn foo(id: u64) -> %FormValue {
+    switch (id) {
+        2 => FormValue.Two { true },
+        1 => FormValue.One,
+        else => return error.Whatever,
+    }
+}
+
+#attribute("test")
+fn switchProngImplicitCast() {
+    const result = switch (%%foo(2)) {
+        One => false,
+        Two => |x| x,
+    };
+    assert(result);
+}
test/self_hosted.zig
@@ -11,6 +11,7 @@ const test_maybe_return = @import("cases/maybe_return.zig");
 const test_max_value_type = @import("cases/max_value_type.zig");
 const test_var_params = @import("cases/var_params.zig");
 const test_const_slice_child = @import("cases/const_slice_child.zig");
+const test_switch_prong_implicit_cast = @import("cases/switch_prong_implicit_cast.zig");
 
 // normal comment
 /// this is a documentation comment