Commit 69109bc270

Andrew Kelley <superjoe30@gmail.com>
2016-04-13 01:18:41
add error for dividing by zero in static function evaluation
1 parent be4df96
src/analyze.cpp
@@ -2653,26 +2653,6 @@ static TypeTableEntry *resolve_expr_const_val_as_float_num_lit(CodeGen *g, AstNo
     return g->builtin_types.entry_num_lit_float;
 }
 
-static TypeTableEntry *resolve_expr_const_val_as_bignum_op(CodeGen *g, AstNode *node,
-        bool (*bignum_fn)(BigNum *, BigNum *, BigNum *), AstNode *op1, AstNode *op2,
-        TypeTableEntry *resolved_type)
-{
-    ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
-    ConstExprValue *op1_val = &get_resolved_expr(op1)->const_val;
-    ConstExprValue *op2_val = &get_resolved_expr(op2)->const_val;
-
-    const_val->ok = true;
-
-    if (bignum_fn(&const_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum)) {
-        add_node_error(g, node,
-            buf_sprintf("value cannot be represented in any integer type"));
-    } else {
-        num_lit_fits_in_other_type(g, node, resolved_type);
-    }
-
-    return resolved_type;
-}
-
 static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import,
         BlockContext *context, AstNode *node, Buf *err_name)
 {
@@ -3074,37 +3054,23 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
                     return resolved_type;
                 }
 
-                if (bin_op_type == BinOpTypeAdd) {
-                    return resolve_expr_const_val_as_bignum_op(g, node, bignum_add, *op1, *op2, resolved_type);
-                } else if (bin_op_type == BinOpTypeSub) {
-                    return resolve_expr_const_val_as_bignum_op(g, node, bignum_sub, *op1, *op2, resolved_type);
-                } else if (bin_op_type == BinOpTypeMult) {
-                    return resolve_expr_const_val_as_bignum_op(g, node, bignum_mul, *op1, *op2, resolved_type);
-                } else if (bin_op_type == BinOpTypeDiv) {
-                    ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val;
-                    if ((is_int && op2_val->data.x_bignum.data.x_uint == 0) ||
-                        (is_float && op2_val->data.x_bignum.data.x_float == 0.0))
-                    {
+                ConstExprValue *out_val = &get_resolved_expr(node)->const_val;
+                int err;
+                if ((err = eval_const_expr_bin_op(op1_val, resolved_type, bin_op_type,
+                                op2_val, resolved_type, out_val)))
+                {
+                    if (err == ErrorDivByZero) {
                         add_node_error(g, node, buf_sprintf("division by zero is undefined"));
                         return g->builtin_types.entry_invalid;
-                    } else {
-                        return resolve_expr_const_val_as_bignum_op(g, node, bignum_div, *op1, *op2, resolved_type);
+                    } else if (err == ErrorOverflow) {
+                        add_node_error(g, node, buf_sprintf("value cannot be represented in any integer type"));
+                        return g->builtin_types.entry_invalid;
                     }
-                } else if (bin_op_type == BinOpTypeMod) {
-                    return resolve_expr_const_val_as_bignum_op(g, node, bignum_mod, *op1, *op2, resolved_type);
-                } else if (bin_op_type == BinOpTypeBinOr) {
-                    return resolve_expr_const_val_as_bignum_op(g, node, bignum_or, *op1, *op2, resolved_type);
-                } else if (bin_op_type == BinOpTypeBinAnd) {
-                    return resolve_expr_const_val_as_bignum_op(g, node, bignum_and, *op1, *op2, resolved_type);
-                } else if (bin_op_type == BinOpTypeBinXor) {
-                    return resolve_expr_const_val_as_bignum_op(g, node, bignum_xor, *op1, *op2, resolved_type);
-                } else if (bin_op_type == BinOpTypeBitShiftLeft) {
-                    return resolve_expr_const_val_as_bignum_op(g, node, bignum_shl, *op1, *op2, resolved_type);
-                } else if (bin_op_type == BinOpTypeBitShiftRight) {
-                    return resolve_expr_const_val_as_bignum_op(g, node, bignum_shr, *op1, *op2, resolved_type);
-                } else {
-                    zig_unreachable();
+                    return g->builtin_types.entry_invalid;
                 }
+
+                num_lit_fits_in_other_type(g, node, resolved_type);
+                return resolved_type;
             }
         case BinOpTypeUnwrapMaybe:
             {
src/error.cpp
@@ -12,6 +12,8 @@ const char *err_str(int err) {
         case ErrorFileNotFound: return "file not found";
         case ErrorFileSystem: return "file system error";
         case ErrorFileTooBig: return "file too big";
+        case ErrorDivByZero: return "division by zero";
+        case ErrorOverflow: return "overflow";
     }
     return "(invalid error)";
 }
src/error.hpp
@@ -19,6 +19,8 @@ enum Error {
     ErrorFileNotFound,
     ErrorFileSystem,
     ErrorFileTooBig,
+    ErrorDivByZero,
+    ErrorOverflow
 };
 
 const char *err_str(int err);
src/eval.cpp
@@ -1,5 +1,6 @@
 #include "eval.hpp"
 #include "analyze.hpp"
+#include "error.hpp"
 
 static bool eval_fn_args(EvalFnRoot *efr, FnTableEntry *fn, ConstExprValue *args, ConstExprValue *out_val);
 
@@ -96,16 +97,20 @@ static bool eval_bool_bin_op_bool(bool a, BinOpType bin_op, bool b) {
     }
 }
 
-static void eval_const_expr_bin_op_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val,
+static int eval_const_expr_bin_op_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val,
         ConstExprValue *out_val, bool (*bignum_fn)(BigNum *, BigNum *, BigNum *))
 {
     bool overflow = bignum_fn(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum);
-    assert(!overflow);
+    if (overflow) {
+        return ErrorOverflow;
+    }
+
     out_val->ok = true;
     out_val->depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var;
+    return 0;
 }
 
-void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,
+int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,
         BinOpType bin_op, ConstExprValue *op2_val, TypeTableEntry *op2_type, ConstExprValue *out_val)
 {
     assert(op1_val->ok);
@@ -126,7 +131,7 @@ void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,
         case BinOpTypeAssignBoolAnd:
         case BinOpTypeAssignBoolOr:
             out_val->ok = true;
-            return;
+            return 0;
         case BinOpTypeBoolOr:
         case BinOpTypeBoolAnd:
             assert(op1_type->id == TypeTableEntryIdBool);
@@ -134,7 +139,7 @@ void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,
             out_val->data.x_bool = eval_bool_bin_op_bool(op1_val->data.x_bool, bin_op, op2_val->data.x_bool);
             out_val->ok = true;
             out_val->depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var;
-            return;
+            return 0;
         case BinOpTypeCmpEq:
         case BinOpTypeCmpNotEq:
         case BinOpTypeCmpLessThan:
@@ -181,7 +186,7 @@ void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,
                     op1_val->depends_on_compile_var || op2_val->depends_on_compile_var;
                 out_val->data.x_bool = answer;
                 out_val->ok = true;
-                return;
+                return 0;
             }
         case BinOpTypeAdd:
             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,
                 if ((is_int && op2_val->data.x_bignum.data.x_uint == 0) ||
                     (is_float && op2_val->data.x_bignum.data.x_float == 0.0))
                 {
-                    zig_panic("TODO handle errors in eval");
+                    return ErrorDivByZero;
                 } else {
                     return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_div);
                 }
@@ -249,7 +254,26 @@ static bool eval_bin_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val)
 
     BinOpType bin_op = node->data.bin_op_expr.bin_op;
 
-    eval_const_expr_bin_op(&op1_val, op1_type, bin_op, &op2_val, op2_type, out_val);
+    int err;
+    if ((err = eval_const_expr_bin_op(&op1_val, op1_type, bin_op, &op2_val, op2_type, out_val))) {
+        ef->root->abort = true;
+        if (err == ErrorDivByZero) {
+            ErrorMsg *msg = add_node_error(ef->root->codegen, ef->root->fn->fn_def_node,
+                    buf_sprintf("function evaluation caused division by zero"));
+            add_error_note(ef->root->codegen, msg, ef->root->call_node, buf_sprintf("called from here"));
+            add_error_note(ef->root->codegen, msg, node, buf_sprintf("division by zero here"));
+        } else if (err == ErrorOverflow) {
+            ErrorMsg *msg = add_node_error(ef->root->codegen, ef->root->fn->fn_def_node,
+                    buf_sprintf("function evaluation caused overflow"));
+            add_error_note(ef->root->codegen, msg, ef->root->call_node, buf_sprintf("called from here"));
+            add_error_note(ef->root->codegen, msg, node, buf_sprintf("overflow occurred here"));
+        } else {
+            zig_unreachable();
+        }
+        return true;
+    }
+
+    assert(out_val->ok);
 
     return false;
 }
@@ -1133,8 +1157,11 @@ bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_va
         return true;
     }
 
-    assert(out_val->ok);
+    if (efr.abort) {
+        return true;
+    }
 
-    return efr.abort;
+    assert(out_val->ok);
+    return false;
 }
 
src/eval.hpp
@@ -14,7 +14,7 @@ bool eval_fn(CodeGen *g, AstNode *node, FnTableEntry *fn, ConstExprValue *out_va
         AstNode *struct_node);
 
 bool const_values_equal(ConstExprValue *a, ConstExprValue *b, TypeTableEntry *type_entry);
-void eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,
+int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,
         BinOpType bin_op, ConstExprValue *op2_val, TypeTableEntry *op2_type, ConstExprValue *out_val);
 
 void eval_const_expr_implicit_cast(CastOp cast_op,
test/run_tests.cpp
@@ -1481,6 +1481,16 @@ fn foo() {
     const pointer = &array[0];
 }
     )SOURCE", 1, ".tmp_source.zig:4:27: error: out of bounds array access");
+
+    add_compile_fail_case("compile time division by zero", R"SOURCE(
+const x = foo(0);
+fn foo(x: i32) -> i32 {
+    1 / x
+}
+    )SOURCE", 3,
+            ".tmp_source.zig:3:1: error: function evaluation caused division by zero",
+            ".tmp_source.zig:2:14: note: called from here",
+            ".tmp_source.zig:4:7: note: division by zero here");
 }
 
 //////////////////////////////////////////////////////////////////////////////