chiark / gitweb /
src/class-finalize.lisp: Improve reporting of CPL construction errors.
authorMark Wooding <mdw@distorted.org.uk>
Sun, 26 Mar 2017 14:16:18 +0000 (15:16 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Fri, 8 Jun 2018 18:58:28 +0000 (19:58 +0100)
Introduce `report-class-list-merge-error' to report problems properly.

doc/SYMBOLS
doc/meta.tex
src/class-finalize-impl.lisp

index 45369ff07745e5464c80feab83daa44a95136fc7..dcd96ec6222ef9645a0b16ab0d2a8110bd95e1b9 100644 (file)
@@ -198,6 +198,7 @@ class-finalize-impl.lisp
   l*loops-cpl                                   function
   merge-class-lists                             function
   python-cpl                                    function
+  report-class-list-merge-error                 function
 
 class-finalize-proto.lisp
   check-sod-class                               generic
index 07103509c93e928641741e1346281e8a09cbd725..24a7af8f82a8169e1dfe281d846772bc10825091 100644 (file)
 \begin{describe}{gf}{compute-cpl @<class> @> @<list>}
 \end{describe}
 
+\begin{describe}{fun}
+    {report-class-list-merge-error @<class> @<lists> @<error>}
+\end{describe}
+
 \begin{describe}{fun}{merge-class-lists @<class> @<lists> @<pick> @> @<list>}
 \end{describe}
 
index 89337736e136c17d2512f0bc26c968c05f8c3abd..772ad6ff570f0c5b7c58844ec2515b0a57b2d534 100644 (file)
@@ -50,17 +50,65 @@ (cl:in-package #:sod)
 
 ;;; Utilities.
 
+(export 'report-class-list-merge-error)
+(defun report-class-list-merge-error (class lists error)
+  "Report a failure to merge superclasseses.
+
+   Here, CLASS is the class whose class precedence list we're trying to
+   compute; the LISTS are the individual superclass orderings being merged;
+   and ERROR is an `inconsistent-merge-error' describing the problem that was
+   encountered.
+
+   Each of the LISTS is assumed to begin with the class from which the
+   corresponding constraint originates; see `merge-class-lists'."
+
+  (let* ((state (make-inheritance-path-reporter-state class))
+        (candidates (merge-error-candidates error))
+        (focus (remove-duplicates
+                (remove nil
+                        (mapcar (lambda (list)
+                                  (cons (car list)
+                                        (remove-if-not
+                                         (lambda (item)
+                                           (member item candidates))
+                                         list)))
+                                lists)
+                        :key #'cddr)
+                :test #'equal :key #'cdr)))
+
+    (cerror*-with-location class "Ill-formed superclass graph: ~
+                                 can't construct class precedence list ~
+                                 for `~A'"
+                          class)
+    (dolist (offenders focus)
+      (let ((super (car offenders)))
+       (info-with-location super
+                           "~{Class `~A' orders `~A' before ~
+                              ~#[<BUG>~;`~A'~;`~A' and `~A'~:;~
+                                 ~@{`~A', ~#[~;and `~A'~]~}~]~}"
+                           offenders)
+       (report-inheritance-path state super)))))
+
 (export 'merge-class-lists)
 (defun merge-class-lists (class lists pick)
-  "Merge the LISTS of subclasses of CLASS, using PICK to break ties.
+  "Merge the LISTS of superclasses of CLASS, using PICK to break ties.
 
    This is a convenience wrapper around the main `merge-lists' function.
    Given that class linearizations (almost?) always specify a custom
-   tiebreaker function, this isn't a keyword argument."
+   tiebreaker function, this isn't a keyword argument.
+
+   If a merge error occurs, this function translates it into a rather more
+   useful form, and tries to provide helpful notes.
+
+   For error reporting purposes, it's assumed that each of the LISTS begins
+   with the class from which the corresponding constraint originates.  This
+   initial class does double-duty: it is also considered to be part of the
+   list for the purpose of the merge."
+
   (handler-case (merge-lists lists :pick pick)
-    (inconsistent-merge-error ()
-      (error "Failed to compute class precedence list for `~A'"
-            (sod-class-name class)))))
+    (inconsistent-merge-error (error)
+      (report-class-list-merge-error class lists error)
+      (continue error))))
 
 ;;; Tiebreaker functions.