X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/sod/blobdiff_plain/dea4d05507e59ab779ed4bb209e05971d87e260c..6e92afa75860a55640efa6f3ba39f9624b41e8a8:/src/class-utilities.lisp diff --git a/src/class-utilities.lisp b/src/class-utilities.lisp index bf02aa6..38cb75e 100644 --- a/src/class-utilities.lisp +++ b/src/class-utilities.lisp @@ -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 --------------------------------------------------