Extend float infinity handling in MAKE-NUMERIC-TYPE

This commit is contained in:
Jan Moringen 2022-04-15 14:17:35 +02:00
parent d23a2ff60d
commit 088a277579
2 changed files with 106 additions and 62 deletions

View file

@ -2608,24 +2608,83 @@ expansion happened."
(case type
(rational
#+sb-xc-host (return-from coerce-numeric-bound)
#-sb-xc-host (rational thing))
#-sb-xc-host (if (and (floatp thing) (float-infinity-p thing))
(return-from coerce-numeric-bound nil)
(rational thing)))
((float single-float)
(cond #-sb-xc-host
((<= most-negative-single-float thing most-positive-single-float)
(coerce thing 'single-float))
(t
(return-from coerce-numeric-bound))))
(return-from coerce-numeric-bound nil))))
(double-float
(cond #-sb-xc-host
((<= most-negative-double-float thing most-positive-double-float)
(coerce thing 'double-float))
(t
(return-from coerce-numeric-bound)))))))
(return-from coerce-numeric-bound nil)))))))
(when bound
(if (consp bound)
(list (c (car bound)))
(c bound)))))
(defun %make-union-numeric-type (class format complexp low high enumerable)
(declare (type (member integer rational float nil) class))
(macrolet ((unionize (&rest specs)
`(type-union
,@(loop for (class format coerce simple-coerce) in specs
collect `(make-numeric-type
:class ',class
:format ',format
:complexp complexp
:low ,(if simple-coerce
`(coerce low ',coerce)
`(coerce-numeric-bound low ',coerce))
:high ,(if simple-coerce
`(coerce high ',coerce)
`(coerce-numeric-bound high ',coerce))
:enumerable enumerable)))))
(cond ((and (null class) (member complexp '(:real :complex)))
(cond ((not (bounds-unbounded-p low high))
(cond ((and (floatp low) (float-infinity-p low)
(eql low high))
;; low and high are some float
;; infinity. not representable as a
;; rational.
(let ((complexp :real)) ; TODO what if complexp was :complex?
(unionize (float single-float single-float t)
(float double-float double-float t))))
(t
(unionize (rational nil rational)
(float single-float single-float)
(float double-float double-float)))))
((eq complexp :complex)
(specifier-type 'complex))
(t
(specifier-type 'real))))
((and (eq class 'float) (member complexp '(:real :complex))
(eq format nil))
(cond ((not (bounds-unbounded-p low high))
(if (and (floatp low) (float-infinity-p low)
(eql low high))
(let ((complexp :real))
(unionize (float single-float single-float t)
(float double-float double-float t)
#+long-float((error "long-float"))))
(unionize (float single-float single-float)
(float double-float double-float)
#+long-float((error "long-float")))))
((eq complexp :complex)
(specifier-type '(complex float)))
(t
(specifier-type 'float))))
((and (null complexp)
(or class format low high))
(type-union (make-numeric-type :class class :format format :complexp :complex
:low low :high high :enumerable enumerable)
(make-numeric-type :class class :format format :complexp :real
:low low :high high :enumerable enumerable))))))
;;; Impose canonicalization rules for NUMERIC-TYPE. Note that in some
;;; cases, despite the name, we return *EMPTY-TYPE* or a UNION-TYPE instead of a
;;; NUMERIC-TYPE.
@ -2643,50 +2702,9 @@ expansion happened."
(defun make-numeric-type (&key class format (complexp :real) low high
enumerable)
(declare (type (member integer rational float nil) class))
(macrolet ((unionize (types classes formats)
`(let (types)
(loop for thing in ',types
for class in ',classes
for format in ',formats
do
(let ((low (coerce-numeric-bound low thing))
(high (coerce-numeric-bound high thing)))
(push (make-numeric-type
:format format
:class class
:complexp complexp
:low low
:high high
:enumerable enumerable)
types)))
(apply #'type-union types))))
(when (and (null class) (member complexp '(:real :complex)))
(return-from make-numeric-type
(if (bounds-unbounded-p low high)
(if (eq complexp :complex)
(specifier-type 'complex)
(specifier-type 'real))
(unionize (rational single-float double-float)
(rational float float)
(nil single-float double-float)))))
(when (and (eql class 'float) (member complexp '(:complex :real)) (eql format nil))
(return-from make-numeric-type
(if (bounds-unbounded-p low high)
(if (eq complexp :complex)
(specifier-type '(complex float))
(specifier-type 'float))
(unionize (single-float double-float #+long-float (error "long-float"))
(float float)
(single-float double-float)))))
(when (and (null complexp)
(or class format low high))
(return-from make-numeric-type
(type-union (make-numeric-type :class class :format format
:low low :high high :enumerable enumerable
:complexp :complex)
(make-numeric-type :class class :format format
:low low :high high :enumerable enumerable
:complexp :real)))))
(let ((union-type (%make-union-numeric-type
class format complexp low high enumerable)))
(when union-type (return-from make-numeric-type union-type)))
(multiple-value-bind (low high)
(case class
(integer
@ -2710,12 +2728,12 @@ expansion happened."
(or (case class
(float
(macrolet ((float-type (fmt complexp
&aux (spec (if (eq complexp :complex)
`(complex ,fmt) fmt)))
&aux (spec (if (eq complexp :complex)
`(complex ,fmt) fmt)))
`(literal-ctype (interned-numeric-type ',spec
:class 'float :complexp ,complexp
:format ',fmt :enumerable nil)
,spec)))
,spec)))
(when (bounds-unbounded-p low high)
(ecase format
(single-float
@ -2729,10 +2747,10 @@ expansion happened."
(integer
(macrolet ((int-type (low high)
`(literal-ctype
(interned-numeric-type nil
:class 'integer :low ,low :high ,high
:enumerable (if (and ,low ,high) t nil))
(integer ,(or low '*) ,(or high '*)))))
(interned-numeric-type nil
:class 'integer :low ,low :high ,high
:enumerable (if (and ,low ,high) t nil))
(integer ,(or low '*) ,(or high '*)))))
(cond ((neq complexp :real) nil)
((and (eql low 0) (eql high (1- array-dimension-limit)))
(int-type 0 #.(1- array-dimension-limit))) ; INDEX type

View file

@ -670,10 +670,42 @@
(assert (eq (sb-int:info :type :kind s) :primitive))
(assert (eq (sb-int:info :type :kind s) :instance)))))))
(with-test (:name :make-numeric-type)
(with-test (:name (make-numeric-type :smoke))
(assert (eq (make-numeric-type :class 'integer :low '(4) :high '(5))
*empty-type*)))
(with-test (:name (make-numeric-type :union))
(assert (equal (type-specifier (make-numeric-type :low '(-79106810381456307)))
`(or (rational (-79106810381456307))
(single-float (-7.910681e16))
(double-float (-7.91068103814563d16))))))
(with-test (:name (make-numeric-type :infinities))
;; Without class
(assert (equal (type-specifier
(make-numeric-type :low sb-ext:single-float-negative-infinity
:high sb-ext:single-float-negative-infinity))
`(or (single-float ,sb-ext:single-float-negative-infinity
,sb-ext:single-float-negative-infinity)
(double-float ,sb-ext:double-float-negative-infinity
,sb-ext:double-float-negative-infinity))))
(assert (equal (type-specifier
(make-numeric-type :low sb-ext:single-float-negative-infinity))
'real))
;; With FLOAT class
(assert (equal (type-specifier
(make-numeric-type :class 'float
:low sb-ext:single-float-negative-infinity
:high sb-ext:single-float-negative-infinity))
`(or (single-float ,sb-ext:single-float-negative-infinity
,sb-ext:single-float-negative-infinity)
(double-float ,sb-ext:double-float-negative-infinity
,sb-ext:double-float-negative-infinity))))
(assert (equal (type-specifier
(make-numeric-type :class 'float
:low sb-ext:single-float-negative-infinity))
`float)))
(with-test (:name :prettier-union-types :skipped-on (not :sb-unicode))
;; (OR STRING BIGNUM) used to unparse as
;; (OR (VECTOR CHARACTER) BASE-STRING (INTEGER * -4611686018427387905)
@ -734,16 +766,10 @@
(assert (eq (type-of (eval '(sb-kernel:%make-instance 12)))
'sb-kernel:instance)))
(with-test (:name :make-numeric-type-union)
(assert (equal (sb-kernel:type-specifier
(sb-kernel:make-numeric-type :low '(-79106810381456307)))
'(or (double-float (-7.91068103814563d16)) (single-float (-7.910681e16))
(rational (-79106810381456307))))))
(with-test (:name (:cons-union :lp1912863))
(let ((c (cons 2 4)))
(assert (not (typep c '(or (cons (integer 0 8) (integer 5 15))
(cons (integer 3 15) (integer 4 14))))))))
(cons (integer 3 15) (integer 4 14))))))))
(with-test (:name (:rational-union :equivalent-to-t))
(let ((type '(or (integer * -1) (rational -1/2 1/2) (integer 1) (not integer))))