My toy compiler crashes if I divide by zero in a constant expression:
int x = 1 / 0;
Is this behaviour allowed by the C and/or C++ standards?>
Others have already mentioned the relevant text from the standards, so, I'm not going to repeat that.
My C compiler's expression evaluating function takes an expression in Reverse Polish Notation (array of values (numbers and identifiers) and operators) and returns two things: a flag for whether or not the expression evaluates to a constant and the value if it's a constant (0 otherwise). If the result is a constant, the whole RPN reduces to just that constant. 1/0 is not a constant expression since it doesn't evaluate to a constant integer value. The RPN is not reduced for 1/0 and stays intact.
In C, static variables can be initialized with constant values only. So, the compiler errors out when it sees that an initializer for a static variable is not a constant. Variables of automatic storage can be initialized with non-constant expressions. In this case my compiler generates code to evaluate 1/0 (it still has the RPN for this expression!). If this code is reached at runtime, UB occurs as prescribed by the language standards. [On x86 this UB takes on the form of the division by zero CPU exception, while on MIPS this UB yields an incorrect quotient value (the CPU does not have a division by zero exception).]
My compiler properly supports short-circuiting in ||-expressions and &&-expressions. So, it evaluates 1 || 1/0
as 1 and 0 && 1/0
as 0, regardless of whether or not the right-hand operand of the logical operator is a constant. The expression evaluating function removes the right-hand operands of these operators (along with the operators) when they must not be evaluated and so 1 || 1/0
transforms into 1 != 0
(recall that the operands of && and || undergo comparison with 0), which yields 1 and 0 && 1/0
transforms into 0 != 0
, which yields 0.
Another case to take care of is INT_MIN / -1
and INT_MIN % -1
(ditto for larger integer types). The quotient is not representable as a signed int (in the case of 2's complement signed integers, which is what we have in all modern CPUs) and so this is UB as well (you get the same division by zero exception on x86 at runtime). I handle this case similarly. This expression can't initialize a variable of static storage and it's thrown away if it's not evaluated in the logical &&/|| operator. It can initialize an automatic variable, possibly leading to UB at runtime.
I also issue a warning when such division is encountered.