chiark / gitweb /
src/class-*.lisp: Improve metaclass selection.
[sod] / src / class-utilities.lisp
index bf02aa6944f0eb32b00dd1f771144f4e5de2e682..38cb75ef073f422c82fb26ecb2d89e5433da9e02 100644 (file)
@@ -7,7 +7,7 @@
 
 ;;;----- 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
@@ -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)))
-      (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)))
-      (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)
@@ -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))))
 
+;;;--------------------------------------------------------------------------
+;;; 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.
 
@@ -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
-   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."
 
@@ -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
-  ;; 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
@@ -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)
-                   (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)))
@@ -188,6 +274,10 @@ (export 'vtmsgs-struct-tag)
 (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)))
@@ -196,4 +286,8 @@ (export 'vtable-name)
 (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 --------------------------------------------------