fix error when switch prong has implicit cast

closes #194
This commit is contained in:
Andrew Kelley 2016-09-20 16:10:34 -04:00
parent 158225a203
commit b97bfc3ecb
6 changed files with 243 additions and 14 deletions

View File

@ -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) {

View File

@ -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) {

View File

@ -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;

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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