authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-11-16 21:15:15-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-11-16 21:15:15-05:00
loge26ccd5166000f81a589c446d04102c21045bff6
tree5751f2f782ad35b30622aa8875ce0c8f6a37a9d3
parentf12d36641f67564d2103f75ed7a5445219197db5

debug safety for unions


4 files changed, 48 insertions(+), 12 deletions(-)

src/all_types.hpp+1
...@@ -1317,6 +1317,7 @@ enum PanicMsgId {...@@ -1317,6 +1317,7 @@ enum PanicMsgId {
1317 PanicMsgIdUnwrapMaybeFail,1317 PanicMsgIdUnwrapMaybeFail,
1318 PanicMsgIdInvalidErrorCode,1318 PanicMsgIdInvalidErrorCode,
1319 PanicMsgIdIncorrectAlignment,1319 PanicMsgIdIncorrectAlignment,
1320 PanicMsgIdBadUnionField,
13201321
1321 PanicMsgIdCount,1322 PanicMsgIdCount,
1322};1323};
src/codegen.cpp+26-11
...@@ -810,6 +810,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {...@@ -810,6 +810,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
810 return buf_create_from_str("invalid error code");810 return buf_create_from_str("invalid error code");
811 case PanicMsgIdIncorrectAlignment:811 case PanicMsgIdIncorrectAlignment:
812 return buf_create_from_str("incorrect alignment");812 return buf_create_from_str("incorrect alignment");
813 case PanicMsgIdBadUnionField:
814 return buf_create_from_str("access of inactive union field");
813 }815 }
814 zig_unreachable();816 zig_unreachable();
815}817}
...@@ -2415,6 +2417,23 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executab...@@ -2415,6 +2417,23 @@ static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutable *executab
2415 return bitcasted_union_field_ptr;2417 return bitcasted_union_field_ptr;
2416 }2418 }
24172419
2420 if (ir_want_debug_safety(g, &instruction->base)) {
2421 LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_tag_index, "");
2422 LLVMValueRef tag_value = gen_load_untyped(g, tag_field_ptr, 0, false, "");
2423 LLVMValueRef expected_tag_value = LLVMConstInt(union_type->data.unionation.tag_type->type_ref,
2424 field->value, false);
2425
2426 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckOk");
2427 LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckFail");
2428 LLVMValueRef ok_val = LLVMBuildICmp(g->builder, LLVMIntEQ, tag_value, expected_tag_value, "");
2429 LLVMBuildCondBr(g->builder, ok_val, ok_block, bad_block);
2430
2431 LLVMPositionBuilderAtEnd(g->builder, bad_block);
2432 gen_debug_safety_crash(g, PanicMsgIdBadUnionField);
2433
2434 LLVMPositionBuilderAtEnd(g->builder, ok_block);
2435 }
2436
2418 LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_union_index, "");2437 LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_union_index, "");
2419 LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, "");2438 LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, "");
2420 return bitcasted_union_field_ptr;2439 return bitcasted_union_field_ptr;
...@@ -3977,21 +3996,17 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {...@@ -3977,21 +3996,17 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
39773996
3978 LLVMValueRef union_value_ref;3997 LLVMValueRef union_value_ref;
3979 {3998 {
3980 unsigned field_count;
3981 LLVMValueRef fields[2];
3982 fields[0] = correctly_typed_value;
3983 if (pad_bytes == 0) {3999 if (pad_bytes == 0) {
3984 field_count = 1;4000 union_value_ref = correctly_typed_value;
3985 } else {4001 } else {
4002 LLVMValueRef fields[2];
3986 fields[0] = correctly_typed_value;4003 fields[0] = correctly_typed_value;
3987 fields[1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), (unsigned)pad_bytes));4004 fields[1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), (unsigned)pad_bytes));
3988 field_count = 2;4005 if (make_unnamed_struct || type_entry->data.unionation.gen_tag_index != SIZE_MAX) {
3989 }4006 union_value_ref = LLVMConstStruct(fields, 2, false);
39904007 } else {
3991 if (make_unnamed_struct || type_entry->data.unionation.gen_tag_index != SIZE_MAX) {4008 union_value_ref = LLVMConstNamedStruct(union_type_ref, fields, 2);
3992 union_value_ref = LLVMConstStruct(fields, field_count, false);4009 }
3993 } else {
3994 union_value_ref = LLVMConstNamedStruct(union_type_ref, fields, field_count);
3995 }4010 }
3996 }4011 }
39974012
test/cases/union.zig+1-1
...@@ -41,7 +41,7 @@ const Foo = union {...@@ -41,7 +41,7 @@ const Foo = union {
41test "basic unions" {41test "basic unions" {
42 var foo = Foo { .int = 1 };42 var foo = Foo { .int = 1 };
43 assert(foo.int == 1);43 assert(foo.int == 1);
44 foo.float = 12.34;44 foo = Foo {.float = 12.34};
45 assert(foo.float == 12.34);45 assert(foo.float == 12.34);
46}46}
4747
test/debug_safety.zig+20
...@@ -260,4 +260,24 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -260,4 +260,24 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
260 \\ return int_slice[0];260 \\ return int_slice[0];
261 \\}261 \\}
262 );262 );
263
264 cases.addDebugSafety("bad union field access",
265 \\pub fn panic(message: []const u8) -> noreturn {
266 \\ @import("std").os.exit(126);
267 \\}
268 \\
269 \\const Foo = union {
270 \\ float: f32,
271 \\ int: u32,
272 \\};
273 \\
274 \\pub fn main() -> %void {
275 \\ var f = Foo { .int = 42 };
276 \\ bar(&f);
277 \\}
278 \\
279 \\fn bar(f: &Foo) {
280 \\ f.float = 12.34;
281 \\}
282 );
263}283}