Re: Unportable ways to add non-trivial type constraints.

Stas Boukarev <[email protected]> Mon, 3 Feb 2025 15:43:44 +0300
Newsgroups gmane.lisp.steel-bank.general
Message-ID <CAF63=12oeo84Wqeq7529S1CmtBd=T12OL9k-4crTHjofcggVxQ@mail.gmail.com>
Constraint propagation is changing all the time, it's going to be not
just non-portable but non-portable between different versions. And
possibly in subtle ways where you can't catch it.
So, don't do this.

On Mon, Feb 3, 2025 at 3:40 PM Vasily Postnicov <[email protected]> wrote:
>
> 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


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