| author | |
| committer | |
| log | 69109bc270c3167a3534dd32fc4f4def855e0be9 |
| tree | b9839d8b4f0a5475328e6c1988bdb9ccf5c862ad |
| parent | be4df96e4b80a3307b3661fd5ca3114478499daf |
6 files changed, 65 insertions(+), 58 deletions(-)
src/analyze.cpp+13-47| ... | ... | @@ -2653,26 +2653,6 @@ static TypeTableEntry *resolve_expr_const_val_as_float_num_lit(CodeGen *g, AstNo |
| 2653 | 2653 | return g->builtin_types.entry_num_lit_float; |
| 2654 | 2654 | } |
| 2655 | 2655 | |
| 2656 | static TypeTableEntry *resolve_expr_const_val_as_bignum_op(CodeGen *g, AstNode *node, | |
| 2657 | bool (*bignum_fn)(BigNum *, BigNum *, BigNum *), AstNode *op1, AstNode *op2, | |
| 2658 | TypeTableEntry *resolved_type) | |
| 2659 | { | |
| 2660 | ConstExprValue *const_val = &get_resolved_expr(node)->const_val; | |
| 2661 | ConstExprValue *op1_val = &get_resolved_expr(op1)->const_val; | |
| 2662 | ConstExprValue *op2_val = &get_resolved_expr(op2)->const_val; | |
| 2663 | ||
| 2664 | const_val->ok = true; | |
| 2665 | ||
| 2666 | if (bignum_fn(&const_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) { | |
| 2667 | add_node_error(g, node, | |
| 2668 | buf_sprintf("value cannot be represented in any integer type")); | |
| 2669 | } else { | |
| 2670 | num_lit_fits_in_other_type(g, node, resolved_type); | |
| 2671 | } | |
| 2672 | ||
| 2673 | return resolved_type; | |
| 2674 | } | |
| 2675 | ||
| 2676 | 2656 | static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import, |
| 2677 | 2657 | BlockContext *context, AstNode *node, Buf *err_name) |
| 2678 | 2658 | { |
| ... | ... | @@ -3074,37 +3054,23 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, |
| 3074 | 3054 | return resolved_type; |
| 3075 | 3055 | } |
| 3076 | 3056 | |
| 3077 | if (bin_op_type == BinOpTypeAdd) { | |
| 3078 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_add, *op1, *op2, resolved_type); | |
| 3079 | } else if (bin_op_type == BinOpTypeSub) { | |
| 3080 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_sub, *op1, *op2, resolved_type); | |
| 3081 | } else if (bin_op_type == BinOpTypeMult) { | |
| 3082 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_mul, *op1, *op2, resolved_type); | |
| 3083 | } else if (bin_op_type == BinOpTypeDiv) { | |
| 3084 | ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val; | |
| 3085 | if ((is_int && op2_val->data.x_bignum.data.x_uint == 0) || | |
| 3086 | (is_float && op2_val->data.x_bignum.data.x_float == 0.0)) | |
| 3087 | { | |
| 3057 | ConstExprValue *out_val = &get_resolved_expr(node)->const_val; | |
| 3058 | int err; | |
| 3059 | if ((err = eval_const_expr_bin_op(op1_val, resolved_type, bin_op_type, | |
| 3060 | op2_val, resolved_type, out_val))) | |
| 3061 | { | |
| 3062 | if (err == ErrorDivByZero) { | |
| 3088 | 3063 | add_node_error(g, node, buf_sprintf("division by zero is undefined")); |
| 3089 | 3064 | return g->builtin_types.entry_invalid; |
| 3090 | } else { | |
| 3091 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_div, *op1, *op2, resolved_type); | |
| 3065 | } else if (err == ErrorOverflow) { | |
| 3066 | add_node_error(g, node, buf_sprintf("value cannot be represented in any integer type")); | |
| 3067 | return g->builtin_types.entry_invalid; | |
| 3092 | 3068 | } |
| 3093 | } else if (bin_op_type == BinOpTypeMod) { | |
| 3094 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_mod, *op1, *op2, resolved_type); | |
| 3095 | } else if (bin_op_type == BinOpTypeBinOr) { | |
| 3096 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_or, *op1, *op2, resolved_type); | |
| 3097 | } else if (bin_op_type == BinOpTypeBinAnd) { | |
| 3098 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_and, *op1, *op2, resolved_type); | |
| 3099 | } else if (bin_op_type == BinOpTypeBinXor) { | |
| 3100 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_xor, *op1, *op2, resolved_type); | |
| 3101 | } else if (bin_op_type == BinOpTypeBitShiftLeft) { | |
| 3102 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_shl, *op1, *op2, resolved_type); | |
| 3103 | } else if (bin_op_type == BinOpTypeBitShiftRight) { | |
| 3104 | return resolve_expr_const_val_as_bignum_op(g, node, bignum_shr, *op1, *op2, resolved_type); | |
| 3105 | } else { | |
| 3106 | zig_unreachable(); | |
| 3069 | return g->builtin_types.entry_invalid; | |
| 3107 | 3070 | } |
| 3071 | ||
| 3072 | num_lit_fits_in_other_type(g, node, resolved_type); | |
| 3073 | return resolved_type; | |
| 3108 | 3074 | } |
| 3109 | 3075 | case BinOpTypeUnwrapMaybe: |
| 3110 | 3076 | { |
src/error.cpp+2| ... | ... | @@ -12,6 +12,8 @@ const char *err_str(int err) { |
| 12 | 12 | case ErrorFileNotFound: return "file not found"; |
| 13 | 13 | case ErrorFileSystem: return "file system error"; |
| 14 | 14 | case ErrorFileTooBig: return "file too big"; |
| 15 | case ErrorDivByZero: return "division by zero"; | |
| 16 | case ErrorOverflow: return "overflow"; | |
| 15 | 17 | } |
| 16 | 18 | return "(invalid error)"; |
| 17 | 19 | } |
src/error.hpp+2| ... | ... | @@ -19,6 +19,8 @@ enum Error { |
| 19 | 19 | ErrorFileNotFound, |
| 20 | 20 | ErrorFileSystem, |
| 21 | 21 | ErrorFileTooBig, |
| 22 | ErrorDivByZero, | |
| 23 | ErrorOverflow | |
| 22 | 24 | }; |
| 23 | 25 | |
| 24 | 26 | const char *err_str(int err); |
src/eval.cpp+37-10| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | #include "eval.hpp" |
| 2 | 2 | #include "analyze.hpp" |
| 3 | #include "error.hpp" | |
| 3 | 4 | |
| 4 | 5 | static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args, ConstExprValue *out_val); |
| 5 | 6 | |
| ... | ... | @@ -96,16 +97,20 @@ static bool eval_bool_bin_op_bool(bool a, BinOpType bin_op, bool b) { |
| 96 | 97 | } |
| 97 | 98 | } |
| 98 | 99 | |
| 99 | static void eval_const_expr_bin_op_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val, | |
| 100 | static int eval_const_expr_bin_op_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val, | |
| 100 | 101 | ConstExprValue *out_val, bool (*bignum_fn)(BigNum *, BigNum *, BigNum *)) |
| 101 | 102 | { |
| 102 | 103 | bool overflow = bignum_fn(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum); |
| 103 | assert(!overflow); | |
| 104 | if (overflow) { | |
| 105 | return ErrorOverflow; | |
| 106 | } | |
| 107 | ||
| 104 | 108 | out_val->ok = true; |
| 105 | 109 | out_val->depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; |
| 110 | return 0; | |
| 106 | 111 | } |
| 107 | 112 | |
| 108 | void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, | |
| 113 | int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, | |
| 109 | 114 | BinOpType bin_op, ConstExprValue *op2_val, TypeTableEntry *op2_type, ConstExprValue *out_val) |
| 110 | 115 | { |
| 111 | 116 | assert(op1_val->ok); |
| ... | ... | @@ -126,7 +131,7 @@ void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, |
| 126 | 131 | case BinOpTypeAssignBoolAnd: |
| 127 | 132 | case BinOpTypeAssignBoolOr: |
| 128 | 133 | out_val->ok = true; |
| 129 | return; | |
| 134 | return 0; | |
| 130 | 135 | case BinOpTypeBoolOr: |
| 131 | 136 | case BinOpTypeBoolAnd: |
| 132 | 137 | assert(op1_type->id == TypeTableEntryIdBool); |
| ... | ... | @@ -134,7 +139,7 @@ void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, |
| 134 | 139 | out_val->data.x_bool = eval_bool_bin_op_bool(op1_val->data.x_bool, bin_op, op2_val->data.x_bool); |
| 135 | 140 | out_val->ok = true; |
| 136 | 141 | out_val->depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; |
| 137 | return; | |
| 142 | return 0; | |
| 138 | 143 | case BinOpTypeCmpEq: |
| 139 | 144 | case BinOpTypeCmpNotEq: |
| 140 | 145 | case BinOpTypeCmpLessThan: |
| ... | ... | @@ -181,7 +186,7 @@ void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, |
| 181 | 186 | op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; |
| 182 | 187 | out_val->data.x_bool = answer; |
| 183 | 188 | out_val->ok = true; |
| 184 | return; | |
| 189 | return 0; | |
| 185 | 190 | } |
| 186 | 191 | case BinOpTypeAdd: |
| 187 | 192 | return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_add); |
| ... | ... | @@ -215,7 +220,7 @@ void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, |
| 215 | 220 | if ((is_int && op2_val->data.x_bignum.data.x_uint == 0) || |
| 216 | 221 | (is_float && op2_val->data.x_bignum.data.x_float == 0.0)) |
| 217 | 222 | { |
| 218 | zig_panic("TODO handle errors in eval"); | |
| 223 | return ErrorDivByZero; | |
| 219 | 224 | } else { |
| 220 | 225 | return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_div); |
| 221 | 226 | } |
| ... | ... | @@ -249,7 +254,26 @@ static bool eval_bin_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) |
| 249 | 254 | |
| 250 | 255 | BinOpType bin_op = node->data.bin_op_expr.bin_op; |
| 251 | 256 | |
| 252 | eval_const_expr_bin_op(&op1_val, op1_type, bin_op, &op2_val, op2_type, out_val); | |
| 257 | int err; | |
| 258 | if ((err = eval_const_expr_bin_op(&op1_val, op1_type, bin_op, &op2_val, op2_type, out_val))) { | |
| 259 | ef->root->abort = true; | |
| 260 | if (err == ErrorDivByZero) { | |
| 261 | ErrorMsg *msg = add_node_error(ef->root->codegen, ef->root->fn->fn_def_node, | |
| 262 | buf_sprintf("function evaluation caused division by zero")); | |
| 263 | add_error_note(ef->root->codegen, msg, ef->root->call_node, buf_sprintf("called from here")); | |
| 264 | add_error_note(ef->root->codegen, msg, node, buf_sprintf("division by zero here")); | |
| 265 | } else if (err == ErrorOverflow) { | |
| 266 | ErrorMsg *msg = add_node_error(ef->root->codegen, ef->root->fn->fn_def_node, | |
| 267 | buf_sprintf("function evaluation caused overflow")); | |
| 268 | add_error_note(ef->root->codegen, msg, ef->root->call_node, buf_sprintf("called from here")); | |
| 269 | add_error_note(ef->root->codegen, msg, node, buf_sprintf("overflow occurred here")); | |
| 270 | } else { | |
| 271 | zig_unreachable(); | |
| 272 | } | |
| 273 | return true; | |
| 274 | } | |
| 275 | ||
| 276 | assert(out_val->ok); | |
| 253 | 277 | |
| 254 | 278 | return false; |
| 255 | 279 | } |
| ... | ... | @@ -1133,8 +1157,11 @@ bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_va |
| 1133 | 1157 | return true; |
| 1134 | 1158 | } |
| 1135 | 1159 | |
| 1136 | assert(out_val->ok); | |
| 1160 | if (efr.abort) { | |
| 1161 | return true; | |
| 1162 | } | |
| 1137 | 1163 | |
| 1138 | return efr.abort; | |
| 1164 | assert(out_val->ok); | |
| 1165 | return false; | |
| 1139 | 1166 | } |
| 1140 | 1167 |
src/eval.hpp+1-1| ... | ... | @@ -14,7 +14,7 @@ bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_va |
| 14 | 14 | AstNode *struct_node); |
| 15 | 15 | |
| 16 | 16 | bool const_values_equal(ConstExprValue *a, ConstExprValue *b, TypeTableEntry *type_entry); |
| 17 | void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, | |
| 17 | int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, | |
| 18 | 18 | BinOpType bin_op, ConstExprValue *op2_val, TypeTableEntry *op2_type, ConstExprValue *out_val); |
| 19 | 19 | |
| 20 | 20 | void eval_const_expr_implicit_cast(CastOp cast_op, |
test/run_tests.cpp+10| ... | ... | @@ -1481,6 +1481,16 @@ fn foo() { |
| 1481 | 1481 | const pointer = &array[0]; |
| 1482 | 1482 | } |
| 1483 | 1483 | )SOURCE", 1, ".tmp_source.zig:4:27: error: out of bounds array access"); |
| 1484 | ||
| 1485 | add_compile_fail_case("compile time division by zero", R"SOURCE( | |
| 1486 | const x = foo(0); | |
| 1487 | fn foo(x: i32) -> i32 { | |
| 1488 | 1 / x | |
| 1489 | } | |
| 1490 | )SOURCE", 3, | |
| 1491 | ".tmp_source.zig:3:1: error: function evaluation caused division by zero", | |
| 1492 | ".tmp_source.zig:2:14: note: called from here", | |
| 1493 | ".tmp_source.zig:4:7: note: division by zero here"); | |
| 1484 | 1494 | } |
| 1485 | 1495 | |
| 1486 | 1496 | ////////////////////////////////////////////////////////////////////////////// |