;;;; Arithmetic with no answer, unhandled. One program, one case per argument, ;;;; the same shape bounds.flan has and for the same reason: a death is ;;;; observable only as an exit status and a sentence on stderr, so each case ;;;; needs its own run. ;;;; ;;;; What is asserted is the *reason* — the location, and which operation ;;;; against which operands. Before this change none of these cases had a ;;;; reason to assert on: the first four were a raw SIGFPE, which prints ;;;; nothing at all, and the last was undefined and would have printed whatever ;;;; the optimiser decided the answer was. ;;;; ;;;; Everything comes through a global rather than a literal, which keeps the ;;;; operands dynamic. A literal divisor is exactly the case the guard is ;;;; allowed to elide, and folding these away would leave the test asserting on ;;;; a program that does not contain the check. (defvar zero i64) (defvar neg1 i64 -1) (defvar big i64 9223372036854775807) (defvar ten i64 10) (defvar uz u32) (defvar huge f64 1e300) (defn main [args [string]] i32 (let [n (i32 (bytes->i64 (bytes (at args 1)))) ;; The most negative i64. No literal spells it — the reader parses the ;; digits and then negates, and the positive half does not fit — so it ;; is built, which also keeps it out of the constant folder's reach. min (- (- (i64 0) big) 1)] (cond ;; None of these may die. Division that is fine has to stay fine, and ;; these are the four shapes the guard has an opinion about: an ordinary ;; dynamic divisor, a literal one the guard drops entirely, unsigned ;; division, which has no overflow case because it has no most-negative ;; value, and a float division by zero, which is an infinity and is a ;; defined answer this language is not in the business of refusing. (= n 0) (do (print (/ ten (+ zero 3))) (print " ") (print (/ ten 2)) (print " ") (print (/ (u32 100) (+ uz 7))) (print " ") (print (/ (f64 1.0) (f64 0.0))) (println "")) (= n 1) (print (/ ten zero)) ; divide by zero (= n 2) (print (% ten zero)) ; remainder by zero ;; The one division that overflows. Nothing is wrong with either ;; operand on its own; it is the pair, and it is the only pair. (= n 3) (print (/ min neg1)) ;; `srem` overflows on exactly the operands `sdiv` does, because the ;; quotient is what does not fit and a remainder computes one too. (= n 4) (print (% min neg1)) ;; A float too large for the destination, and the same value against a ;; narrower destination, which reports its own range. (= n 5) (print (i64 huge)) (= n 6) (print (i8 (/ huge 1e290))) ;; NaN, which fails the range test at both ends rather than passing it at ;; neither: the comparisons are ordered, deliberately. (= n 7) (print (i64 (/ (f64 0.0) (f64 0.0)))) :else (println "?")) 0))