Mercurial > core / lisp/lib/obj/meta/sealed.lisp
changeset 698: |
96958d3eb5b0 |
parent: |
10faf95f90dd
|
author: |
Richard Westhaver <ellis@rwest.io> |
date: |
Fri, 04 Oct 2024 22:04:59 -0400 |
permissions: |
-rw-r--r-- |
description: |
fixes |
1 ;;; obj/meta/sealed.lisp --- Sealed Meta-objects 3 ;; see https://github.com/marcoheisig/sealable-metaobjects 6 (in-package :obj/meta/sealed) 8 (defun starts-with (item) 11 (list (eql (first sequence) item)) 12 (sequence (eql (elt sequence 0) item)) 15 (defun type-specifier-and (&rest type-specifiers) 16 (let ((relevant (remove t type-specifiers))) 17 (cond ((null relevant) t) 18 ((null (cdr relevant)) (first relevant)) 19 (t `(and ,@relevant))))) 21 (defun type-specifier-or (&rest type-specifiers) 22 (let ((relevant (remove nil type-specifiers))) 23 (cond ((null relevant) nil) 24 ((null (cdr relevant)) (first relevant)) 25 (t `(or ,@relevant))))) 27 (defun type-specifier-not (type-specifier) 28 (cond ((eql type-specifier t) nil) 29 ((eql type-specifier nil) t) 30 (t `(not ,type-specifier)))) 32 (defgeneric ensure-specializer (specializer-designator) 33 (:method ((class class)) 35 (:method ((symbol symbol)) 36 (or (find-class symbol nil) 38 (:method ((cons cons)) 39 (if (typep cons '(cons (eql eql) (cons t null))) 40 (intern-eql-specializer (second cons)) 43 (error "~@<~S is not a specializer, or a type designator that ~ 44 can be converted to a specializer.~:@>" 47 (defgeneric specializer-type (specializer) 48 (:method ((class class)) 50 (:method ((eql-specializer eql-specializer)) 51 `(eql ,(eql-specializer-object eql-specializer)))) 53 (defgeneric specializer-prototype (specializer &optional excluded-specializers) 55 "Returns an object that is of the type indicated by SPECIALIZER, but not 56 of any of the types indicated the optionally supplied 57 EXCLUDED-SPECIALIZERS. Returns a secondary value of T if such an object 58 could be determined, and NIL if no such object was found. 61 (specializer-prototype 62 (find-class 'double-float)) 65 (specializer-prototype 66 (find-class 'double-float) 67 (list (intern-eql-specializer 5.0d0))) 70 (specializer-prototype 72 (list (find-class 'rational) (find-class 'float))) 76 (defgeneric specializer-direct-superspecializers (specializer) 77 (:method ((class class)) 78 (class-direct-superclasses class)) 79 (:method ((eql-specializer eql-specializer)) 82 (eql-specializer-object eql-specializer))))) 84 (defgeneric specializer-intersectionp (specializer-1 specializer-2) 85 (:method ((class-1 class) (class-2 class)) 86 (multiple-value-bind (disjointp success) 87 (subtypep `(and ,class-1 ,class-2) nil) 90 (:method ((class class) (eql-specializer eql-specializer)) 91 (typep (eql-specializer-object eql-specializer) class)) 92 (:method ((eql-specializer eql-specializer) (class class)) 93 (typep (eql-specializer-object eql-specializer) class)) 94 (:method ((eql-specializer-1 eql-specializer) (eql-specializer-2 eql-specializer)) 95 (eql (eql-specializer-object eql-specializer-1) 96 (eql-specializer-object eql-specializer-2)))) 98 (defgeneric specializer-subsetp (specializer-1 specializer-2) 99 (:method ((class-1 class) (class-2 class)) 100 (values (subtypep class-1 class-2))) 101 (:method ((class class) (eql-specializer eql-specializer)) 102 (subtypep class (specializer-type eql-specializer))) 103 (:method ((eql-specializer eql-specializer) (class class)) 104 (typep (eql-specializer-object eql-specializer) class)) 105 (:method ((eql-specializer-1 eql-specializer) (eql-specializer-2 eql-specializer)) 106 (eql (eql-specializer-object eql-specializer-1) 107 (eql-specializer-object eql-specializer-2)))) 109 ;;; Working with domains. 111 (defgeneric ensure-domain (domain-designator)) 113 (defgeneric method-domain (method)) 115 (defgeneric domain-specializers (domain)) 117 (defgeneric domain-arity (domain)) 119 (defgeneric domain-equal (domain-1 domain-2)) 121 (defgeneric domain-intersectionp (domain-1 domain-2)) 123 (defgeneric domain-subsetp (domain-1 domain-2)) 125 ;;; Checking for sealability. 127 (defgeneric metaobject-sealable-p (metaobject) 128 (:method ((class class)) (eql class (find-class t))) 129 (:method ((generic-function generic-function)) nil) 130 (:method ((method method)) nil) 131 (:method ((built-in-class built-in-class)) t) 132 (:method ((structure-class structure-class)) t) 133 #+sbcl (:method ((system-class sb-pcl:system-class)) t)) 135 (defgeneric class-sealable-p (class) 136 (:method ((class class)) 137 (metaobject-sealable-p class))) 139 (defgeneric generic-function-sealable-p (generic-function) 140 (:method ((generic-function generic-function)) 141 (metaobject-sealable-p generic-function))) 143 (defgeneric method-sealable-p (method) 144 (:method ((method method)) 145 (metaobject-sealable-p method))) 147 (defgeneric specializer-sealable-p (specializer) 148 (:method ((class class)) 149 (class-sealable-p class)) 150 (:method ((eql-specializer eql-specializer)) 153 (eql-specializer-object eql-specializer))))) 155 ;;; Checking for sealed-ness. 157 (defgeneric metaobject-sealed-p (metaobject) 158 (:method ((class class)) (eql class (find-class t))) 159 (:method ((generic-function generic-function)) nil) 160 (:method ((method method)) nil) 161 (:method ((built-in-class built-in-class)) t) 162 (:method ((structure-class structure-class)) t) 163 #+sbcl (:method ((system-class sb-pcl:system-class)) t)) 165 (defgeneric class-sealed-p (class) 166 (:method ((class class)) 167 (metaobject-sealed-p class))) 169 (defgeneric generic-function-sealed-p (generic-function) 170 (:method ((generic-function generic-function)) 171 (metaobject-sealed-p generic-function))) 173 (defgeneric method-sealed-p (method) 174 (:method ((method method)) 175 (metaobject-sealed-p method))) 177 (defgeneric specializer-sealed-p (specializer) 178 (:method ((class class)) 179 (class-sealed-p class)) 180 (:method ((eql-specializer eql-specializer)) 181 (specializer-sealed-p 183 (eql-specializer-object eql-specializer))))) 185 ;;; Sealing of metaobjects. 187 (defgeneric seal-metaobject (metaobject) 188 ;; Invoke primary methods on SEAL-METAOBJECT at most once. 189 (:method :around ((metaobject t)) 190 (unless (metaobject-sealed-p metaobject) 192 ;; Signal an error if the default primary method is reached. 193 (:method ((metaobject t)) 194 (error "Cannot seal the metaobject ~S." metaobject)) 195 (:method :before ((class class)) 196 ;; Class sealing implies finalization. 197 (unless (class-finalized-p class) 198 (finalize-inheritance class)) 199 ;; A sealed class must have sealed superclasses. 200 (loop for class in (rest (class-precedence-list class)) 201 until (member class *standard-metaobjects*) 202 do (seal-class class)))) 204 (defgeneric seal-class (class) 205 ;; Invoke primary methods on SEAL-CLASS at most once. 206 (:method :around ((class class)) 207 (unless (class-sealed-p class) 209 (:method ((symbol symbol)) 210 (seal-metaobject (find-class symbol))) 211 (:method ((class class)) 212 (seal-metaobject class))) 214 (defgeneric seal-generic-function (generic-function) 215 ;; Invoke primary methods on SEAL-GENERIC-FUNCTION at most once. 216 (:method :around ((generic-function generic-function)) 217 (unless (generic-function-sealed-p generic-function) 219 (:method ((generic-function generic-function)) 220 (seal-metaobject generic-function))) 222 (defgeneric seal-method (method) 223 ;; Invoke primary methods on SEAL-METHOD at most once. 224 (:method :around ((method method)) 225 (unless (method-sealed-p method) 227 (:method ((method method)) 228 (seal-metaobject method))) 230 (defgeneric seal-domain (generic-function domain)) 232 (defgeneric seal-specializer (specializer) 233 (:method ((class class)) 235 (:method ((eql-specializer eql-specializer)) 238 (eql-specializer-object eql-specializer))))) 240 ;;; Method properties 242 (defgeneric method-properties (method) 243 (:method ((method method)) 246 (defgeneric validate-method-property (method method-property) 247 (:method ((method method) (method-property t)) 252 (defgeneric sealed-domains (generic-function) 253 (:method ((generic-function generic-function)) 256 (defgeneric compute-static-call-signatures (generic-function domain)) 258 (defgeneric externalizable-object-p (object) 259 ;; Built-in objects are usually externalizable. 260 (:method ((object t)) 261 (typep (class-of object) 'built-in-class)) 262 ;; Functions are not externalizable by definition. 263 (:method ((function function)) 265 ;; Structure objects may be externalizable even without an appropriate 266 ;; method on MAKE-LOAD-FORM. 267 (:method ((structure-object structure-object)) 268 ;; TODO: Returning T here is a bit bold. Actually we'd have to check 269 ;; whether each slot of the structure has a value that is 272 ;; Standard objects are only externalizable if they have an appropriate 273 ;; method on MAKE-LOAD-FORM. 274 (:method ((standard-object standard-object)) 275 (and (make-load-form standard-object) t))) 279 :initform (required-argument :specializers) 280 :initarg :specializers 281 :reader domain-specializers) 283 :initform (required-argument :arity) 285 :reader domain-arity))) 287 (defmethod print-object ((domain domain) stream) 288 (print-unreadable-object (domain stream :type t) 289 (format stream "~{~S~^ ~}" 290 (mapcar #'specializer-type (domain-specializers domain))))) 292 (defun make-domain (specializers &aux (arity (list-length specializers))) 293 (dolist (specializer specializers) 294 (check-type specializer specializer)) 295 (make-instance 'domain 296 :specializers specializers 299 (defmethod ensure-domain ((domain domain)) 302 (defmethod ensure-domain ((sequence sequence)) 304 (map 'list #'ensure-specializer sequence))) 306 (defmethod method-domain ((method method)) 307 (make-domain (method-specializers method))) 309 (defmethod domain-equal 312 (and (= (domain-arity domain-1) 313 (domain-arity domain-2)) 315 (domain-specializers domain-1) 316 (domain-specializers domain-2)))) 318 (defmethod domain-intersectionp 321 (assert (= (domain-arity domain-1) 322 (domain-arity domain-2))) 323 (every #'specializer-intersectionp 324 (domain-specializers domain-1) 325 (domain-specializers domain-2))) 327 (defmethod domain-subsetp 330 (assert (= (domain-arity domain-1) 331 (domain-arity domain-2))) 332 (every #'specializer-subsetp 333 (domain-specializers domain-1) 334 (domain-specializers domain-2))) 336 (defclass sealable-metaobject-mixin () 337 ((%sealed-p :initform nil :reader metaobject-sealed-p))) 339 (defmethod metaobject-sealable-p ((metaobject sealable-metaobject-mixin)) 342 (defmethod seal-metaobject ((metaobject sealable-metaobject-mixin)) 343 (setf (slot-value metaobject '%sealed-p) t)) 345 ;;; It is an error to change the class of a sealed metaobject. 346 (defmethod change-class :around 347 ((metaobject sealable-metaobject-mixin) new-class &key &allow-other-keys) 348 (declare (ignore new-class)) 349 (if (metaobject-sealed-p metaobject) 350 (error "Attempt to change the class of the sealed metaobject ~S." 354 ;;; It is an error to change any object's class to a sealed metaobject. 355 (defmethod update-instance-for-different-class :around 356 (previous (current sealable-metaobject-mixin) &key &allow-other-keys) 357 (error "Attempt to change the class of ~S to the sealable metaobject ~S." 358 previous (class-of current))) 360 ;;; Attempts to reinitialize a sealed metaobject are silently ignored. 361 (defmethod reinitialize-instance :around 362 ((metaobject sealable-metaobject-mixin) &key &allow-other-keys) 363 (if (metaobject-sealed-p metaobject) 367 ;;; It is an error to change the class of an instance of a sealable 370 (defclass sealable-metaobject-instance (t) 373 (defmethod change-class :around 374 ((instance sealable-metaobject-instance) new-class &key &allow-other-keys) 375 (declare (ignore new-class)) 376 (error "Attempt to change the class of the sealable metaobject instance ~S." 379 (defmethod shared-initialize 380 ((instance sealable-metaobject-mixin) 383 &key direct-superclasses) 384 (unless (every #'class-sealable-p direct-superclasses) 385 (error "~@<The superclasses of a sealable metaobject must be sealable. ~ 386 The superclass ~S violates this restriction.~:@>" 387 (find-if-not #'class-sealable-p direct-superclasses))) 388 (apply #'call-next-method instance slot-names 390 (adjoin (find-class 'sealable-metaobject-instance) direct-superclasses) 393 (defclass sealable-class (sealable-metaobject-mixin class) 396 ;;; There is no portable way to add options to a method. So instead, we 397 ;;; allow programmers to declare METHOD-PROPERTIES. 401 ;;; (defmethod foo (x y) 402 ;;; (declare (method-properties inline)) 405 (declaim (declaration method-properties)) 407 (defclass potentially-sealable-method (sealable-metaobject-mixin method) 409 :initarg .method-properties. 410 :accessor method-properties 413 (defmethod shared-initialize :after 414 ((psm potentially-sealable-method) 415 slot-names &key ((.method-properties. method-properties) '()) &allow-other-keys) 416 (declare (ignore slot-names)) 417 (dolist (method-property method-properties) 418 (unless (validate-method-property psm method-property) 419 (error "~@<~S is not a valid method property for the method ~S.~@:>" 420 method-property psm)))) 422 ;;; Track all properties that have been declared in the body of the method 423 ;;; lambda, and make them accessible as METHOD-PROPERTIES of that method. 424 (defmethod make-method-lambda :around 425 ((gf generic-function) 426 (psm potentially-sealable-method) 429 (declare (ignore environment)) 430 (multiple-value-bind (method-lambda initargs) 434 (list* '.method-properties. 435 (let* ((declare-forms (remove-if-not (starts-with 'declare) lambda)) 436 (declarations (apply #'append (mapcar #'rest declare-forms)))) 437 (reduce #'union (remove-if-not (starts-with 'method-properties) declarations) 442 (defmethod metaobject-sealable-p ((psm potentially-sealable-method)) 443 (every #'specializer-sealed-p (method-specializers psm))) 445 (defmethod seal-metaobject :before ((psm potentially-sealable-method)) 446 (mapcar #'seal-specializer (method-specializers psm))) 448 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 452 (defclass potentially-sealable-standard-method 453 (standard-method potentially-sealable-method) 456 (defclass sealable-generic-function (sealable-metaobject-mixin generic-function) 460 :reader sealed-domains 461 :writer (setf %sealed-domains))) 463 :method-class (find-class 'potentially-sealable-method)) 464 (:metaclass funcallable-standard-class)) 466 ;;; Check that the supplied domain is sane. 467 (defmethod seal-domain 468 ((sgf sealable-generic-function) 470 (seal-domain sgf (ensure-domain domain))) 472 (defmethod seal-domain :around 473 ((sgf sealable-generic-function) 475 ;; Ensure that we don't seal any domain more than once. 476 (unless (find domain (sealed-domains sgf) :test #'domain-equal) 477 (call-next-method sgf domain))) 479 ;;; Ensure that the generic function is sealed, and that the newly sealed 480 ;;; domain is disjoint from other domains. 481 (defmethod seal-domain :before 482 ((sgf sealable-generic-function) 484 ;; Ensure that the length of the domain matches the number of mandatory 485 ;; arguments of the generic function. 486 (unless (= (domain-arity domain) 487 (length (generic-function-argument-precedence-order sgf))) 488 (error "~@<Cannot seal the domain ~S with arity ~R ~ 489 of the generic function ~S with arity ~R.~@:>" 490 (mapcar #'specializer-type (domain-specializers domain)) 491 (domain-arity domain) 492 (generic-function-name sgf) 493 (length (generic-function-argument-precedence-order sgf)))) 494 ;; Attempt to seal the supplied generic function. 495 (seal-generic-function sgf) 496 ;; Ensure that the domain does not intersect any existing sealed domains. 497 (dolist (existing-domain (sealed-domains sgf)) 498 (when (domain-intersectionp domain existing-domain) 499 (error "~@<Cannot seal the domain ~S of the generic function ~S, ~ 500 because it intersects with the existing domain ~S.~@:>" 501 (mapcar #'specializer-type domain) 503 (mapcar #'specializer-type existing-domain))))) 505 ;;; Add a new sealed domain. 506 (defmethod seal-domain 507 ((sgf sealable-generic-function) 509 (dolist (method (generic-function-methods sgf)) 510 (when (domain-intersectionp (method-domain method) domain) 511 (unless (domain-subsetp (method-domain method) domain) 512 (error "~@<The method ~S with specializers ~S is only partially ~ 513 within the sealed domain ~S.~:@>" 515 (mapcar #'specializer-type (method-specializers method)) 516 (mapcar #'specializer-type (domain-specializers domain)))) 517 (seal-method method))) 518 (setf (%sealed-domains sgf) 519 (cons domain (sealed-domains sgf)))) 521 ;;; Skip the call to add-method if the list of specializers is equal to 522 ;;; that of an existing, sealed method. 523 (defmethod add-method :around 524 ((sgf sealable-generic-function) 525 (psm potentially-sealable-method)) 526 (dolist (method (generic-function-methods sgf)) 527 (when (and (method-sealed-p method) 528 (equal (method-specializers psm) 529 (method-specializers method))) 530 (return-from add-method psm))) 533 ;;; Ensure that the method to be added is disjoint from all sealed domains. 534 (defmethod add-method :before 535 ((sgf sealable-generic-function) 536 (psm potentially-sealable-method)) 537 (dolist (domain (sealed-domains sgf)) 538 (when (domain-intersectionp domain (method-domain psm)) 539 (error "~@<Cannot add the method ~S with specializers ~S to ~ 540 the sealed generic function ~S, because it intersects ~ 541 with the existing sealed domain ~S.~:@>" 542 psm (method-specializers psm) 543 sgf (mapcar #'specializer-type (domain-specializers domain)))))) 545 ;;; Ensure that the method to be removed is disjoint from all sealed domains. 546 (defmethod remove-method :before 547 ((sgf sealable-generic-function) 548 (psm potentially-sealable-method)) 549 (dolist (domain (sealed-domains sgf)) 550 (when (domain-intersectionp domain (method-domain psm)) 551 (error "~@<Cannot remove the method ~S with specializers ~S from ~ 552 the sealed generic function ~S, because it intersects ~ 553 with the existing sealed domain ~S.~:@>" 554 psm (method-specializers psm) 555 sgf (mapcar #'specializer-type (domain-specializers domain)))))) 557 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 561 (defclass sealable-standard-generic-function 562 (standard-generic-function sealable-generic-function) 565 :method-class (find-class 'potentially-sealable-standard-method)) 566 (:metaclass funcallable-standard-class)) 568 ;;; Finding a suitable prototype for eql specializers is easy. 569 (defmethod specializer-prototype ((eql-specializer eql-specializer) 570 &optional excluded-specializers) 571 (if (member eql-specializer excluded-specializers) 573 (values (eql-specializer-object eql-specializer) t))) 575 (defun eql-specializer-p (object) 576 (typep object 'eql-specializer)) 578 (defmethod specializer-prototype ((class class) &optional excluded-specializers) 579 (let* ((excluded-non-eql-specializers (remove-if #'eql-specializer-p excluded-specializers)) 580 (excluded-eql-specializers (remove-if-not #'eql-specializer-p excluded-specializers)) 581 (excluded-objects (mapcar #'eql-specializer-object excluded-eql-specializers)) 582 (excluded-types (mapcar #'specializer-type excluded-non-eql-specializers))) 583 (map-class-prototypes 585 ;; The prototype must not be a member of the excluded objects. 586 (when (not (member prototype excluded-objects)) 587 ;; The prototype must not be of one of the excluded types. 589 (lambda (excluded-type) 590 (typep prototype excluded-type)) 592 (return-from specializer-prototype (values prototype t))))) 596 ;;; The difficult part is to find suitable prototypes for specializers that 597 ;;; are classes. Ideally, we want several prototypes for each class, such 598 ;;; that we can avoid collisions with excluded specializers. Our technique 599 ;;; is to find prototypes from two sources - the value returned by the MOP 600 ;;; function CLASS-PROTOTYPE, and manually curated lists of prototypes for 601 ;;; each class, which we store in the hash table *CLASS-PROTOTYPES*. 603 (defvar *class-prototypes* (make-hash-table :test #'eq)) 605 (defun map-class-prototypes (function class) 606 (let ((visited-classes (make-hash-table :test #'eq))) 607 (labels ((visit-class (class) 608 (unless (gethash class visited-classes) 609 (setf (gethash class visited-classes) t) 610 (loop for prototype in (gethash class *class-prototypes* '()) do 611 (funcall function prototype)) 612 (mapc #'visit-class (class-direct-subclasses class)) 613 ;; CLASS-PROTOTYPE is difficult to handle... 614 (when (class-finalized-p class) 615 (let ((prototype (class-prototype class))) 616 ;; Surprisingly, some implementations don't always 617 ;; return a CLASS-PROTOTYPE that is an instance of the 618 ;; given class. So we only scan the prototype if it is 620 (when (typep prototype class) 621 (funcall function prototype))))))) 622 (visit-class class)))) 624 (defun register-class-prototype (prototype) 625 (pushnew prototype (gethash (class-of prototype) *class-prototypes* '()) 628 ;; Register list prototypes. 629 (register-class-prototype '(.prototype.)) 630 (register-class-prototype nil) 632 (defparameter *array-element-types* 634 (mapcar #'upgraded-array-element-type 635 (append '(short-float single-float double-float long-float base-char character t) 636 '((complex short-float) 637 (complex single-float) 638 (complex double-float) 639 (complex long-float)) 640 (loop for bits from 1 to 64 641 collect `(unsigned-byte ,bits) 642 collect `(signed-byte ,bits)))) 645 (defun array-initial-element (element-type) 646 (cond ((subtypep element-type 'number) 647 (coerce 0 element-type)) 648 ((subtypep element-type 'character) 649 (coerce #\0 element-type)) 652 ;; Register vector and array prototypes. 653 (loop for adjustable in '(nil t) do 654 (loop for fill-pointer in '(nil t) do 655 (loop for dimensions in '(() (2) (2 2)) do 656 (loop for element-type in *array-element-types* do 657 (let ((storage-vector 658 (make-array (reduce #'* dimensions) 659 :element-type element-type 660 :initial-element (array-initial-element element-type)))) 661 (register-class-prototype 662 (make-array dimensions 663 :adjustable adjustable 664 :fill-pointer (and (= 1 (length dimensions)) fill-pointer) 665 :element-type element-type 666 :displaced-to storage-vector)) 667 (register-class-prototype 668 (make-array dimensions 669 :adjustable adjustable 670 :fill-pointer (and (= 1 (length dimensions)) fill-pointer) 671 :element-type element-type 672 :initial-element (array-initial-element element-type)))))))) 674 ;; Register integer and rational prototypes. 675 (loop for integer in '(19 1337 1338 91676) do 676 (register-class-prototype (+ integer)) 677 (register-class-prototype (- integer))) 678 (loop for bits = 1 then (* bits 2) until (>= bits 512) 679 for value = (expt 2 bits) do 680 (loop for value in (list (1+ value) value (1- value)) do 681 (register-class-prototype value) 682 (register-class-prototype (- value)) 683 (register-class-prototype (/ value 17)))) 685 ;; Register float and complex float prototypes. 686 (register-class-prototype pi) 687 (register-class-prototype (- pi)) 688 (register-class-prototype (exp 1S0)) 689 (register-class-prototype (exp 1F0)) 690 (register-class-prototype (exp 1D0)) 691 (register-class-prototype (exp 1L0)) 692 (mapcar #'register-class-prototype 693 (list most-positive-short-float 694 most-positive-single-float 695 most-positive-double-float 696 most-positive-long-float 697 most-negative-short-float 698 most-negative-single-float 699 most-negative-double-float 700 most-positive-long-float 705 short-float-negative-epsilon 706 single-float-negative-epsilon 707 double-float-negative-epsilon 708 long-float-negative-epsilon)) 709 (loop for base in '(-0.7L0 -0.1L0 -0.0L0 +0.0L0 +0.1L0 +0.7L0) do 710 (loop for fp-type in '(short-float single-float double-float long-float) do 711 (loop for exponent in '(1 2 3 5 7 23 99) do 712 (let ((float (scale-float (coerce base fp-type) exponent))) 713 (register-class-prototype float) 714 (register-class-prototype (complex (float 0 float) float)))))) 716 ;; Register character prototypes. 717 (loop for char across "The quick brown fox jumps over the lazy dog." do 718 (register-class-prototype (char-downcase char)) 719 (register-class-prototype (char-upcase char))) 720 (loop for char across "0123456789!$\"'(),_-./:;?+<=>#%&*@[\\]{\|}`^~" do 721 (register-class-prototype char)) 722 (loop for char in '(#\backspace #\tab #\newline #\linefeed #\page #\return #\space #\rubout) do 723 (register-class-prototype char)) 725 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 727 ;;; Reasoning About Specializer Specificity 730 (;; The specializer of an snode. 731 (%specializer :initarg :specializer :accessor snode-specializer) 732 ;; A (possibly empty) list of snodes for each child class or eql specializer. 733 (%children :initform '() :accessor snode-children) 734 ;; A list of snodes with one entry for each parent class. 735 (%parents :initform '() :accessor snode-parents) 736 ;; Whether the snode has already been visited. 737 (%visitedp :initform nil :accessor snode-visitedp) 738 ;; Whether the snode corresponds to a specializer of an existing method 740 (%relevantp :initform nil :accessor snode-relevantp))) 742 (defun snode-type (snode) 744 (specializer-type (snode-specializer snode)) 746 (apply #'type-specifier-or 747 (loop for subspecializer in (snode-children snode) 750 (snode-specializer subspecializer))))))) 752 (defun snode-prototype (snode) 753 (specializer-prototype 754 (snode-specializer snode) 755 (mapcar #'snode-specializer (snode-children snode)))) 757 (defvar *snode-table*) 759 (defun specializer-snode (specializer) 760 (multiple-value-bind (snode present-p) 761 (gethash specializer *snode-table*) 764 (let ((snode (make-instance 'snode :specializer specializer))) 765 (setf (gethash specializer *snode-table*) snode) 768 (defun snode-add-edge (super-snode sub-snode) 769 (pushnew super-snode (snode-parents sub-snode)) 770 (pushnew sub-snode (snode-children super-snode)) 773 (defun type-prototype-pairs (specializers domain) 774 (let* ((*snode-table* (make-hash-table)) 775 (specializer-snodes (mapcar #'specializer-snode specializers)) 776 (domain-snode (specializer-snode domain))) 777 ;; Initialize domain and specializer snodes. 778 (dolist (snode specializer-snodes) 779 (setf (snode-relevantp snode) t)) 780 (setf (snode-relevantp domain-snode) t) 781 ;; Now connect all snodes. 782 (labels ((visit (current relevant) 783 (unless (snode-visitedp current) 784 (setf (snode-visitedp current) t) 785 (unless (eql current domain) 787 (specializer-direct-superspecializers 788 (snode-specializer current))) 789 (let ((super (specializer-snode specializer))) 790 (cond ((snode-relevantp super) 791 (snode-add-edge super relevant) 794 (visit super relevant))))))))) 795 (mapc #'visit specializer-snodes specializer-snodes)) 796 ;; Finally, build all pairs. 798 (loop for snode being the hash-values of *snode-table* do 799 (when (snode-relevantp snode) 800 (multiple-value-bind (prototype prototype-p) 801 (snode-prototype snode) 803 (push (list (snode-type snode) prototype) 807 ;;; In this file, we compute the static call signatures of a given, sealed 808 ;;; generic function. A static call signature consists of a list of types, 809 ;;; and a list of prototypes. The list of types is guaranteed to be 810 ;;; non-overlapping with the types of any other call signature. The list 811 ;;; of prototypes is chosen such that the list of applicable methods of 812 ;;; these prototypes is representative for all arguments of the types of 813 ;;; the call signature. 815 (defclass static-call-signature () 818 :reader static-call-signature-types) 821 :reader static-call-signature-prototypes))) 823 (defmethod print-object ((scs static-call-signature) stream) 824 (print-unreadable-object (scs stream :type t :identity t) 825 (format stream "~S ~S" 826 (static-call-signature-types scs) 827 (static-call-signature-prototypes scs)))) 829 (defmethod make-load-form 830 ((static-call-signature static-call-signature) &optional environment) 831 (make-load-form-saving-slots 832 static-call-signature 833 :slot-names '(%types %prototypes) 834 :environment environment)) 836 (defmethod externalizable-object-p 837 ((static-call-signature static-call-signature)) 839 (every #'externalizable-object-p 840 (static-call-signature-types static-call-signature)) 841 (every #'externalizable-object-p 842 (static-call-signature-prototypes static-call-signature)))) 844 (defmethod compute-static-call-signatures 845 ((sgf sealable-generic-function) 847 (let* ((sealed-methods 850 (domain-intersectionp (method-domain method) domain)) 851 (generic-function-methods sgf))) 852 (list-of-specializers 853 (mapcar #'method-specializers sealed-methods)) 854 (static-call-signatures '())) 855 (unless (null list-of-specializers) 856 (map-types-and-prototypes 857 (lambda (types prototypes) 858 (push (make-instance 'static-call-signature 860 :prototypes prototypes) 861 static-call-signatures)) 862 ;; Transpose the list of specializers so that we operate on each 863 ;; argument instead of on each method. 864 (apply #'mapcar #'list list-of-specializers) 866 static-call-signatures)) 868 (defun map-types-and-prototypes (fn specializers-list domain) 869 (assert (= (length specializers-list) 870 (domain-arity domain))) 871 (labels ((rec (sl specializers types prototypes) 873 (funcall fn (reverse types) (reverse prototypes)) 874 (loop for (type prototype) 875 in (type-prototype-pairs 877 (first specializers)) 881 (cons prototype prototypes)))))) 882 (rec specializers-list (domain-specializers domain) '() '())))