flan/test/programs/arith.flan
Joseph Ferano 2e203f64b8 bytes copies, bytes-view aliases, and a dev-session segfault parks
The INSERTIONSORT crash, all three rulings (FIX.org 2026-09-20):

- (bytes s) allocates a writable copy through the allocator surface —
  context or (bytes s a), StorageExhausted with retry, a registry note in
  dev builds (flan_bytes_dup, lowered like vec-new). (bytes-view s) is the
  old zero-cost reinterpret, renamed, read-only by convention; every
  in-repo reader swept over to it. (string b) unchanged.
- String constants were already read-only on both backends at -O0; now
  pinned — bytes-copy.flan rows on LLVM/-O0/--x86, and dies_segv rows
  asserting the write-through-view trap on both backends.
- A dev build installs a SIGSEGV/SIGBUS handler by the same dev-only
  constructor slot that arms the registry: one line naming the address and
  the innermost frame, then the trap-hook park — stopped, not dead, the
  daemon serving. No agent: message and re-raise. Release builds untouched.
  Pinned by trap_park over dev-segv.flan.
2026-09-20 23:12:42 +07:00

82 lines
4.2 KiB
Plaintext

;;;; 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)
;; The narrow versions of the same two failures. They are here because they are
;; the ones the two backends reach by different routes: the overflow test
;; compares against the *narrow* type's most negative value inside a 64-bit
;; register, and the f32 range test is compared in f32 on one backend and in a
;; double on the other. Both routes are supposed to give the same answer and
;; the survey is what says so.
(defvar i32big i32 2147483647)
(defvar m1-32 i32 -1)
(defvar wide f32 1e30)
(defn main [args [string]] i32
(let [n (i32 (bytes->i64 (bytes-view (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))))
;; The same overflow one width down, which is the case the two backends
;; used to disagree about *silently* rather than both dying: this one
;; loaded sign-extended into a 64-bit register, divided there and
;; truncated on the store, answering -2147483648, where the other backend
;; emitted poison. Neither was wrong about anything; they just were not
;; the same program.
(= n 8) (print (/ (- (- (i32 0) i32big) 1) m1-32))
;; And an f32 source, whose range test the two backends reach by
;; different routes on purpose — compared in f32 here and in a widened
;; double there, which agree because every bound is a power of two and is
;; exact in both.
(= n 9) (print (i32 wide))
:else (println "?"))
0))