Commit b97bfc3ecb
Changed files (6)
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