master: Do not cons for (when (intersection a b))

stassats via Sbcl-commits <[email protected]> Fri, 15 May 2026 22:24:13 +0000
Newsgroups gmane.lisp.steel-bank.cvs
Message-ID <[email protected]>
The branch "master" has been updated in SBCL:
       via  8007e82231cc8d1f8dfd8baa8591abbf750354ac (commit)
      from  1f8a40c435fc3df7a41500db298c84000e3adce8 (commit)

- Log -----------------------------------------------------------------
commit 8007e82231cc8d1f8dfd8baa8591abbf750354ac
Author: Stas Boukarev <[email protected]>
Date:   Sat May 16 01:20:50 2026 +0300

    Do not cons for (when (intersection a b))
    
    (Except for a possible hash-table).
---
 src/code/list.lisp        | 32 +++++++++++++++++++++++++++++++-
 src/compiler/fndb.lisp    | 10 ++++++++++
 src/compiler/seqtran.lisp | 11 +++++++++++
 3 files changed, 52 insertions(+), 1 deletion(-)

diff --git a/src/code/list.lisp b/src/code/list.lisp
index 4b32f3a8c..442b9b1a7 100644
--- a/src/code/list.lisp
+++ b/src/code/list.lisp
@@ -1129,7 +1129,37 @@
                      (dolist (elt short)
                        (when (funcall member-test elt long key test)
                          (push elt res)))))))
-          res))))))
+          res)))))
+
+;;; A boolean variant
+(defun intersection-p (list1 list2
+                       &key key (test nil testp) (test-not nil notp))
+  (declare (dynamic-extent key test test-not)
+           (explicit-check key test test-not))
+  (when (and testp notp)
+    (error ":TEST and :TEST-NOT were both supplied."))
+  (with-member-test (member-test)
+    (when (and list1 list2)
+      (multiple-value-bind (short long short-length long-nthcdr)
+          (shorter-list-length list1 list2)
+        (let ((hash-table (hashing-p notp testp test short-length long-nthcdr)))
+          (cond (hash-table
+                 (dolist (elt short)
+                   (setf (gethash (apply-key key elt) hash-table) t))
+                 (dolist (elt long)
+                   (when (gethash (apply-key key elt) hash-table)
+                     (return-from intersection-p t))))
+                (t
+                 (flet ((swapped-test (x y)
+                          (funcall (truly-the function test) y x)))
+                   (declare (dynamic-extent #'swapped-test))
+                   (let ((test (if (eq list1 short)
+                                   test
+                                   (and test #'swapped-test))))
+                     (dolist (elt short)
+                       (when (funcall member-test elt long key test)
+                         (return-from intersection-p t)))))))
+          nil))))))
 
 (defun nintersection (list1 list2
                       &key key (test nil testp) (test-not nil notp))
diff --git a/src/compiler/fndb.lisp b/src/compiler/fndb.lisp
index d3748a566..69a7f4be3 100644
--- a/src/compiler/fndb.lisp
+++ b/src/compiler/fndb.lisp
@@ -1233,6 +1233,16 @@
   list
   (foldable flushable call))
 
+(defknown sb-impl::intersection-p
+  (proper-list proper-list &key (:key (function-designator ((or (nth-arg 0 :sequence t)
+                                                  (nth-arg 1 :sequence t)))))
+                  (:test (function-designator ((nth-arg 0 :sequence t :key :key)
+                                               (nth-arg 1 :sequence t :key :key))))
+                  (:test-not (function-designator ((nth-arg 0 :sequence t :key :key)
+                                                   (nth-arg 1 :sequence t :key :key)))))
+  boolean
+  (foldable flushable call))
+
 (defknown (nunion nset-exclusive-or)
   ((modifying list) (modifying list)
    &key (:key (function-designator ((or (nth-arg 0 :sequence t)
diff --git a/src/compiler/seqtran.lisp b/src/compiler/seqtran.lisp
index 7b9e984c3..71faafa76 100644
--- a/src/compiler/seqtran.lisp
+++ b/src/compiler/seqtran.lisp
@@ -3969,6 +3969,17 @@
           (t
            (give-up-ir1-transform)))))
 
+(defoptimizer (intersection rewrite-full-call)
+    ((function sequence &rest args) node)
+  (when (block nil
+          (map-all-dests (lambda (dest lvar nth-value)
+                           (declare (ignore lvar nth-value))
+                           (unless (if-p dest)
+                             (return)))
+                         node)
+          t)
+    'sb-impl::intersection-p))
+
 (deftransform nunion ((list1 list2 &key key test test-not))
   (let ((null-type (specifier-type 'null)))
     (cond ((csubtypep (lvar-type list1) null-type)

-----------------------------------------------------------------------


hooks/post-receive
-- 
SBCL