chiark / gitweb /
src/class-*.lisp: Improve metaclass selection.
[sod] / src / class-utilities.lisp
index bf02aa6944f0eb32b00dd1f771144f4e5de2e682..38cb75ef073f422c82fb26ecb2d89e5433da9e02 100644 (file)
@@ -7,7 +7,7 @@
 
 ;;;----- Licensing notice ---------------------------------------------------
 ;;;
 
 ;;;----- Licensing notice ---------------------------------------------------
 ;;;
-;;; This file is part of the Sensble Object Design, an object system for C.
+;;; This file is part of the Sensible Object Design, an object system for C.
 ;;;
 ;;; SOD is free software; you can redistribute it and/or modify
 ;;; it under the terms of the GNU General Public License as published by
 ;;;
 ;;; SOD is free software; you can redistribute it and/or modify
 ;;; it under the terms of the GNU General Public License as published by
@@ -51,13 +51,13 @@ (flet ((find-thing-by-name (what class list name key)
 
   (defun find-instance-slot-by-name (class super-nick slot-name)
     (let ((super (find-superclass-by-nick class super-nick)))
 
   (defun find-instance-slot-by-name (class super-nick slot-name)
     (let ((super (find-superclass-by-nick class super-nick)))
-      (find-thing-by-name "slot" super (sod-class-slots super)
+      (find-thing-by-name "instance slot" super (sod-class-slots super)
                          slot-name #'sod-slot-name)))
 
   (defun find-class-slot-by-name (class super-nick slot-name)
     (let* ((meta (sod-class-metaclass class))
           (super (find-superclass-by-nick meta super-nick)))
                          slot-name #'sod-slot-name)))
 
   (defun find-class-slot-by-name (class super-nick slot-name)
     (let* ((meta (sod-class-metaclass class))
           (super (find-superclass-by-nick meta super-nick)))
-      (find-thing-by-name "slot" super (sod-class-slots super)
+      (find-thing-by-name "class slot" super (sod-class-slots super)
                          slot-name #'sod-slot-name)))
 
   (defun find-message-by-name (class super-nick message-name)
                          slot-name #'sod-slot-name)))
 
   (defun find-message-by-name (class super-nick message-name)
@@ -65,6 +65,91 @@   (defun find-message-by-name (class super-nick message-name)
       (find-thing-by-name "message" super (sod-class-messages super)
                          message-name #'sod-message-name))))
 
       (find-thing-by-name "message" super (sod-class-messages super)
                          message-name #'sod-message-name))))
 
+;;;--------------------------------------------------------------------------
+;;; Describing class inheritance paths in diagnostics.
+
+(export 'inheritance-path-reporter-state)
+(defclass inheritance-path-reporter-state ()
+  ((%class :type sod-class :initarg :class)
+   (paths :type list :initarg :paths)
+   (seen :type hash-table :initform (make-hash-table))))
+
+(export 'make-inheritance-path-reporter-state)
+(defun make-inheritance-path-reporter-state (class)
+  (make-instance 'inheritance-path-reporter-state :class class))
+
+(export 'report-inheritance-path)
+(defun report-inheritance-path (state super)
+  "Issue informational messages showing how CLASS inherits from SUPER."
+  (with-slots (paths (class %class) include-boundary seen) state
+    (unless (slot-boundp state 'paths)
+      (setf paths (distinguished-point-shortest-paths
+                  class
+                  (lambda (c)
+                    (mapcar (lambda (super) (cons super 1))
+                            (sod-class-direct-superclasses c))))))
+    (dolist (hop (mapcon (lambda (subpath)
+                          (let ((super (car subpath))
+                                (sub (and (cdr subpath)
+                                          (cadr subpath))))
+                            (if (or (not sub) (gethash super seen))
+                                nil
+                                (progn
+                                  (setf (gethash super seen) t)
+                                  (list (cons super sub))))))
+                        (cdr (find super paths :key #'cadr))))
+      (let ((super (car hop))
+           (sub (cdr hop)))
+       (info-with-location sub
+                           "Class `~A' is a direct superclass ~
+                            of `~A', defined here"
+                           super sub)))))
+
+;;;--------------------------------------------------------------------------
+;;; Metaclass inference.
+
+(export 'select-minimal-class-property)
+(defun select-minimal-class-property (supers key order default what
+                                     &key (present (lambda (x)
+                                                     (format nil "`~A'" x)))
+                                          allow-empty)
+  "Return the minimal partially-ordered key from the SUPERS.
+
+   KEY is a function of one argument which returns some interesting property
+   of a class.  The keys are assumed to be partially ordered by ORDER, a
+   function of two arguments which returns non-nil if its first argument
+   precedes its second.  If there is a unique minimal key then return it;
+   otherwise report a useful error and pick some candidate in an arbitrary
+   way; the DEFAULT may be chosen if no better choices are available.  If
+   ALLOW-EMPTY is non-nil, then no error is reported if there are no SUPERS,
+   and the DEFAULT choice is returned immediately.
+
+   In an error message, the keys are described as WHAT, which should be a
+   noun phrase; keys are filtered through PRESENT, a function of one
+   argument, before presentation.
+
+   The function returns two values: the chosen value, and a flag which is
+   non-nil if it was chosen without errors."
+
+  (let ((candidates (partial-order-minima (mapcar key supers) order)))
+    (cond ((and (null candidates) allow-empty)
+          (values default t))
+         ((and candidates (null (cdr candidates)))
+          (values (car candidates) t))
+         (t
+          (cerror* "No obvious choice for implicit ~A: ~
+                    ~{~#[root classes must specify explicitly~:;~
+                         candidates are ~
+                         ~#[~;~A~;~A and ~A~:;~@{~A, ~#[~;and ~A~]~}~]~]~:}"
+                   what (mapcar present candidates))
+          (dolist (candidate candidates)
+            (let ((super (find candidate supers :key key)))
+              (info-with-location super
+                                  "Direct superclass `~A' defined here ~
+                                   has ~A ~A"
+                                  super what (funcall present candidate))))
+          (values (if candidates (car candidates) default) nil)))))
+
 ;;;--------------------------------------------------------------------------
 ;;; Miscellaneous useful functions.
 
 ;;;--------------------------------------------------------------------------
 ;;; Miscellaneous useful functions.
 
@@ -100,7 +185,7 @@ (defun find-root-superclass (class)
 
    The root superclass is the superclass which itself has no direct
    superclasses.  In universes not based on the provided builtin module, the
 
    The root superclass is the superclass which itself has no direct
    superclasses.  In universes not based on the provided builtin module, the
-   root class may not be our beloved SodObject; however, there must be one
+   root class may not be our beloved `SodObject'; however, there must be one
    (otherwise the class graph is cyclic, which should be forbidden), and we
    insist that it be unique."
 
    (otherwise the class graph is cyclic, which should be forbidden), and we
    insist that it be unique."
 
@@ -110,7 +195,7 @@ (defun find-root-superclass (class)
   ;;
   ;; Note!  This function gets called from `check-sod-class' before the
   ;; class's chains have been computed.  Therefore we iterate over the direct
   ;;
   ;; Note!  This function gets called from `check-sod-class' before the
   ;; class's chains have been computed.  Therefore we iterate over the direct
-  ;; superclass's chains rather than the class's own.  This misses a chain
+  ;; superclasses' chains rather than the class's own.  This misses a chain
   ;; only in the case where the class is its own chain head.  There are two
   ;; subcases: if there are no direct superclasses at all, then the class is
   ;; its own root; otherwise, it clearly can't be the root and the omission
   ;; only in the case where the class is its own chain head.  There are two
   ;; subcases: if there are no direct superclasses at all, then the class is
   ;; its own root; otherwise, it clearly can't be the root and the omission
@@ -160,7 +245,8 @@ (defun argument-lists-compatible-p (message-args method-args)
   (and (= (length message-args) (length method-args))
        (every (lambda (message-arg method-arg)
                (if (eq message-arg :ellipsis)
   (and (= (length message-args) (length method-args))
        (every (lambda (message-arg method-arg)
                (if (eq message-arg :ellipsis)
-                   (eq method-arg (c-type va-list))
+                   (c-type-equal-p (argument-type method-arg)
+                                   c-type-va-list)
                    (c-type-equal-p (argument-type message-arg)
                                    (argument-type method-arg))))
              message-args method-args)))
                    (c-type-equal-p (argument-type message-arg)
                                    (argument-type method-arg))))
              message-args method-args)))
@@ -188,6 +274,10 @@ (export 'vtmsgs-struct-tag)
 (defun vtmsgs-struct-tag (class super)
   (format nil "~A__vtmsgs_~A" class (sod-class-nickname super)))
 
 (defun vtmsgs-struct-tag (class super)
   (format nil "~A__vtmsgs_~A" class (sod-class-nickname super)))
 
+(export 'vtable-union-tag)
+(defun vtable-union-tag (class chain-head)
+  (format nil "~A__vtu_~A" class (sod-class-nickname chain-head)))
+
 (export 'vtable-struct-tag)
 (defun vtable-struct-tag (class chain-head)
   (format nil "~A__vt_~A" class (sod-class-nickname chain-head)))
 (export 'vtable-struct-tag)
 (defun vtable-struct-tag (class chain-head)
   (format nil "~A__vt_~A" class (sod-class-nickname chain-head)))
@@ -196,4 +286,8 @@ (export 'vtable-name)
 (defun vtable-name (class chain-head)
   (format nil "~A__vtable_~A" class (sod-class-nickname chain-head)))
 
 (defun vtable-name (class chain-head)
   (format nil "~A__vtable_~A" class (sod-class-nickname chain-head)))
 
+(export 'message-macro-name)
+(defun message-macro-name (class entry)
+  (format nil "~A_~A" class (method-entry-slot-name entry)))
+
 ;;;----- That's all, folks --------------------------------------------------
 ;;;----- That's all, folks --------------------------------------------------