aboutsummaryrefslogtreecommitdiff
path: root/src/check_expr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/check_expr.cpp')
-rw-r--r--src/check_expr.cpp75
1 files changed, 73 insertions, 2 deletions
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index db82a60ce..e0b6408e3 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -129,6 +129,8 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
gb_internal bool is_exact_value_zero(ExactValue const &v);
+gb_internal IntegerDivisionByZeroKind check_for_integer_division_by_zero(CheckerContext *c, Ast *node);
+
enum LoadDirectiveResult {
LoadDirective_Success = 0,
LoadDirective_Error = 1,
@@ -4309,7 +4311,25 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
}
if (fail) {
- error(y->expr, "Division by zero not allowed");
+ if (is_type_integer(x->type) || (x->mode == Addressing_Constant && x->value.kind == ExactValue_Integer)) {
+ if (check_for_integer_division_by_zero(c, node) != IntegerDivisionByZero_Trap) {
+ // Okay
+ break;
+ }
+ }
+
+ switch (op.kind) {
+ case Token_Mod:
+ case Token_ModMod:
+ case Token_ModEq:
+ case Token_ModModEq:
+ error(y->expr, "Division by zero through '%.*s' not allowed", LIT(token_strings[op.kind]));
+ break;
+ case Token_Quo:
+ case Token_QuoEq:
+ error(y->expr, "Division by zero not allowed");
+ break;
+ }
x->mode = Addressing_Invalid;
return;
}
@@ -4349,7 +4369,43 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
}
}
- x->value = exact_binary_operator_value(op.kind, a, b);
+ match_exact_values(&a, &b);
+
+
+ IntegerDivisionByZeroKind zero_behaviour = check_for_integer_division_by_zero(c, node);
+ if (zero_behaviour != IntegerDivisionByZero_Trap &&
+ b.kind == ExactValue_Integer && big_int_is_zero(&b.value_integer) &&
+ (op.kind == Token_QuoEq || op.kind == Token_Mod || op.kind == Token_ModMod)) {
+ if (op.kind == Token_QuoEq) {
+ if (zero_behaviour == IntegerDivisionByZero_Zero) {
+ // x/0 == 0
+ x->value = b;
+ } else {
+ // x/0 == x
+ x->value = a;
+ }
+ } else {
+ /*
+ NOTE(bill): @integer division by zero rules
+
+ truncated: r = a - b*trunc(a/b)
+ floored: r = a - b*floor(a/b)
+
+ IFF a/0 == 0, then (a%0 == a) or (a%%0 == a)
+ IFF a/0 == a, then (a%0 == 0) or (a%%0 == 0)
+ */
+
+ if (zero_behaviour == IntegerDivisionByZero_Zero) {
+ // x%0 == x
+ x->value = a;
+ } else {
+ // x%0 == 0
+ x->value = b;
+ }
+ }
+ } else {
+ x->value = exact_binary_operator_value(op.kind, a, b);
+ }
if (is_type_typed(x->type)) {
if (node != nullptr) {
@@ -9602,6 +9658,21 @@ gb_internal bool check_for_dynamic_literals(CheckerContext *c, Ast *node, AstCom
return cl->elems.count > 0;
}
+gb_internal IntegerDivisionByZeroKind check_for_integer_division_by_zero(CheckerContext *c, Ast *node) {
+ // TODO(bill): per file `#+feature` flags
+ u64 flags = check_feature_flags(c, node);
+ if ((flags & OptInFeatureFlag_IntegerDivisionByZero_Trap) != 0) {
+ return IntegerDivisionByZero_Trap;
+ }
+ if ((flags & OptInFeatureFlag_IntegerDivisionByZero_Zero) != 0) {
+ return IntegerDivisionByZero_Zero;
+ }
+ if ((flags & OptInFeatureFlag_IntegerDivisionByZero_Self) != 0) {
+ return IntegerDivisionByZero_Self;
+ }
+ return build_context.integer_division_by_zero_behaviour;
+}
+
gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ExprKind kind = Expr_Expr;
ast_node(cl, CompoundLit, node);