Unportable ways to add non-trivial type constraints.

Vasily Postnicov <[email protected]> Mon, 3 Feb 2025 12:37:39 +0000
Newsgroups gmane.lisp.steel-bank.general
Message-ID <CADnZ6B=qEAAX6TreYDNUhQ=aATo-pP_UAsREMPTBNUNtVToSJg@mail.gmail.com>
Hi! Suppose I want to write a specialized eql comparison function which has
a type a -> a -> BOOLEAN (i.e. types of its arguments must be equal).

There is no portable way to specify such a type in Common Lisp. But, in
src/compiler/fun-info.lisp I found this:

  ;; If true, the function can add flow-sensitive type information
  ;; about the state of the world after its execution. The COMBINATION
  ;; node is passed as an argument, along with the current set of
  ;; active constraints for the block.  The function returns a
  ;; sequence of constraints; a constraint is a triplet of a
  ;; constraint kind (a symbol, see (defstruct (constraint ...)) in
  ;; constraint.lisp) and arguments, either LVARs, LAMBDA-VARs, or
  ;; CTYPEs.  If any of these arguments is NIL, the constraint is
  ;; skipped. This simplifies integration with OK-LVAR-LAMBDA-VAR,
  ;; which maps LVARs to LAMBDA-VARs.  An optional fourth value in
  ;; each constraint flips the meaning of the constraint if it is
  ;; non-NIL.
  (constraint-propagate nil :type (or function null))

So I thought this is what I need and tried the following:
(defpackage foo
  (:use #:cl)
  (:export #:foo #:bar #:baz))
(in-package :foo)

(sb-c:defknown (foo) (t t) boolean
    (sb-c:movable sb-c:flushable sb-c:foldable))

(defun foo (x y)
  (eql x y))

(sb-c:defoptimizer (foo sb-c::constraint-propagate) ((x y) node gen)
  (let ((type-x (sb-c::lvar-type x))
        (type-y (sb-c::lvar-type y))
        (var-x  (sb-c::ok-lvar-lambda-var x gen))
        (var-y  (sb-c::ok-lvar-lambda-var y gen)))
    (when (and var-x var-y)
      (print
       (list
        (list 'typep var-x type-y)
        (list 'typep var-y type-x))))))

(declaim (ftype (function (integer string) (values boolean &optional)) bar))
(defun bar (x y)
  (foo x y))

When compiling bar I have this printed:
((TYPEP
  #<SB-C::LAMBDA-VAR
    :%SOURCE-NAME X
    :TYPE #<SB-KERNEL:NUMERIC-TYPE INTEGER>
    :WHERE-FROM :DECLARED {1111651493}>
  #<SB-KERNEL:UNION-TYPE STRING> T)
 (TYPEP
  #<SB-C::LAMBDA-VAR
    :%SOURCE-NAME Y
    :TYPE #<SB-KERNEL:UNION-TYPE STRING>
    :WHERE-FROM :DECLARED {1111651553}>
  #<SB-KERNEL:NUMERIC-TYPE INTEGER> T))
but no compile-time or run-time when I use bar:

CL-USER> (foo:baz 543 "dsd")
NIL

Can you please give me a hint how type information about arguments can be
added to a function? My goal is to learn how I can write a bit type-safer
code and non-portability does not bother me.

Also, if there are any publications about how SBCL handles types, please
tell me.

_______________________________________________
Sbcl-help mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/sbcl-help