diff options
| author | Stefan Monnier | 2023-10-30 00:59:19 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2023-10-30 00:59:19 -0400 |
| commit | bdec2d2d464919572ae948ba8150e014aa649191 (patch) | |
| tree | 7cc0cfa5c7142b08707b9a8cc01bb401e356dfc8 | |
| parent | 271d8b70f8d772807484454d3369f515fdff350a (diff) | |
| download | emacs-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.el | 8 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cl-preloaded.el | 3 | ||||
| -rw-r--r-- | lisp/emacs-lisp/comp-cstr.el | 156 | ||||
| -rw-r--r-- | lisp/emacs-lisp/comp.el | 5 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/comp-cstr-tests.el | 12 |
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 () |