Unchecked / *unchecked-math* arithmetic wraps to signed 64-bit
clojure.core's unchecked-* (and +/-/*/inc/dec under *unchecked-math*) are long
ops that WRAP on overflow; jolt's checked arithmetic is arbitrary-precision and
its unchecked-* were plain non-wrapping (+ x y), diverging from the JVM. Now they
truncate to the low 64 bits as a signed long, matching Clojure:
(unchecked-add 9223372036854775807 1) => -9223372036854775808
(unchecked-multiply 9223372036854775807 …) => 1
- host/chez/seq.ss: jolt-wrap64 + binary jolt-unc{add,sub,mul,inc,dec,neg}2 and
the variadic clojure.core/unchecked-* fns (def-var!'d in natives-seq.ss, where
def-var! is bound). The overlay's plain unchecked-* defns are removed.
- backend lng-ops: unchecked-+/-/* emit the wrapping jolt-unc* helpers (the
raising fx ops can't wrap on Chez's 61-bit fixnums); unchecked-inc/dec too.
- *unchecked-math* is honored: the analyzer reads it (jolt.host/unchecked-math?)
and rewrites +/-/*/inc/dec to their unchecked-* for the rest of a file that
(set!)s it, like the JVM.
- jolt->fx: a ^long value that overflows the 61-bit fixnum range passes through
as an exact integer instead of erroring (a full-width long from wrapping math).
Also adds Long/bitCount / numberOfLeadingZeros / reverse and Math/getExponent /
scalb (test.check's splittable PRNG uses them).
This lets clojure.test.check load and run quick-check on jolt. re-mint (analyzer/
backend/overlay are seed sources). make test green (+6 corpus rows, 0 new
divergences, numeric gate updated), shakesmoke byte-identical.
This commit is contained in:
parent
86dd9650b6
commit
a028cab04f
12 changed files with 588 additions and 517 deletions
|
|
@ -194,20 +194,9 @@
|
|||
(def *' *)
|
||||
(def inc' inc)
|
||||
(def dec' dec)
|
||||
(defn unchecked-add [x y] (+ x y))
|
||||
(defn unchecked-subtract [x y] (- x y))
|
||||
(defn unchecked-multiply [x y] (* x y))
|
||||
(defn unchecked-negate [x] (- x))
|
||||
(defn unchecked-inc [x] (+ x 1))
|
||||
(defn unchecked-dec [x] (- x 1))
|
||||
(def unchecked-add-int unchecked-add)
|
||||
(def unchecked-subtract-int unchecked-subtract)
|
||||
(def unchecked-multiply-int unchecked-multiply)
|
||||
(def unchecked-negate-int unchecked-negate)
|
||||
(def unchecked-inc-int unchecked-inc)
|
||||
(def unchecked-dec-int unchecked-dec)
|
||||
(defn unchecked-divide-int [x y] (quot x y))
|
||||
(defn unchecked-remainder-int [x y] (rem x y))
|
||||
;; unchecked-add / -subtract / -multiply / -negate / -inc / -dec (+ the -int
|
||||
;; variants) and -divide-int / -remainder-int are host-defined (host/chez/seq.ss):
|
||||
;; they WRAP to signed 64 bits like the JVM, which a plain (+ x y) overlay can't do.
|
||||
(defn unchecked-int [x] (int x))
|
||||
(def unchecked-long unchecked-int)
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
form-bigdec? form-bigdec-source
|
||||
form-ns-value? form-ns-value-name
|
||||
form-var-value? form-var-value-ns form-var-value-name
|
||||
unchecked-math?
|
||||
form-macro? form-expand-1 resolve-global
|
||||
form-sym-meta form-coll-meta host-intern! form-syntax-quote-lower
|
||||
record-type? record-ctor-key form-position late-bind?
|
||||
|
|
@ -611,6 +612,18 @@
|
|||
(var-ref (compile-ns ctx) nm)
|
||||
(uncompilable (str "Unable to resolve symbol: " nm " in this context"))))))))
|
||||
|
||||
;; The wrapping unchecked-* name a core arithmetic op rewrites to under
|
||||
;; *unchecked-math*, or nil. n is the full item count (head + args); unary - is a
|
||||
;; negate.
|
||||
(defn- unchecked-arith [hname n]
|
||||
(cond
|
||||
(= hname "+") "unchecked-add"
|
||||
(= hname "*") "unchecked-multiply"
|
||||
(= hname "-") (if (= n 2) "unchecked-negate" "unchecked-subtract")
|
||||
(= hname "inc") "unchecked-inc"
|
||||
(= hname "dec") "unchecked-dec"
|
||||
:else nil))
|
||||
|
||||
(defn- analyze-list [ctx form env]
|
||||
(let [items (vec (form-elements form))]
|
||||
(if (zero? (count items))
|
||||
|
|
@ -626,8 +639,15 @@
|
|||
(= "clojure.core" (form-sym-ns head))
|
||||
(contains? handled (form-sym-name head)))
|
||||
(form-sym-name head)))
|
||||
shadowed (and hname (local? env hname))]
|
||||
shadowed (and hname (local? env hname))
|
||||
;; under *unchecked-math*, a core +/-/*/inc/dec becomes its wrapping
|
||||
;; unchecked-* (computed once; nil when off or not such an op).
|
||||
unm (when (and hname (not shadowed) (unchecked-math?))
|
||||
(unchecked-arith hname (count items)))]
|
||||
(cond
|
||||
;; *unchecked-math* rewrite, before macro/special dispatch (these are
|
||||
;; ordinary core fns). The unchecked-* form re-analyzes normally.
|
||||
unm (analyze ctx (cons (symbol unm) (rest items)) env)
|
||||
;; Canonical order (Clojure/CLJS analyze-seq): macroexpand FIRST, then
|
||||
;; dispatch special forms / interop / invoke. A local shadows the macro.
|
||||
;; A true special form is NOT shadowable by a same-named macro, matching
|
||||
|
|
|
|||
|
|
@ -96,7 +96,9 @@
|
|||
"<" "fl<?" ">" "fl>?" "<=" "fl<=?" ">=" "fl>=?" "=" "fl=?" "==" "fl=?"})
|
||||
(def ^:private lng-ops
|
||||
{"+" "fx+" "-" "fx-" "*" "fx*" "min" "fxmin" "max" "fxmax"
|
||||
"unchecked-add" "fx+" "unchecked-subtract" "fx-" "unchecked-multiply" "fx*"
|
||||
;; unchecked-* WRAP to signed 64 bits (Java long), so they can't use the raising
|
||||
;; fx ops — the backend emits the wrapping jolt-unc* helpers (host/chez/seq.ss).
|
||||
"unchecked-add" "jolt-uncadd2" "unchecked-subtract" "jolt-uncsub2" "unchecked-multiply" "jolt-uncmul2"
|
||||
"quot" "fxquotient" "rem" "fxremainder" "mod" "fxmodulo"
|
||||
"<" "fx<?" ">" "fx>?" "<=" "fx<=?" ">=" "fx>=?" "=" "fx=?" "==" "fx=?"})
|
||||
|
||||
|
|
@ -485,8 +487,11 @@
|
|||
(cond
|
||||
(and (= kind :double) (= nm "inc")) (str "(fl+ " (first args) " 1.0)")
|
||||
(and (= kind :double) (= nm "dec")) (str "(fl- " (first args) " 1.0)")
|
||||
(and (= kind :long) (or (= nm "inc") (= nm "unchecked-inc"))) (str "(fx1+ " (first args) ")")
|
||||
(and (= kind :long) (or (= nm "dec") (= nm "unchecked-dec"))) (str "(fx1- " (first args) ")")
|
||||
(and (= kind :long) (= nm "inc")) (str "(fx1+ " (first args) ")")
|
||||
(and (= kind :long) (= nm "dec")) (str "(fx1- " (first args) ")")
|
||||
;; unchecked-inc/dec wrap (Java long), so the raising fx1+/fx1- can't be used.
|
||||
(and (= kind :long) (= nm "unchecked-inc")) (str "(jolt-uncinc " (first args) ")")
|
||||
(and (= kind :long) (= nm "unchecked-dec")) (str "(jolt-uncdec " (first args) ")")
|
||||
:else
|
||||
(let [op (case kind :double (dbl-ops nm) :long (lng-ops nm) :bigdec (bd-ops nm))]
|
||||
(order-args (fn [as] (str "(" op " " (str/join " " as) ")"))))))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue