aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2023-10-30 00:59:19 -0400
committerStefan Monnier2023-10-30 00:59:19 -0400
commitbdec2d2d464919572ae948ba8150e014aa649191 (patch)
tree7cc0cfa5c7142b08707b9a8cc01bb401e356dfc8
parent271d8b70f8d772807484454d3369f515fdff350a (diff)
downloademacs-bdec2d2d464919572ae948ba8150e014aa649191.tar.gz
emacs-bdec2d2d464919572ae948ba8150e014aa649191.zip
comp-cstr.el: The type hierarchy is a DAG, not a tree
Adjust the type operations to account for the fact that types can have several parents. * lisp/emacs-lisp/comp-cstr.el (comp--cl-class-hierarchy): Use `cl--class-allparents`. Add FIXME. (comp--direct-supertype): Declare obsolete. (comp--direct-supertypes): New function. (comp--normalize-typeset0): Rewrite to use `comp--direct-supertypes`; adjust to account for the DAG structure; use `cl-set-difference`. (comp--direct-subtypes): Rewrite. (comp--intersection): New function. (comp-supertypes): Rewrite and change return type. (comp-subtype-p): Simplify. (comp-union-typesets): Use `comp-supertypes` instead of iterating over `comp-cstr-ctxt-typeof-types`. * lisp/emacs-lisp/comp.el (comp--native-compile): Don't catch errors if we're debugging. * test/lisp/emacs-lisp/comp-cstr-tests.el: Adjust tests. * lisp/emacs-lisp/cl-macs.el (cl-defstruct): Fix mishap when we evaluate (cl-defstruct cl-structure-object ..) during the compilation of `cl-preloaded.el`. * lisp/emacs-lisp/cl-preloaded.el: Add corresponding assertion.
-rw-r--r--lisp/emacs-lisp/cl-macs.el8
-rw-r--r--lisp/emacs-lisp/cl-preloaded.el3
-rw-r--r--lisp/emacs-lisp/comp-cstr.el156
-rw-r--r--lisp/emacs-lisp/comp.el5
-rw-r--r--test/lisp/emacs-lisp/comp-cstr-tests.el12
5 files changed, 112 insertions, 72 deletions
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el
index 722d561b9f4..a4a241d9c63 100644
--- a/lisp/emacs-lisp/cl-macs.el
+++ b/lisp/emacs-lisp/cl-macs.el
@@ -3092,7 +3092,11 @@ To see the documentation for a defined struct type, use
3092 descs))) 3092 descs)))
3093 (t 3093 (t
3094 (error "Structure option %s unrecognized" opt))))) 3094 (error "Structure option %s unrecognized" opt)))))
3095 (unless (or include-name type) 3095 (unless (or include-name type
3096 ;; Don't create a bogus parent to `cl-structure-object'
3097 ;; while compiling the (cl-defstruct cl-structure-object ..)
3098 ;; in `cl-preloaded.el'.
3099 (eq name cl--struct-default-parent))
3096 (setq include-name cl--struct-default-parent)) 3100 (setq include-name cl--struct-default-parent))
3097 (when include-name (setq include (cl--struct-get-class include-name))) 3101 (when include-name (setq include (cl--struct-get-class include-name)))
3098 (if print-func 3102 (if print-func
@@ -3331,7 +3335,7 @@ To see the documentation for a defined struct type, use
3331;;; Add cl-struct support to pcase 3335;;; Add cl-struct support to pcase
3332 3336
3333;;In use by comp.el 3337;;In use by comp.el
3334(defun cl--struct-all-parents (class) 3338(defun cl--struct-all-parents (class) ;FIXME: Merge with `cl--class-allparents'
3335 (when (cl--struct-class-p class) 3339 (when (cl--struct-class-p class)
3336 (let ((res ()) 3340 (let ((res ())
3337 (classes (list class))) 3341 (classes (list class)))
diff --git a/lisp/emacs-lisp/cl-preloaded.el b/lisp/emacs-lisp/cl-preloaded.el
index 27603ae8626..03068639575 100644
--- a/lisp/emacs-lisp/cl-preloaded.el
+++ b/lisp/emacs-lisp/cl-preloaded.el
@@ -333,6 +333,9 @@ supertypes from the most specific to least specific.")
333 (cl--class-parents class))))) 333 (cl--class-parents class)))))
334 (nreverse parents))) 334 (nreverse parents)))
335 335
336(eval-and-compile
337 (cl-assert (null (cl--class-parents (cl--find-class 'cl-structure-object)))))
338
336;; Make sure functions defined with cl-defsubst can be inlined even in 339;; Make sure functions defined with cl-defsubst can be inlined even in
337;; packages which do not require CL. We don't put an autoload cookie 340;; packages which do not require CL. We don't put an autoload cookie
338;; directly on that function, since those cookies only go to cl-loaddefs. 341;; directly on that function, since those cookies only go to cl-loaddefs.
diff --git a/lisp/emacs-lisp/comp-cstr.el b/lisp/emacs-lisp/comp-cstr.el
index d23304c8874..ee0ae10539d 100644
--- a/lisp/emacs-lisp/comp-cstr.el
+++ b/lisp/emacs-lisp/comp-cstr.el
@@ -89,8 +89,10 @@ Integer values are handled in the `range' slot.")
89 89
90(defun comp--cl-class-hierarchy (x) 90(defun comp--cl-class-hierarchy (x)
91 "Given a class name `x' return its hierarchy." 91 "Given a class name `x' return its hierarchy."
92 `(,@(mapcar #'cl--struct-class-name (cl--struct-all-parents 92 `(,@(cl--class-allparents (cl--struct-get-class x))
93 (cl--struct-get-class x))) 93 ;; FIXME: AFAICT, `comp--all-classes' will also find those struct types
94 ;; which use :type and can thus be either `vector' or `cons' (the latter
95 ;; isn't `atom').
94 atom 96 atom
95 t)) 97 t))
96 98
@@ -267,8 +269,9 @@ Return them as multiple value."
267 (string-lessp (symbol-name x) 269 (string-lessp (symbol-name x)
268 (symbol-name y))) 270 (symbol-name y)))
269 271
270(defun comp--direct-supertype (type) 272(defun comp--direct-supertype (type) ;FIXME: There can be several!
271 "Return the direct supertype of TYPE." 273 "Return the direct supertype of TYPE."
274 (declare (obsolete comp--direct-supertype "30.1"))
272 (cl-loop 275 (cl-loop
273 named outer 276 named outer
274 for i in (comp-cstr-ctxt-typeof-types comp-ctxt) 277 for i in (comp-cstr-ctxt-typeof-types comp-ctxt)
@@ -276,24 +279,50 @@ Return them as multiple value."
276 when (eq j type) 279 when (eq j type)
277 do (cl-return-from outer y)))) 280 do (cl-return-from outer y))))
278 281
282(defun comp--direct-supertypes (type)
283 "Return the direct supertypes of TYPE."
284 (let ((supers (comp-supertypes type)))
285 (cl-assert (eq type (car supers)))
286 (cl-loop
287 with notdirect = nil
288 with direct = nil
289 for parent in (cdr supers)
290 unless (memq parent notdirect)
291 do (progn
292 (push parent direct)
293 (setq notdirect (append notdirect (comp-supertypes parent))))
294 finally return direct)))
295
279(defun comp--normalize-typeset0 (typeset) 296(defun comp--normalize-typeset0 (typeset)
280 ;; For every type search its supertype. If all the subtypes of that 297 ;; For every type search its supertypes. If all the subtypes of a
281 ;; supertype are presents remove all of them, add the identified 298 ;; supertype are presents remove all of them, add the identified
282 ;; supertype and restart. 299 ;; supertype and restart.
300 ;; FIXME: The intention is to return a 100% equivalent but simpler
301 ;; typeset, but this is only the case when the supertype is abstract
302 ;; and "final/closed" (i.e. can't have new subtypes).
283 (when typeset 303 (when typeset
284 (while (eq 'restart 304 (while (eq 'restart
285 (cl-loop 305 (cl-loop
286 named main 306 named main
287 for i in typeset 307 for sup in (cl-remove-duplicates
288 for sup = (comp--direct-supertype i) 308 (apply #'append
309 (mapcar #'comp--direct-supertypes typeset)))
289 for subs = (comp--direct-subtypes sup) 310 for subs = (comp--direct-subtypes sup)
290 when (and sup 311 when (and (length> subs 1) ;;FIXME: Why?
291 (length> subs 1) 312 ;; Every subtype of `sup` is a subtype of
292 (cl-every (lambda (x) (member x typeset)) subs)) 313 ;; some element of `typeset`?
293 do (cl-loop for s in subs 314 ;; It's tempting to just check (member x typeset),
294 do (setq typeset (cl-delete s typeset)) 315 ;; but think of the typeset (marker number),
295 finally (progn (push sup typeset) 316 ;; where `sup' is `integer-or-marker' and `sub'
296 (cl-return-from main 'restart)))))) 317 ;; is `integer'.
318 (cl-every (lambda (sub)
319 (cl-some (lambda (type)
320 (comp-subtype-p sub type))
321 typeset))
322 subs))
323 do (progn
324 (setq typeset (cons sup (cl-set-difference typeset subs)))
325 (cl-return-from main 'restart)))))
297 typeset)) 326 typeset))
298 327
299(defun comp-normalize-typeset (typeset) 328(defun comp-normalize-typeset (typeset)
@@ -303,56 +332,53 @@ Return them as multiple value."
303(defun comp--direct-subtypes (type) 332(defun comp--direct-subtypes (type)
304 "Return all the direct subtypes of TYPE." 333 "Return all the direct subtypes of TYPE."
305 ;; TODO: memoize. 334 ;; TODO: memoize.
306 (cl-sort 335 (let ((subtypes ()))
307 (cl-loop for j in (comp-cstr-ctxt-typeof-types comp-ctxt) 336 (dolist (j (comp-cstr-ctxt-typeof-types comp-ctxt))
308 for res = (cl-loop for i in j 337 (let ((occur (memq type j)))
309 with last = nil 338 (when occur
310 when (eq i type) 339 (while (not (eq j occur))
311 return last 340 (let ((candidate (pop j)))
312 do (setq last i)) 341 (when (and (not (memq candidate subtypes))
313 when res 342 (memq type (comp--direct-supertypes candidate)))
314 collect res) 343 (push candidate subtypes)))))))
315 #'comp--sym-lessp)) 344 (cl-sort subtypes #'comp--sym-lessp)))
345
346(defun comp--intersection (list1 list2)
347 "Like `cl-intersection` but preserves the order of one of its args."
348 (if (equal list1 list2) list1
349 (let ((res nil))
350 (while list2
351 (if (memq (car list2) list1)
352 (push (car list2) res))
353 (pop list2))
354 (nreverse res))))
316 355
317(defun comp-supertypes (type) 356(defun comp-supertypes (type)
318 "Return a list of pairs (supertype . hierarchy-level) for TYPE." 357 "Return the ordered list of supertypes of TYPE."
358 ;; FIXME: We should probably keep the results in
359 ;; `comp-cstr-ctxt-typeof-types' (or maybe even precompute them
360 ;; and maybe turn `comp-cstr-ctxt-typeof-types' into a hash-table).
361 ;; Or maybe we shouldn't keep structs and defclasses in it,
362 ;; and just use `cl--class-allparents' when needed (and refuse to
363 ;; compute their direct subtypes since we can't know them).
319 (cl-loop 364 (cl-loop
320 named outer 365 named loop
321 with found = nil 366 with above
322 for l in (comp-cstr-ctxt-typeof-types comp-ctxt) 367 for lane in (comp-cstr-ctxt-typeof-types comp-ctxt)
323 do (cl-loop 368 do (let ((x (memq type lane)))
324 for x in l 369 (cond
325 for i from (length l) downto 0 370 ((null x) nil)
326 when (eq type x) 371 ((eq x lane) (cl-return-from loop x)) ;A base type: easy case.
327 do (setf found t) 372 (t (setq above
328 when found 373 (if above (comp--intersection x above) x)))))
329 collect `(,x . ,i) into res 374 finally return above))
330 finally (when found
331 (cl-return-from outer res)))))
332
333(defun comp-common-supertype-2 (type1 type2)
334 "Return the first common supertype of TYPE1 TYPE2."
335 (when-let ((types (cl-intersection
336 (comp-supertypes type1)
337 (comp-supertypes type2)
338 :key #'car)))
339 (car (cl-reduce (lambda (x y)
340 (if (> (cdr x) (cdr y)) x y))
341 types))))
342
343(defun comp-common-supertype (&rest types)
344 "Return the first common supertype of TYPES."
345 (or (gethash types (comp-cstr-ctxt-common-supertype-mem comp-ctxt))
346 (puthash types
347 (cl-reduce #'comp-common-supertype-2 types)
348 (comp-cstr-ctxt-common-supertype-mem comp-ctxt))))
349 375
350(defsubst comp-subtype-p (type1 type2) 376(defsubst comp-subtype-p (type1 type2)
351 "Return t if TYPE1 is a subtype of TYPE2 or nil otherwise." 377 "Return t if TYPE1 is a subtype of TYPE2 or nil otherwise."
352 (let ((types (cons type1 type2))) 378 (let ((types (cons type1 type2)))
353 (or (gethash types (comp-cstr-ctxt-subtype-p-mem comp-ctxt)) 379 (or (gethash types (comp-cstr-ctxt-subtype-p-mem comp-ctxt))
354 (puthash types 380 (puthash types
355 (eq (comp-common-supertype-2 type1 type2) type2) 381 (memq type2 (comp-supertypes type1))
356 (comp-cstr-ctxt-subtype-p-mem comp-ctxt))))) 382 (comp-cstr-ctxt-subtype-p-mem comp-ctxt)))))
357 383
358(defun comp-union-typesets (&rest typesets) 384(defun comp-union-typesets (&rest typesets)
@@ -360,16 +386,18 @@ Return them as multiple value."
360 (or (gethash typesets (comp-cstr-ctxt-union-typesets-mem comp-ctxt)) 386 (or (gethash typesets (comp-cstr-ctxt-union-typesets-mem comp-ctxt))
361 (puthash typesets 387 (puthash typesets
362 (cl-loop 388 (cl-loop
363 with types = (apply #'append typesets) 389 ;; List of (TYPE . SUPERTYPES)", ordered from
390 ;; "most general" to "least general"
391 with typess = (sort (mapcar #'comp-supertypes
392 (apply #'append typesets))
393 (lambda (l1 l2)
394 (<= (length l1) (length l2))))
364 with res = '() 395 with res = '()
365 for lane in (comp-cstr-ctxt-typeof-types comp-ctxt) 396 for types in typess
366 do (cl-loop 397 ;; Don't keep this type if it's a subtype of one of
367 with last = nil 398 ;; the other types.
368 for x in lane 399 unless (comp--intersection types res)
369 when (memq x types) 400 do (push (car types) res)
370 do (setf last x)
371 finally (when last
372 (push last res)))
373 finally return (comp-normalize-typeset res)) 401 finally return (comp-normalize-typeset res))
374 (comp-cstr-ctxt-union-typesets-mem comp-ctxt)))) 402 (comp-cstr-ctxt-union-typesets-mem comp-ctxt))))
375 403
@@ -863,7 +891,7 @@ Non memoized version of `comp-cstr-intersection-no-mem'."
863 (comp-subtype-p neg-type pos-type)) 891 (comp-subtype-p neg-type pos-type))
864 do (cl-loop 892 do (cl-loop
865 with found 893 with found
866 for (type . _) in (comp-supertypes neg-type) 894 for type in (comp-supertypes neg-type)
867 when found 895 when found
868 collect type into res 896 collect type into res
869 when (eq type pos-type) 897 when (eq type pos-type)
diff --git a/lisp/emacs-lisp/comp.el b/lisp/emacs-lisp/comp.el
index 181e5ca96a1..bdc59703de9 100644
--- a/lisp/emacs-lisp/comp.el
+++ b/lisp/emacs-lisp/comp.el
@@ -4180,7 +4180,7 @@ the deferred compilation mechanism."
4180 (comp-log "\n \n" 1) 4180 (comp-log "\n \n" 1)
4181 (unwind-protect 4181 (unwind-protect
4182 (progn 4182 (progn
4183 (condition-case err 4183 (condition-case-unless-debug err
4184 (cl-loop 4184 (cl-loop
4185 with report = nil 4185 with report = nil
4186 for t0 = (current-time) 4186 for t0 = (current-time)
@@ -4199,7 +4199,8 @@ the deferred compilation mechanism."
4199 (comp-log (format "Done compiling %s" data) 0) 4199 (comp-log (format "Done compiling %s" data) 0)
4200 (cl-loop for (pass . time) in (reverse report) 4200 (cl-loop for (pass . time) in (reverse report)
4201 do (comp-log (format "Pass %s took: %fs." 4201 do (comp-log (format "Pass %s took: %fs."
4202 pass time) 0)))) 4202 pass time)
4203 0))))
4203 (native-compiler-skip) 4204 (native-compiler-skip)
4204 (t 4205 (t
4205 (let ((err-val (cdr err))) 4206 (let ((err-val (cdr err)))
diff --git a/test/lisp/emacs-lisp/comp-cstr-tests.el b/test/lisp/emacs-lisp/comp-cstr-tests.el
index d2f552af6fa..cbedce0c47d 100644
--- a/test/lisp/emacs-lisp/comp-cstr-tests.el
+++ b/test/lisp/emacs-lisp/comp-cstr-tests.el
@@ -42,14 +42,14 @@
42 ',expected-type-spec)))) 42 ',expected-type-spec))))
43 43
44 (defconst comp-cstr-typespec-tests-alist 44 (defconst comp-cstr-typespec-tests-alist
45 `(;; 1 45 '(;; 1
46 (symbol . symbol) 46 (symbol . symbol)
47 ;; 2 47 ;; 2
48 ((or string array) . array) 48 ((or string array) . array)
49 ;; 3 49 ;; 3
50 ((or symbol number) . (or number symbol)) 50 ((or symbol number) . (or number symbol))
51 ;; 4 51 ;; 4
52 ((or cons atom) . (or atom cons)) ;; SBCL return T 52 ((or cons atom) . t) ;; SBCL return T
53 ;; 5 53 ;; 5
54 ((or integer number) . number) 54 ((or integer number) . number)
55 ;; 6 55 ;; 6
@@ -219,14 +219,18 @@
219 ;; 88 219 ;; 88
220 ((and (or (member a b c)) (not (or (member a b)))) . (member c)) 220 ((and (or (member a b c)) (not (or (member a b)))) . (member c))
221 ;; 89 221 ;; 89
222 ((or cons symbol) . list) 222 ((or cons symbol) . (or list symbol)) ;; FIXME: Why `list'?
223 ;; 90 223 ;; 90
224 ((or string char-table bool-vector vector) . array) 224 ((or string char-table bool-vector vector) . array)
225 ;; 91 225 ;; 91
226 ((or string char-table bool-vector vector number) . (or array number)) 226 ((or string char-table bool-vector vector number) . (or array number))
227 ;; 92 227 ;; 92
228 ((or string char-table bool-vector vector cons symbol number) . 228 ((or string char-table bool-vector vector cons symbol number) .
229 (or number sequence))) 229 (or number sequence symbol))
230 ;; 93?
231 ;; FIXME: I get `cons' rather than `list'?
232 ;;((or null cons) . list)
233 )
230 "Alist type specifier -> expected type specifier.")) 234 "Alist type specifier -> expected type specifier."))
231 235
232(defmacro comp-cstr-synthesize-tests () 236(defmacro comp-cstr-synthesize-tests ()