From 3be8c2bfe0ac3b376c2b1c58b2c10df71d3ec1f1 Mon Sep 17 00:00:00 2001
From: Mark Wooding <mdw@distorted.org.uk>
Date: Sun, 18 Oct 2009 00:05:32 +0100
Subject: [PATCH] Another day, another commit.

Main highlight is that we now have output of class instances and direct
methods.  Yet to come are effective methods and entries, and vtables.

Today's work has been surprisingly focussed.  Incidental changes are
few; the most significant is probably trimming trailing whitespace from
the last line of a fragment, which eliminates some ugly blank lines in
the output.
---
 builtin.lisp       |  44 +++++++++-----
 class-defs.lisp    |   2 +-
 class-output.lisp  | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++--
 examples.lisp      |  17 ++++--
 lex.lisp           |  18 ++++--
 module-output.lisp |  27 +++++++++
 sod-backg.tex      | 154 ++++++++++++++++++++++++++++++++++++++++++++++++
 sod.tex            |   2 +-
 8 files changed, 405 insertions(+), 29 deletions(-)
 create mode 100644 sod-backg.tex

diff --git a/builtin.lisp b/builtin.lisp
index 21fa1e3..67c04c1 100644
--- a/builtin.lisp
+++ b/builtin.lisp
@@ -31,18 +31,22 @@
 (defun output-imprint-function (class stream)
   (let ((ilayout (sod-class-ilayout class)))
     (format stream "~&~:
+/* Imprint raw memory with instance structure. */
 static void *~A__imprint(void *p)
 {
   struct ~A *sod__obj = p;
 
-  ~:{sod__obj.~A._vt = &~A;~:^~%  ~}
+  ~:{sod__obj.~A.~A._vt = &~A;~:^~%  ~}
   return (p);
 }~2%"
 	    class
 	    (ilayout-struct-tag class)
 	    (mapcar (lambda (ichain)
-		      (list (sod-class-nickname (ichain-head ichain))
-			    (vtable-name class (ichain-head ichain))))
+		      (let* ((head (ichain-head ichain))
+			     (tail (ichain-tail ichain)))
+			(list (sod-class-nickname head)
+			      (sod-class-nickname tail)
+			      (vtable-name class head))))
 		    (ilayout-ichains ilayout)))))
 
 (defun output-init-function (class stream)
@@ -55,8 +59,9 @@ static void *~A__init(void *p)
 	    class
 	    (ilayout-struct-tag class))
     (dolist (ichain (ilayout-ichains ilayout))
-      (let ((ich (format nil "sod__obj.~A"
-			 (sod-class-nickname (ichain-head ichain)))))
+      (let ((ich (format nil "sod__obj.~A.~A"
+			 (sod-class-nickname (ichain-head ichain))
+			 (sod-class-nickname (ichain-tail ichain)))))
 	(dolist (item (ichain-body ichain))
 	  (etypecase item
 	    (vtable-pointer
@@ -73,11 +78,11 @@ static void *~A__init(void *p)
 		     (ecase (sod-initializer-value-kind init)
 		       (:single
 			(format stream "  ~A = ~A;~%"
-				isl (sod-initializer-value-form slot)))
+				isl (sod-initializer-value-form init)))
 		       (:compound
 			(format stream "  ~A = (~A)~A;~%"
 				isl (sod-slot-type dslot)
-				(sod-initializer-value-form slot)))))))))))))
+				(sod-initializer-value-form init)))))))))))))
     (format stream "~&~:
   return (p);
 }~2%")))
@@ -86,6 +91,7 @@ static void *~A__init(void *p)
   (let ((supers (sod-class-direct-superclasses class)))
     (when supers
       (format stream "~&~:
+/* Direct superclasses. */
 static const SodClass *const ~A__supers[] = {
   ~{~A__class~^,~%  ~}
 };~2%"
@@ -93,6 +99,7 @@ static const SodClass *const ~A__supers[] = {
 
 (defun output-cpl-vector (class stream)
   (format stream "~&~:
+/* Class precedence list. */
 static const SodClass *const ~A__cpl[] = {
   ~{~A__class~^,~%  ~}
 };~2%"
@@ -101,8 +108,9 @@ static const SodClass *const ~A__cpl[] = {
 (defun output-chains-vector (class stream)
   (let ((chains (sod-class-chains class)))
     (format stream "~&~:
+/* Chain structure. */
 ~1@*~:{static const SodClass *const ~A__chain_~A[] = {
-~{  ~A__class~^,~%~}
+  ~{~A__class~^,~%  ~}
 };~:^~2%~}
 
 ~0@*static const struct sod_chain ~A__chains[] = {
@@ -137,6 +145,14 @@ static const SodClass *const ~A__cpl[] = {
    of the information (name, type, and how to initialize them) about these
    slots in one place, so that's what we do here."))
 
+(defclass sod-magic-class-initializer (sod-class-initializer)
+  ((initializer-function :initarg :initializer-function
+			 :type (or symbol function)
+			 :reader sod-initializer-function)
+   (prepare-function  :initarg :prepare-function
+		      :type (or symbol function)
+		      :reader sod-initializer-prepare-function)))
+
 (defmethod shared-initialize :after
     ((slot sod-class-slot) slot-names &key pset)
   (declare (ignore slot-names))
@@ -160,7 +176,7 @@ static const SodClass *const ~A__cpl[] = {
 
 (defmethod compute-effective-slot ((class sod-class) (slot sod-class-slot))
   (make-instance 'sod-class-effective-slot
-		 :slot slot
+		 :class class :slot slot
 		 :initializer-function (sod-slot-initializer-function slot)
 		 :prepare-function (sod-slot-prepare-function slot)
 		 :initializer (find-slot-initializer class slot)))
@@ -188,12 +204,12 @@ static const SodClass *const ~A__cpl[] = {
 		 (format nil "sizeof(struct ~A)"
 			 (ilayout-struct-tag class))))
     ("imprint" ,(c-type (* (fun (* void) ("/*p*/" (* void)))))
-	       :prepare-function 'output-imprint-function
+	       :prepare-function output-imprint-function
 	       :initializer-function
 	       ,(lambda (class)
 		  (format nil "~A__imprint" class)))
     ("init" ,(c-type (* (fun (* void) ("/*p*/" (* void)))))
-	    :prepare-function 'output-init-function
+	    :prepare-function output-init-function
 	    :initializer-function
 	    ,(lambda (class)
 	       (format nil "~A__init" class)))
@@ -204,7 +220,7 @@ static const SodClass *const ~A__cpl[] = {
 		,(lambda (class)
 		   (length (sod-class-direct-superclasses class))))
     ("supers" ,(c-type (* (* (class "SodClass" :const) :const)))
-	      :prepare-function 'output-supers-vector
+	      :prepare-function output-supers-vector
 	      :initializer-function
 	      ,(lambda (class)
 		 (if (sod-class-direct-superclasses class)
@@ -215,7 +231,7 @@ static const SodClass *const ~A__cpl[] = {
 		,(lambda (class)
 		   (length (sod-class-precedence-list class))))
     ("cpl" ,(c-type (* (* (class "SodClass" :const) :const)))
-	   :prepare-function 'output-cpl-vector
+	   :prepare-function output-cpl-vector
 	   :initializer-function
 	   ,(lambda (class)
 	      (format nil "~A__cpl" class)))
@@ -241,7 +257,7 @@ static const SodClass *const ~A__cpl[] = {
 		,(lambda (class)
 		   (length (sod-class-chains class))))
     ("chains" ,(c-type (* (struct "sod_chain" :const)))
-	      :prepare-function 'output-chains-vector
+	      :prepare-function output-chains-vector
 	      :initializer-function
 	      ,(lambda (class)
 		 (format nil "~A__chains" class)))
diff --git a/class-defs.lisp b/class-defs.lisp
index 512505b..59c8716 100644
--- a/class-defs.lisp
+++ b/class-defs.lisp
@@ -228,7 +228,7 @@
   ((slot :initarg :slot :type sod-slot :reader sod-initializer-slot)
    (location :initarg :location :initform (file-location nil)
 	     :type file-location :reader file-location)
-   (class :initarg :class :type sod-class :reader sod-initializer-clas)
+   (class :initarg :class :type sod-class :reader sod-initializer-class)
    (value-kind :initarg :value-kind :type keyword
 	       :reader sod-initializer-value-kind)
    (value-form :initarg :value-form :type c-fragment
diff --git a/class-output.lisp b/class-output.lisp
index dc07665..ee2daf3 100644
--- a/class-output.lisp
+++ b/class-output.lisp
@@ -65,9 +65,6 @@
   (sequence-output (stream sequencer)
 
     :constraint
-    (:typedefs)
-
-    :constraint
     ((:classes :start)
      (class :banner)
      (class :islots :start) (class :islots :slots) (class :islots :end)
@@ -97,7 +94,7 @@
      (let ((metaclass (sod-class-metaclass class))
 	   (metaroot (find-root-metaclass class)))
        (format stream "/* The class object. */~%~
-		       extern struct ~A ~A__classobj;~%~
+		       extern const struct ~A ~A__classobj;~%~
 		       #define ~:*~A__class (&~:*~A__classobj.~A.~A)~2%"
 	       (ilayout-struct-tag metaclass) class
 	       (sod-class-nickname (sod-class-chain-head metaroot))
@@ -220,6 +217,10 @@
        (format stream "  const struct ~A *_vt;~%"
 	       (vtable-struct-tag chain-tail chain-head))))))
 
+(defmethod add-output-hooks progn ((islots islots) reason sequencer)
+  (dolist (slot (islots-slots islots))
+    (add-output-hooks slot reason sequencer)))
+
 (defmethod add-output-hooks progn
     ((islots islots) (reason (eql :h)) sequencer)
   (with-slots (class subclass slots) islots
@@ -344,6 +345,167 @@
 	       (sod-class-nickname target-head))))))
 
 ;;;--------------------------------------------------------------------------
+;;; Implementation output.
+
+(defvar *instance-class*)
+
+(defmethod add-output-hooks progn
+    ((class sod-class) (reason (eql :c)) sequencer)
+  (sequence-output (stream sequencer)
+
+    :constraint
+    ((:classes :start)
+     (class :banner)
+     (class :direct-methods :start) (class :direct-methods :end)
+     (class :effective-methods :start) (class :effective-methods :end)
+     (class :vtables :start) (class :vtables :end)
+     (class :object :prepare) (class :object :start) (class :object :end)
+     (:classes :end))
+
+    ((class :banner)
+     (banner (format nil "Class ~A" class) stream))
+
+    ((class :object :start)
+     (format stream "~
+/* The class object. */
+const struct ~A ~A__classobj = {~%"
+	     (ilayout-struct-tag (sod-class-metaclass class))
+	     class))
+    ((class :object :end)
+     (format stream "};~2%")))
+
+  (let ((*instance-class* class))
+    (add-output-hooks (sod-class-ilayout (sod-class-metaclass class))
+		      'populate-class
+		      sequencer)))
+
+;;;--------------------------------------------------------------------------
+;;; Direct methods.
+
+;; This could well want splitting out into some more elaborate protocol.  We
+;; need a bunch of refactoring anyway.
+
+(defmethod add-output-hooks progn
+    ((method delegating-direct-method) (reason (eql :c)) sequencer)
+  (with-slots (class body) method
+    (unless body
+      (return-from add-output-hooks))
+    (sequence-output (stream sequencer)
+      ((class :direct-method method :start)
+       (format stream "#define CALL_NEXT_METHOD (next_method(~{~A~^, ~}))~%"
+	       (mapcar #'argument-name
+		       (c-function-arguments (sod-method-next-method-type
+					      method)))))
+      ((class :direct-method method :end)
+       (format stream "#undef CALL_NEXT_METHOD~%")))))
+
+(defmethod add-output-hooks progn
+    ((method sod-method) (reason (eql :c)) sequencer)
+  (with-slots (class body) method
+    (unless body
+      (return-from add-output-hooks))
+    (sequence-output (stream sequencer)
+      :constraint ((class :direct-methods :start)
+		   (class :direct-method method :start)
+		   (class :direct-method method :body)
+		   (class :direct-method method :end)
+		   (class :direct-methods :end))
+      ((class :direct-method method :body)
+       (pprint-c-type (sod-method-function-type method)
+		      stream
+		      (sod-method-function-name method))
+       (format stream "~&{~%")
+       (write body :stream stream :pretty nil :escape nil)
+       (format stream "~&}~%"))
+      ((class :direct-method method :end)
+       (terpri stream)))))
+
+;;;--------------------------------------------------------------------------
+;;; Filling in the class object.
+
+(defmethod add-output-hooks progn
+    ((ichain ichain) (reason (eql 'populate-class)) sequencer)
+  (with-slots (class chain-head) ichain
+    (sequence-output (stream sequencer)
+      :constraint ((*instance-class* :object :start)
+		   (*instance-class* :object chain-head :ichain :start)
+		   (*instance-class* :object chain-head :ichain :end)
+		   (*instance-class* :object :end))
+      ((*instance-class* :object chain-head :ichain :start)
+       (format stream "  { { /* ~A ichain */~%"
+	       (sod-class-nickname chain-head)))
+      ((*instance-class* :object chain-head :ichain :end)
+       (format stream "  } },~%")))))
+
+(defmethod add-output-hooks progn
+    ((islots islots) (reason (eql 'populate-class)) sequencer)
+  (with-slots (class) islots
+    (let ((chain-head (sod-class-chain-head class)))
+      (sequence-output (stream sequencer)
+	:constraint ((*instance-class* :object chain-head :ichain :start)
+		     (*instance-class* :object class :slots :start)
+		     (*instance-class* :object class :slots)
+		     (*instance-class* :object class :slots :end)
+		     (*instance-class* :object chain-head :ichain :end))
+	((*instance-class* :object class :slots :start)
+	 (format stream "      { /* Class ~A */~%" class))
+	((*instance-class* :object class :slots :end)
+	 (format stream "      },~%"))))))
+
+(defmethod add-output-hooks progn
+    ((vtptr vtable-pointer) (reason (eql 'populate-class)) sequencer)
+  (with-slots (class chain-head chain-tail) vtptr
+    (sequence-output (stream sequencer)
+      :constraint ((*instance-class* :object chain-head :ichain :start)
+		   (*instance-class* :object chain-head :vtable)
+		   (*instance-class* :object chain-head :ichain :end))
+      ((*instance-class* :object chain-head :vtable)
+       (format stream "      &~A__vtable_~A,~%"
+	       class (sod-class-nickname chain-head))))))
+
+(defgeneric find-class-initializer (slot class)
+  (:method ((slot effective-slot) (class sod-class))
+    (let ((dslot (effective-slot-direct-slot slot)))
+      (or (some (lambda (super)
+		  (find dslot (sod-class-class-initializers super)
+			:test #'sod-initializer-slot))
+		(sod-class-precedence-list class))
+	  (effective-slot-initializer slot)))))
+
+(defgeneric output-class-initializer (slot instance stream)
+  (:method ((slot sod-class-effective-slot) (instance sod-class) stream)
+    (let ((func (effective-slot-initializer-function slot)))
+      (if func
+	  (format stream "        ~A,~%" (funcall func instance))
+	  (call-next-method))))
+  (:method ((slot effective-slot) (instance sod-class) stream)
+    (let ((init (find-class-initializer slot instance)))
+      (ecase (sod-initializer-value-kind init)
+	(:simple (format stream "        ~A,~%"
+			 (sod-initializer-value-form init)))
+	(:compound (format stream "        ~@<{ ~;~A~; },~:>~%"
+			 (sod-initializer-value-form init)))))))
+
+(defmethod add-output-hooks progn ((slot sod-class-effective-slot)
+				   (reason (eql 'populate-class))
+				   sequencer)
+  (let ((instance *instance-class*)
+	(func (effective-slot-prepare-function slot)))
+    (when func
+      (sequence-output (stream sequencer)
+	((instance :object :prepare)
+	 (funcall func instance stream))))))
+
+(defmethod add-output-hooks progn
+    ((slot effective-slot) (reason (eql 'populate-class)) sequencer)
+  (with-slots (class (dslot slot)) slot
+    (let ((instance *instance-class*)
+	  (super (sod-slot-class dslot)))
+      (sequence-output (stream sequencer)
+	((instance :object super :slots)
+	 (output-class-initializer slot instance stream))))))
+
+;;;--------------------------------------------------------------------------
 ;;; Testing.
 
 #+test
diff --git a/examples.lisp b/examples.lisp
index cda6bcf..f91f552 100644
--- a/examples.lisp
+++ b/examples.lisp
@@ -19,7 +19,9 @@
       :nick 'lion
       :link '|Animal|
       (message "bite" (fun void))
-      (method "lion" "bite" (fun void) nil)
+      (method "lion" "bite" (fun void) #{
+	 puts("Munch!");
+       })
       (method "nml" "tickle" (fun void) #{
 	 me->_vt->lion.bite(me);
 	 CALL_NEXT_METHOD;
@@ -28,7 +30,9 @@
     (define-sod-class "Goat" ("Animal")
       :nick 'goat
       (message "butt" (fun void))
-      (method "goat" "butt" (fun void) nil)
+      (method "goat" "butt" (fun void) #{
+	 puts("Whack!");
+       })
       (method "nml" "tickle" (fun void) #{
 	 me->_vt->goat.bite(me);
 	 CALL_NEXT_METHOD;
@@ -37,9 +41,13 @@
     (define-sod-class "Serpent" ("Animal")
       :nick 'serpent
       (message "bite" (fun void))
-      (method "serpent" "bite" (fun void) nil)
+      (method "serpent" "bite" (fun void) #{
+	 puts("Nom!");
+       })
       (message "hiss" (fun void))
-      (method "serpent" "hiss" (fun void) nil)
+      (method "serpent" "hiss" (fun void) #{
+	 puts("Ssss!");
+       })
       (method "nml" "tickle" (fun void) #{
 	 if (me->tickles < 3) me->_vt->hiss(me);
 	 else me->_vt->bite(me);
@@ -57,4 +65,3 @@
 				       (sod-message-name
 					(effective-method-message method)))
 				:test #'string=))))
-
diff --git a/lex.lisp b/lex.lisp
index 2d1c4de..0c0fa65 100644
--- a/lex.lisp
+++ b/lex.lisp
@@ -612,10 +612,20 @@
 	    ;; Something else.  Eat it and continue.
 	    (t (getch)))))
 
-      ;; Return the fragment we've collected.
-      (make-instance 'c-fragment
-		     :location start-floc
-		     :text (get-output-stream-string output)))))
+      (let* ((string (get-output-stream-string output))
+	     (end (position-if (lambda (char)
+				 (or (char= char #\newline)
+				     (not (whitespace-char-p char))))
+			       string
+			       :from-end t))
+	     (trimmed (if end
+			  (subseq string 0 (1+ end))
+			  "")))
+
+	;; Return the fragment we've collected.
+	(make-instance 'c-fragment
+		       :location start-floc
+		       :text trimmed)))))
 
 (defun c-fragment-reader (stream char arg)
   "Reader for C-fragment syntax #{ ... stuff ... }."
diff --git a/module-output.lisp b/module-output.lisp
index 3ec6aee..dedbe97 100644
--- a/module-output.lisp
+++ b/module-output.lisp
@@ -123,4 +123,31 @@
     ((:includes :end)
      (terpri stream))))
 
+;;;--------------------------------------------------------------------------
+;;; Source output.
+
+(defmethod add-output-hooks progn
+    ((module module) (reason (eql :c)) sequencer)
+  (sequence-output (stream sequencer)
+    :constraint (:prologue
+		 (:includes :start) :includes (:includes :end)
+		 (:classes :start) (:classes :end)
+		 :epilogue)
+
+    (:prologue
+     (format stream "~
+/* -*-c-*-
+ *
+ * Implementation file generated by SOD for ~A
+ */~2%"
+	     (namestring (module-name module))))
+
+    (:epilogue
+     (banner "That's all, folks" stream :blank-line-p nil))
+
+    ((:includes :start)
+     (banner "External header files" stream))
+    ((:includes :end)
+     (terpri stream))))
+
 ;;;----- That's all, folks --------------------------------------------------
diff --git a/sod-backg.tex b/sod-backg.tex
new file mode 100644
index 0000000..0f19c7d
--- /dev/null
+++ b/sod-backg.tex
@@ -0,0 +1,154 @@
+%%% -*-latex-*-
+%%%
+%%% Background philosophy
+%%%
+%%% (c) 2009 Straylight/Edgeware
+%%%
+
+%%%----- Licensing notice ---------------------------------------------------
+%%%
+%%% This file is part of the Simple Object Definition system.
+%%%
+%%% SOD is free software; you can redistribute it and/or modify
+%%% it under the terms of the GNU General Public License as published by
+%%% the Free Software Foundation; either version 2 of the License, or
+%%% (at your option) any later version.
+%%%
+%%% SOD is distributed in the hope that it will be useful,
+%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+%%% GNU General Public License for more details.
+%%%
+%%% You should have received a copy of the GNU General Public License
+%%% along with SOD; if not, write to the Free Software Foundation,
+%%% Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+\chapter{Philosophical background}
+
+%%%--------------------------------------------------------------------------
+\section{Superclass linearization}
+
+Before making any decisions about relationships between superclasses, Sod
+\emph{linearizes} them, i.e., imposes a total order consistent with the
+direct-subclass/superclass partial order.
+
+In the vague hope that we don't be completely bogged down in formalism by the
+end of this, let's introduce some notation.  We'll fix some class $z$ and
+consider its set of superclasses $S(z) = \{ a, b, \dots \}$.  We can define a
+relation $c \prec_1 d$ if $c$ is a direct subclass of $d$, and extend it by
+taking the reflexive, transitive closure: $c \preceq d$ if and only if
+\begin{itemize}
+\item $c = d$, or
+\item there exists some class $x$ such that $c \prec_1 x$ and $x \preceq d$.
+\end{itemize}
+This is the `is-subclass-of' relation we've been using so far.\footnote{%
+  In some object systems, notably Flavors, this relation is allowed to fail
+  to be a partial order because of cycles in the class graph.  I haven't
+  given a great deal of thought to how well Sod would cope with a cyclic
+  class graph.} %
+
+The problem comes when we try to resolve inheritance questions.  A class
+should inherit behaviour from its superclasses; but, in a world of multiple
+inheritance, which one do we choose?  We get a simple version of this problem
+when we try to resolve inheritance of slot initializers: only one initializer
+can be inherited.
+
+We start by collecting into a set~$I$ the classes which define an initializer
+for the slot.  If $I$ contains both a class $x$ and one of $x$'s superclasses
+then we should prefer $x$ and consider the superclass to be overridden.  So
+we should confine our attention to \emph{least} classes: a member $x$ of a
+set $I$ is least, with respect to a particular partial order, if $y \preceq
+x$ only when $x = y$.  If there is a single least class in our set the we
+have a winner.  Otherwise we want some way to choose among them.
+
+This is not uncontroversial.  Languages such as \Cplusplus\ refuse to choose
+among least classes; instead, any program in which such a choice must be made
+is simply declared erroneous.
+
+Simply throwing up our hands in horror at this situation is satisfactory when
+we only wanted to pick one `winner', as we do for slot initializers.
+However, method combination is a much more complicated business.  We don't
+want to pick just one winner: we want to order all of the applicable methods
+in some way.  Insisting that there is a clear winner at every step along the
+chain is too much of an imposition.  Instead, we \emph{linearize} the
+classes.
+
+%%%--------------------------------------------------------------------------
+\section{Invariance, covariance, contravariance}
+
+In Sod, at least with regard to the existing method combinations, method
+types are \emph{invariant}.  This is not an accident, and it's not due to
+ignorance.
+
+The \emph{signature} of a function, method or message describes its argument
+and return-value types.  If a method's arguments are an integer and a string,
+and it returns a character, we might write its signature as
+\[ (@|int|, @|string|) \to @|char| \]
+In Sod, a method's arguments have to match its message's arguments precisely,
+and the return type must either be @|void| -- for a dÃ¦mon method -- or again
+match the message's return type.  This is argument and return-type
+\emph{invariance}.
+
+Some object systems allow methods with subtly different signatures to be
+defined on a single message.  In particular, since the idea is that instances
+of a subclass ought to be broadly compatible~(see \xref{sec:phil.lsp}) with
+existing code which expects instances of a superclass, we might be able to
+get away with bending method signatures one way or another to permit this.
+
+\Cplusplus\ permits \emph{return-type covariance}, where a method's return
+type can be a subclass of the return type specified by a less-specific
+method.  Eiffel allows \emph{argument covariance}, where a method's arguments
+can be subclasses of the arguments specified by a less-specific
+method.\footnote{%
+  Attentive readers will note that I ought to be talking about pointers to
+  instances throughout.  I'm trying to limit the weight of the notation.
+  Besides, I prefer data models as found in Lisp and Python where all values
+  are held by reference.} %
+
+Eiffel's argument covariance is unsafe.\footnote{%
+  Argument covariance is correct if you're doing runtime dispatch based on
+  argument types.  Eiffel isn't: it's single dispatch, like Sod is.} %
+Suppose that we have two pairs of classes, $a \prec_1 b$ and $c \prec_1 d$.
+Class $b$ defines a message $m$ with signature $d \to @|int|$; class $a$
+defines a method with signature $c \to @|int|$.  This means that it's wrong
+to send $m$ to an instance $a$ carrying an argument of type $d$.  But of
+course, we can treat an instance of $a$ as if it's an instance of $b$,
+whereupon it appears that we are permitted to pass a~$c$ in our message.  The
+result is a well-known hole in the type system.  Oops.
+
+\Cplusplus's return-type covariance is fine.  Also fine is argument
+\emph{contravariance}.  If $b$ defined its message to have signature $c \to
+@|int|$, and $a$ were to broaden its method to $d \to @|int|$, there'd be no
+problem.  All $c$s are $d$s, so viewing an $a$ as a $b$ does no harm.
+
+All of this fiddling with types is fine as long as method inheritance or
+overriding is an all-or-nothing thing.  But Sod has method combinations,
+where applicable methods are taken from the instance's class and all its
+superclasses and combined.  And this makes everything very messy.
+
+It's possible to sort all of the mess out in the generated effective method
+-- we'd just have to convert the arguments to the types that were expected by
+the direct methods.  This would require expensive run-time conversions of all
+of the non-invariant arguments and return values.  And we'd need some
+complicated rule so that we could choose sensible types for the method
+entries in our vtables.  Something like this:
+\begin{quote} \itshape
+  For each named argument of a message, there must be a unique greatest type
+  among the types given for that argument by the applicable methods; and
+  there must be a unique least type among all of the return types of the
+  applicable methods.
+\end{quote}
+I have visions of people wanting to write special no-effect methods whose
+only purpose is to guide the translator around the class graph properly.
+Let's not.
+
+%% things to talk about:
+%% Liskov substitution principle and why it's mad
+
+%%%----- That's all, folks --------------------------------------------------
+
+%%% Local variables:
+%%% mode: LaTeX
+%%% TeX-master: "sod.tex"
+%%% TeX-PDF-mode: t
+%%% End:
diff --git a/sod.tex b/sod.tex
index 6b8275f..7af3c0f 100644
--- a/sod.tex
+++ b/sod.tex
@@ -932,7 +932,7 @@ the applicable methods are invoked are described fully in
 
 \subsection{Chains and instance layout}
 
-
+\include{sod-backg}
 
 \end{document}
 
-- 
2.11.0

