authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-04-26 11:35:56-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-04-26 11:36:11-07:00
logd1fa5692c685b804181d4658afce1e53ca74ec19
tree18d0c57776cdc470fa918e3e5fa18fdca80aa09d
parent61e6c49bc537a1c8a8da1d8a0e777261eef74cef

add array bounds checking in debug mode

closes #27

5 files changed, 216 insertions(+), 55 deletions(-)

src/all_types.hpp+3
...@@ -1051,6 +1051,7 @@ struct FnTableEntry {...@@ -1051,6 +1051,7 @@ struct FnTableEntry {
1051 bool is_extern;1051 bool is_extern;
1052 bool is_test;1052 bool is_test;
1053 bool is_pure;1053 bool is_pure;
1054 bool safety_off;
1054 BlockContext *parent_block_context;1055 BlockContext *parent_block_context;
1055 FnAnalState anal_state;1056 FnAnalState anal_state;
10561057
...@@ -1315,6 +1316,8 @@ struct BlockContext {...@@ -1315,6 +1316,8 @@ struct BlockContext {
13151316
1316 // if this is true, then this code will not be generated1317 // if this is true, then this code will not be generated
1317 bool codegen_excluded;1318 bool codegen_excluded;
1319
1320 bool safety_off;
1318};1321};
13191322
13201323
src/analyze.cpp+14
...@@ -993,6 +993,18 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t...@@ -993,6 +993,18 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
993 add_node_error(g, directive_node,993 add_node_error(g, directive_node,
994 buf_sprintf("invalid function attribute: '%s'", buf_ptr(name)));994 buf_sprintf("invalid function attribute: '%s'", buf_ptr(name)));
995 }995 }
996 } else if (buf_eql_str(name, "debug_safety")) {
997 if (fn_table_entry->is_extern) {
998 add_node_error(g, directive_node,
999 buf_sprintf("#debug_safety invalid on extern functions"));
1000 } else {
1001 bool enable;
1002 bool ok = resolve_const_expr_bool(g, import, import->block_context,
1003 &directive_node->data.directive.expr, &enable);
1004 if (ok && !enable) {
1005 fn_table_entry->safety_off = true;
1006 }
1007 }
996 } else if (buf_eql_str(name, "condition")) {1008 } else if (buf_eql_str(name, "condition")) {
997 if (fn_proto->top_level_decl.visib_mod == VisibModExport) {1009 if (fn_proto->top_level_decl.visib_mod == VisibModExport) {
998 bool include;1010 bool include;
...@@ -2102,11 +2114,13 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) {...@@ -2102,11 +2114,13 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) {
2102 context->parent_loop_node = parent->parent_loop_node;2114 context->parent_loop_node = parent->parent_loop_node;
2103 context->c_import_buf = parent->c_import_buf;2115 context->c_import_buf = parent->c_import_buf;
2104 context->codegen_excluded = parent->codegen_excluded;2116 context->codegen_excluded = parent->codegen_excluded;
2117 context->safety_off = parent->safety_off;
2105 }2118 }
21062119
2107 if (node && node->type == NodeTypeFnDef) {2120 if (node && node->type == NodeTypeFnDef) {
2108 AstNode *fn_proto_node = node->data.fn_def.fn_proto;2121 AstNode *fn_proto_node = node->data.fn_def.fn_proto;
2109 context->fn_entry = fn_proto_node->data.fn_proto.fn_table_entry;2122 context->fn_entry = fn_proto_node->data.fn_proto.fn_table_entry;
2123 context->safety_off = context->fn_entry->safety_off;
2110 } else if (parent) {2124 } else if (parent) {
2111 context->fn_entry = parent->fn_entry;2125 context->fn_entry = parent->fn_entry;
2112 }2126 }
src/codegen.cpp+98-32
...@@ -330,6 +330,46 @@ static LLVMValueRef get_handle_value(CodeGen *g, AstNode *source_node, LLVMValue...@@ -330,6 +330,46 @@ static LLVMValueRef get_handle_value(CodeGen *g, AstNode *source_node, LLVMValue
330 }330 }
331}331}
332332
333static bool want_debug_safety(CodeGen *g, AstNode *node) {
334 return !g->is_release_build && !node->block_context->safety_off;
335}
336
337static void add_bounds_check(CodeGen *g, AstNode *source_node, LLVMValueRef target_val,
338 LLVMIntPredicate lower_pred, LLVMValueRef lower_value,
339 LLVMIntPredicate upper_pred, LLVMValueRef upper_value)
340{
341 if (!lower_value && !upper_value) {
342 return;
343 }
344 if (upper_value && !lower_value) {
345 lower_value = upper_value;
346 lower_pred = upper_pred;
347 upper_value = nullptr;
348 }
349
350 add_debug_source_node(g, source_node);
351
352 LLVMBasicBlockRef bounds_check_fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoundsCheckFail");
353 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoundsCheckOk");
354 LLVMBasicBlockRef lower_ok_block = upper_value ?
355 LLVMAppendBasicBlock(g->cur_fn->fn_value, "FirstBoundsCheckOk") : ok_block;
356
357 LLVMValueRef lower_ok_val = LLVMBuildICmp(g->builder, lower_pred, target_val, lower_value, "");
358 LLVMBuildCondBr(g->builder, lower_ok_val, lower_ok_block, bounds_check_fail_block);
359
360 LLVMPositionBuilderAtEnd(g->builder, bounds_check_fail_block);
361 LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");
362 LLVMBuildUnreachable(g->builder);
363
364 if (upper_value) {
365 LLVMPositionBuilderAtEnd(g->builder, lower_ok_block);
366 LLVMValueRef upper_ok_val = LLVMBuildICmp(g->builder, upper_pred, target_val, upper_value, "");
367 LLVMBuildCondBr(g->builder, upper_ok_val, ok_block, bounds_check_fail_block);
368 }
369
370 LLVMPositionBuilderAtEnd(g->builder, ok_block);
371}
372
333static LLVMValueRef gen_err_name(CodeGen *g, AstNode *node) {373static LLVMValueRef gen_err_name(CodeGen *g, AstNode *node) {
334 assert(node->type == NodeTypeFnCallExpr);374 assert(node->type == NodeTypeFnCallExpr);
335 assert(g->generate_error_name_table);375 assert(g->generate_error_name_table);
...@@ -344,25 +384,10 @@ static LLVMValueRef gen_err_name(CodeGen *g, AstNode *node) {...@@ -344,25 +384,10 @@ static LLVMValueRef gen_err_name(CodeGen *g, AstNode *node) {
344 LLVMValueRef err_val = gen_expr(g, err_val_node);384 LLVMValueRef err_val = gen_expr(g, err_val_node);
345 add_debug_source_node(g, node);385 add_debug_source_node(g, node);
346386
347 if (!g->is_release_build) {387 if (want_debug_safety(g, node)) {
348 LLVMBasicBlockRef bounds_check_fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoundsCheckFail");
349 LLVMBasicBlockRef lower_ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "LowerBoundsCheckOk");
350 LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "BoundsCheckOk");
351
352 LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(err_val));388 LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(err_val));
353 LLVMValueRef is_zero_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, "");
354 LLVMBuildCondBr(g->builder, is_zero_val, bounds_check_fail_block, lower_ok_block);
355
356 LLVMPositionBuilderAtEnd(g->builder, bounds_check_fail_block);
357 LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");
358 LLVMBuildUnreachable(g->builder);
359
360 LLVMPositionBuilderAtEnd(g->builder, lower_ok_block);
361 LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(err_val), g->error_decls.length, false);389 LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(err_val), g->error_decls.length, false);
362 LLVMValueRef is_too_big_val = LLVMBuildICmp(g->builder, LLVMIntUGE, err_val, end_val, "");390 add_bounds_check(g, node, err_val, LLVMIntNE, zero, LLVMIntULT, end_val);
363 LLVMBuildCondBr(g->builder, is_too_big_val, bounds_check_fail_block, ok_block);
364
365 LLVMPositionBuilderAtEnd(g->builder, ok_block);
366 }391 }
367392
368 LLVMValueRef indices[] = {393 LLVMValueRef indices[] = {
...@@ -869,6 +894,11 @@ static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMVal...@@ -869,6 +894,11 @@ static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMVal
869 }894 }
870895
871 if (array_type->id == TypeTableEntryIdArray) {896 if (array_type->id == TypeTableEntryIdArray) {
897 if (want_debug_safety(g, source_node)) {
898 LLVMValueRef end = LLVMConstInt(g->builtin_types.entry_isize->type_ref,
899 array_type->data.array.len, false);
900 add_bounds_check(g, source_node, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, end);
901 }
872 LLVMValueRef indices[] = {902 LLVMValueRef indices[] = {
873 LLVMConstNull(g->builtin_types.entry_isize->type_ref),903 LLVMConstNull(g->builtin_types.entry_isize->type_ref),
874 subscript_value904 subscript_value
...@@ -887,6 +917,15 @@ static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMVal...@@ -887,6 +917,15 @@ static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMVal
887 assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);917 assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
888 assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);918 assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);
889919
920 if (want_debug_safety(g, source_node)) {
921 add_debug_source_node(g, source_node);
922 int len_index = array_type->data.structure.fields[1].gen_index;
923 assert(len_index >= 0);
924 LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, len_index, "");
925 LLVMValueRef len = LLVMBuildLoad(g->builder, len_ptr, "");
926 add_bounds_check(g, source_node, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, len);
927 }
928
890 add_debug_source_node(g, source_node);929 add_debug_source_node(g, source_node);
891 int ptr_index = array_type->data.structure.fields[0].gen_index;930 int ptr_index = array_type->data.structure.fields[0].gen_index;
892 assert(ptr_index >= 0);931 assert(ptr_index >= 0);
...@@ -907,7 +946,6 @@ static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) {...@@ -907,7 +946,6 @@ static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) {
907 LLVMValueRef array_ptr = gen_array_base_ptr(g, array_expr_node);946 LLVMValueRef array_ptr = gen_array_base_ptr(g, array_expr_node);
908947
909 LLVMValueRef subscript_value = gen_expr(g, node->data.array_access_expr.subscript);948 LLVMValueRef subscript_value = gen_expr(g, node->data.array_access_expr.subscript);
910
911 return gen_array_elem_ptr(g, node, array_ptr, array_type, subscript_value);949 return gen_array_elem_ptr(g, node, array_ptr, array_type, subscript_value);
912}950}
913951
...@@ -969,6 +1007,15 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) {...@@ -969,6 +1007,15 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) {
969 end_val = LLVMConstInt(g->builtin_types.entry_isize->type_ref, array_type->data.array.len, false);1007 end_val = LLVMConstInt(g->builtin_types.entry_isize->type_ref, array_type->data.array.len, false);
970 }1008 }
9711009
1010 if (want_debug_safety(g, node)) {
1011 add_bounds_check(g, node, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
1012 if (node->data.slice_expr.end) {
1013 LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_isize->type_ref,
1014 array_type->data.array.len, false);
1015 add_bounds_check(g, node, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end);
1016 }
1017 }
1018
972 add_debug_source_node(g, node);1019 add_debug_source_node(g, node);
973 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");1020 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");
974 LLVMValueRef indices[] = {1021 LLVMValueRef indices[] = {
...@@ -987,6 +1034,10 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) {...@@ -987,6 +1034,10 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) {
987 LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start);1034 LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start);
988 LLVMValueRef end_val = gen_expr(g, node->data.slice_expr.end);1035 LLVMValueRef end_val = gen_expr(g, node->data.slice_expr.end);
9891036
1037 if (want_debug_safety(g, node)) {
1038 add_bounds_check(g, node, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
1039 }
1040
990 add_debug_source_node(g, node);1041 add_debug_source_node(g, node);
991 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");1042 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, 0, "");
992 LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, "");1043 LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, "");
...@@ -1002,22 +1053,33 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) {...@@ -1002,22 +1053,33 @@ static LLVMValueRef gen_slice_expr(CodeGen *g, AstNode *node) {
1002 assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);1053 assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
1003 assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);1054 assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);
10041055
1056 int ptr_index = array_type->data.structure.fields[0].gen_index;
1057 assert(ptr_index >= 0);
1058 int len_index = array_type->data.structure.fields[1].gen_index;
1059 assert(len_index >= 0);
1060
1061 LLVMValueRef prev_end = nullptr;
1062 if (!node->data.slice_expr.end || want_debug_safety(g, node)) {
1063 add_debug_source_node(g, node);
1064 LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, len_index, "");
1065 prev_end = LLVMBuildLoad(g->builder, src_len_ptr, "");
1066 }
1067
1005 LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start);1068 LLVMValueRef start_val = gen_expr(g, node->data.slice_expr.start);
1006 LLVMValueRef end_val;1069 LLVMValueRef end_val;
1007 if (node->data.slice_expr.end) {1070 if (node->data.slice_expr.end) {
1008 end_val = gen_expr(g, node->data.slice_expr.end);1071 end_val = gen_expr(g, node->data.slice_expr.end);
1009 } else {1072 } else {
1010 add_debug_source_node(g, node);1073 end_val = prev_end;
1011 int len_index = array_type->data.structure.fields[1].gen_index;
1012 assert(len_index >= 0);
1013 LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, len_index, "");
1014 end_val = LLVMBuildLoad(g->builder, src_len_ptr, "");
1015 }1074 }
10161075
1017 int ptr_index = array_type->data.structure.fields[0].gen_index;1076 if (want_debug_safety(g, node)) {
1018 assert(ptr_index >= 0);1077 assert(prev_end);
1019 int len_index = array_type->data.structure.fields[1].gen_index;1078 add_bounds_check(g, node, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
1020 assert(len_index >= 0);1079 if (node->data.slice_expr.end) {
1080 add_bounds_check(g, node, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end);
1081 }
1082 }
10211083
1022 add_debug_source_node(g, node);1084 add_debug_source_node(g, node);
1023 LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, ptr_index, "");1085 LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, ptr_index, "");
...@@ -1225,7 +1287,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {...@@ -1225,7 +1287,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
1225 assert(expr_type->id == TypeTableEntryIdErrorUnion);1287 assert(expr_type->id == TypeTableEntryIdErrorUnion);
1226 TypeTableEntry *child_type = expr_type->data.error.child_type;1288 TypeTableEntry *child_type = expr_type->data.error.child_type;
12271289
1228 if (!g->is_release_build) {1290 if (want_debug_safety(g, node)) {
1229 LLVMValueRef err_val;1291 LLVMValueRef err_val;
1230 if (type_has_bits(child_type)) {1292 if (type_has_bits(child_type)) {
1231 add_debug_source_node(g, node);1293 add_debug_source_node(g, node);
...@@ -1263,7 +1325,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {...@@ -1263,7 +1325,7 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
1263 assert(expr_type->id == TypeTableEntryIdMaybe);1325 assert(expr_type->id == TypeTableEntryIdMaybe);
1264 TypeTableEntry *child_type = expr_type->data.maybe.child_type;1326 TypeTableEntry *child_type = expr_type->data.maybe.child_type;
12651327
1266 if (!g->is_release_build) {1328 if (want_debug_safety(g, node)) {
1267 add_debug_source_node(g, node);1329 add_debug_source_node(g, node);
1268 LLVMValueRef cond_val;1330 LLVMValueRef cond_val;
1269 if (child_type->id == TypeTableEntryIdPointer ||1331 if (child_type->id == TypeTableEntryIdPointer ||
...@@ -2261,7 +2323,7 @@ static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) {...@@ -2261,7 +2323,7 @@ static LLVMValueRef gen_container_init_expr(CodeGen *g, AstNode *node) {
2261 } else if (type_entry->id == TypeTableEntryIdUnreachable) {2323 } else if (type_entry->id == TypeTableEntryIdUnreachable) {
2262 assert(node->data.container_init_expr.entries.length == 0);2324 assert(node->data.container_init_expr.entries.length == 0);
2263 add_debug_source_node(g, node);2325 add_debug_source_node(g, node);
2264 if (!g->is_release_build) {2326 if (want_debug_safety(g, node)) {
2265 LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");2327 LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");
2266 }2328 }
2267 LLVMBuildUnreachable(g->builder);2329 LLVMBuildUnreachable(g->builder);
...@@ -2575,7 +2637,7 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa...@@ -2575,7 +2637,7 @@ static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVa
2575 }2637 }
2576 }2638 }
2577 }2639 }
2578 if (!ignore_uninit && !g->is_release_build) {2640 if (!ignore_uninit && want_debug_safety(g, source_node)) {
2579 TypeTableEntry *isize = g->builtin_types.entry_isize;2641 TypeTableEntry *isize = g->builtin_types.entry_isize;
2580 uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, variable->type->type_ref);2642 uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, variable->type->type_ref);
2581 uint64_t align_bytes = get_memcpy_align(g, variable->type);2643 uint64_t align_bytes = get_memcpy_align(g, variable->type);
...@@ -2790,7 +2852,7 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {...@@ -2790,7 +2852,7 @@ static LLVMValueRef gen_switch_expr(CodeGen *g, AstNode *node) {
2790 if (!else_prong) {2852 if (!else_prong) {
2791 LLVMPositionBuilderAtEnd(g->builder, else_block);2853 LLVMPositionBuilderAtEnd(g->builder, else_block);
2792 add_debug_source_node(g, node);2854 add_debug_source_node(g, node);
2793 if (!g->is_release_build) {2855 if (want_debug_safety(g, node)) {
2794 LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");2856 LLVMBuildCall(g->builder, g->trap_fn_val, nullptr, 0, "");
2795 }2857 }
2796 LLVMBuildUnreachable(g->builder);2858 LLVMBuildUnreachable(g->builder);
...@@ -3383,6 +3445,10 @@ static void do_code_gen(CodeGen *g) {...@@ -3383,6 +3445,10 @@ static void do_code_gen(CodeGen *g) {
33833445
3384 // Generate the list of test function pointers.3446 // Generate the list of test function pointers.
3385 if (g->is_test_build) {3447 if (g->is_test_build) {
3448 if (g->test_fn_count == 0) {
3449 fprintf(stderr, "No tests to run.\n");
3450 exit(0);
3451 }
3386 assert(g->test_fn_count > 0);3452 assert(g->test_fn_count > 0);
3387 assert(next_test_index == g->test_fn_count);3453 assert(next_test_index == g->test_fn_count);
33883454
std/builtin.zig+2
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1// These functions are provided when not linking against libc because LLVM1// These functions are provided when not linking against libc because LLVM
2// sometimes generates code that calls them.2// sometimes generates code that calls them.
33
4#debug_safety(false)
4export fn memset(dest: &u8, c: u8, n: isize) -> &u8 {5export fn memset(dest: &u8, c: u8, n: isize) -> &u8 {
5 var index : @typeof(n) = 0;6 var index : @typeof(n) = 0;
6 while (index != n) {7 while (index != n) {
...@@ -10,6 +11,7 @@ export fn memset(dest: &u8, c: u8, n: isize) -> &u8 {...@@ -10,6 +11,7 @@ export fn memset(dest: &u8, c: u8, n: isize) -> &u8 {
10 return dest;11 return dest;
11}12}
1213
14#debug_safety(false)
13export fn memcpy(noalias dest: &u8, noalias src: &const u8, n: isize) -> &u8 {15export fn memcpy(noalias dest: &u8, noalias src: &const u8, n: isize) -> &u8 {
14 var index : @typeof(n) = 0;16 var index : @typeof(n) = 0;
15 while (index != n) {17 while (index != n) {
test/run_tests.cpp+99-23
...@@ -27,6 +27,7 @@ struct TestCase {...@@ -27,6 +27,7 @@ struct TestCase {
27 ZigList<const char *> program_args;27 ZigList<const char *> program_args;
28 bool is_parseh;28 bool is_parseh;
29 bool is_self_hosted;29 bool is_self_hosted;
30 bool is_debug_safety;
30};31};
3132
32static ZigList<TestCase*> test_cases = {0};33static ZigList<TestCase*> test_cases = {0};
...@@ -122,6 +123,55 @@ static TestCase *add_compile_fail_case(const char *case_name, const char *source...@@ -122,6 +123,55 @@ static TestCase *add_compile_fail_case(const char *case_name, const char *source
122 return test_case;123 return test_case;
123}124}
124125
126static void add_debug_safety_case(const char *case_name, const char *source) {
127 {
128 TestCase *test_case = allocate<TestCase>(1);
129 test_case->is_debug_safety = true;
130 test_case->case_name = buf_ptr(buf_sprintf("%s (debug)", case_name));
131 test_case->source_files.resize(1);
132 test_case->source_files.at(0).relative_path = tmp_source_path;
133 test_case->source_files.at(0).source_code = source;
134
135 test_case->compiler_args.append("build");
136 test_case->compiler_args.append(tmp_source_path);
137
138 test_case->compiler_args.append("--name");
139 test_case->compiler_args.append("test");
140
141 test_case->compiler_args.append("--export");
142 test_case->compiler_args.append("exe");
143
144 test_case->compiler_args.append("--output");
145 test_case->compiler_args.append(tmp_exe_path);
146
147 test_cases.append(test_case);
148 }
149 {
150 TestCase *test_case = allocate<TestCase>(1);
151 test_case->case_name = buf_ptr(buf_sprintf("%s (release)", case_name));
152 test_case->source_files.resize(1);
153 test_case->source_files.at(0).relative_path = tmp_source_path;
154 test_case->source_files.at(0).source_code = source;
155 test_case->output = "";
156
157 test_case->compiler_args.append("build");
158 test_case->compiler_args.append(tmp_source_path);
159
160 test_case->compiler_args.append("--name");
161 test_case->compiler_args.append("test");
162
163 test_case->compiler_args.append("--export");
164 test_case->compiler_args.append("exe");
165
166 test_case->compiler_args.append("--output");
167 test_case->compiler_args.append(tmp_exe_path);
168
169 test_case->compiler_args.append("--release");
170
171 test_cases.append(test_case);
172 }
173}
174
125static TestCase *add_parseh_case(const char *case_name, const char *source, int count, ...) {175static TestCase *add_parseh_case(const char *case_name, const char *source, int count, ...) {
126 va_list ap;176 va_list ap;
127 va_start(ap, count);177 va_start(ap, count);
...@@ -1247,6 +1297,22 @@ fn bar() -> i32 { 2 }...@@ -1247,6 +1297,22 @@ fn bar() -> i32 { 2 }
1247 )SOURCE", 1, ".tmp_source.zig:3:15: error: unable to infer expression type");1297 )SOURCE", 1, ".tmp_source.zig:3:15: error: unable to infer expression type");
1248}1298}
12491299
1300static void add_debug_safety_test_cases(void) {
1301 add_debug_safety_case("out of bounds slice access", R"SOURCE(
1302pub fn main(args: [][]u8) -> %void {
1303 const a = []i32{1, 2, 3, 4};
1304 baz(bar(a));
1305}
1306#static_eval_enable(false)
1307fn bar(a: []i32) -> i32 {
1308 a[4]
1309}
1310#static_eval_enable(false)
1311fn baz(a: i32) {}
1312 )SOURCE");
1313
1314}
1315
1250//////////////////////////////////////////////////////////////////////////////1316//////////////////////////////////////////////////////////////////////////////
12511317
1252static void add_parseh_test_cases(void) {1318static void add_parseh_test_cases(void) {
...@@ -1455,6 +1521,14 @@ static void print_compiler_invocation(TestCase *test_case) {...@@ -1455,6 +1521,14 @@ static void print_compiler_invocation(TestCase *test_case) {
1455 printf("\n");1521 printf("\n");
1456}1522}
14571523
1524static void print_exe_invocation(TestCase *test_case) {
1525 printf("%s", tmp_exe_path);
1526 for (int i = 0; i < test_case->program_args.length; i += 1) {
1527 printf(" %s", test_case->program_args.at(i));
1528 }
1529 printf("\n");
1530}
1531
1458static void run_test(TestCase *test_case) {1532static void run_test(TestCase *test_case) {
1459 if (test_case->is_self_hosted) {1533 if (test_case->is_self_hosted) {
1460 return run_self_hosted_test();1534 return run_self_hosted_test();
...@@ -1531,32 +1605,33 @@ static void run_test(TestCase *test_case) {...@@ -1531,32 +1605,33 @@ static void run_test(TestCase *test_case) {
1531 Buf program_stdout = BUF_INIT;1605 Buf program_stdout = BUF_INIT;
1532 os_exec_process(tmp_exe_path, test_case->program_args, &return_code, &program_stderr, &program_stdout);1606 os_exec_process(tmp_exe_path, test_case->program_args, &return_code, &program_stderr, &program_stdout);
15331607
1534 if (return_code != 0) {1608 if (test_case->is_debug_safety) {
1535 printf("\nProgram exited with return code %d:\n", return_code);1609 if (return_code == 0) {
1536 print_compiler_invocation(test_case);1610 printf("\nProgram expected to hit debug trap but exited with return code 0\n");
1537 printf("%s", tmp_exe_path);1611 print_compiler_invocation(test_case);
1538 for (int i = 0; i < test_case->program_args.length; i += 1) {1612 print_exe_invocation(test_case);
1539 printf(" %s", test_case->program_args.at(i));1613 exit(1);
1614 }
1615 } else {
1616 if (return_code != 0) {
1617 printf("\nProgram exited with return code %d:\n", return_code);
1618 print_compiler_invocation(test_case);
1619 print_exe_invocation(test_case);
1620 printf("%s\n", buf_ptr(&program_stderr));
1621 exit(1);
1540 }1622 }
1541 printf("\n");
1542 printf("%s\n", buf_ptr(&program_stderr));
1543 exit(1);
1544 }
15451623
1546 if (!buf_eql_str(&program_stdout, test_case->output)) {1624 if (!buf_eql_str(&program_stdout, test_case->output)) {
1547 printf("\n");1625 printf("\n");
1548 print_compiler_invocation(test_case);1626 print_compiler_invocation(test_case);
1549 printf("%s", tmp_exe_path);1627 print_exe_invocation(test_case);
1550 for (int i = 0; i < test_case->program_args.length; i += 1) {1628 printf("==== Test failed. Expected output: ====\n");
1551 printf(" %s", test_case->program_args.at(i));1629 printf("%s\n", test_case->output);
1630 printf("========= Actual output: ==============\n");
1631 printf("%s\n", buf_ptr(&program_stdout));
1632 printf("=======================================\n");
1633 exit(1);
1552 }1634 }
1553 printf("\n");
1554 printf("==== Test failed. Expected output: ====\n");
1555 printf("%s\n", test_case->output);
1556 printf("========= Actual output: ==============\n");
1557 printf("%s\n", buf_ptr(&program_stdout));
1558 printf("=======================================\n");
1559 exit(1);
1560 }1635 }
1561 }1636 }
15621637
...@@ -1606,6 +1681,7 @@ int main(int argc, char **argv) {...@@ -1606,6 +1681,7 @@ int main(int argc, char **argv) {
1606 }1681 }
1607 }1682 }
1608 add_compiling_test_cases();1683 add_compiling_test_cases();
1684 add_debug_safety_test_cases();
1609 add_compile_failure_test_cases();1685 add_compile_failure_test_cases();
1610 add_parseh_test_cases();1686 add_parseh_test_cases();
1611 add_self_hosted_tests();1687 add_self_hosted_tests();