Subject: Re: bug#8631: since lexical merge, byte compiler does not know defmethod defines a function
From: Stefan Monnier <[email-masked]>
To: Glenn Morris <[email-masked]>
Cc: "Eric M. Ludlam" <[email-masked]>, [email-masked]
Date: Sat, 07 May 2011 01:07:17 -0300
List-Id: <debbugs-submit.debbugs.gnu.org>

> Since the lexical branch was merged (ie r103798), the byte compiler does
> not understand that eieio's defmethod defines a function.

> Eg:

> emacs -Q -batch -f batch-byte-compile cedet/srecode/dictionary.el

> In end of data:
> dictionary.el:712:1:Warning: the following functions are not known to be
>     defined: srecode-dictionary-add-template-table,[...]

Should be fixed now, thanks to the patch below,

        Stefan

=== modified file 'lisp/emacs-lisp/bytecomp.el'
--- lisp/emacs-lisp/bytecomp.el	2011-04-15 12:30:15 +0000
+++ lisp/emacs-lisp/bytecomp.el	2011-05-07 03:24:05 +0000
@@ -4173,6 +4173,7 @@
 
 ;; Compile normally, but deal with warnings for the function being defined.
 (put 'defalias 'byte-hunk-handler 'byte-compile-file-form-defalias)
+;; Used for eieio--defalias as well.
 (defun byte-compile-file-form-defalias (form)
   (if (and (consp (cdr form)) (consp (nth 1 form))
 	   (eq (car (nth 1 form)) 'quote)

=== modified file 'lisp/emacs-lisp/eieio.el'
--- lisp/emacs-lisp/eieio.el	2011-05-05 03:42:09 +0000
+++ lisp/emacs-lisp/eieio.el	2011-05-07 03:56:24 +0000
@@ -420,6 +420,7 @@
     (load-library (car (cdr (symbol-function cname))))))
 
 (defun eieio-defclass (cname superclasses slots options-and-doc)
+  ;; FIXME: Most of this should be moved to the `defclass' macro.
   "Define CNAME as a new subclass of SUPERCLASSES.
 SLOTS are the slots residing in that class definition, and options or
 documentation OPTIONS-AND-DOC is the toplevel documentation for this class.
@@ -1139,6 +1140,17 @@
 
 ;;; CLOS methods and generics
 ;;
+
+(put 'eieio--defalias 'byte-hunk-handler
+     #'byte-compile-file-form-defalias) ;;(get 'defalias 'byte-hunk-handler)
+(defun eieio--defalias (name body)
+  "Like `defalias', but with less side-effects.
+More specifically, it has no side-effects at all when the new function
+definition is the same (`eq') as the old one."
+  (unless (and (fboundp name)
+               (eq (symbol-function name) body))
+    (defalias name body)))
+
 (defmacro defgeneric (method args &optional doc-string)
   "Create a generic function METHOD.
 DOC-STRING is the base documentation for this class.  A generic
@@ -1147,7 +1160,21 @@
 `defgeneric' for you.  With this implementation the ARGS are
 currently ignored.  You can use `defgeneric' to apply specialized
 top level documentation to a method."
-  `(eieio-defgeneric (quote ,method) ,doc-string))
+  `(eieio--defalias ',method
+                    (eieio--defgeneric-init-form ',method ,doc-string)))
+
+(defun eieio--defgeneric-init-form (method doc-string)
+  "Form to use for the initial definition of a generic."
+  (cond
+   ((or (not (fboundp method))
+        (eq 'autoload (car-safe (symbol-function method))))
+    ;; Make sure the method tables are installed.
+    (eieiomt-install method)
+    ;; Construct the actual body of this function.
+    (eieio-defgeneric-form method doc-string))
+   ((generic-p method) (symbol-function method))           ;Leave it as-is.
+   (t (error "You cannot create a generic/method over an existing symbol: %s"
+             method))))
 
 (defun eieio-defgeneric-form (method doc-string)
   "The lambda form that would be used as the function defined on METHOD.
@@ -1237,26 +1264,6 @@
 		  (cdr entry)
 		  ))))
 
-(defun eieio-defgeneric (method doc-string)
-  "Engine part to `defgeneric' macro defining METHOD with DOC-STRING."
-  (if (and (fboundp method) (not (generic-p method))
-	   (or (byte-code-function-p (symbol-function method))
-	       (not (eq 'autoload (car (symbol-function method)))))
-	   )
-      (error "You cannot create a generic/method over an existing symbol: %s"
-	     method))
-  ;; Don't do this over and over.
-  (unless (fboundp 'method)
-    ;; This defun tells emacs where the first definition of this
-    ;; method is defined.
-    `(defun ,method nil)
-    ;; Make sure the method tables are installed.
-    (eieiomt-install method)
-    ;; Apply the actual body of this function.
-    (fset method (eieio-defgeneric-form method doc-string))
-    ;; Return the method
-    'method))
-
 (defun eieio-unbind-method-implementations (method)
   "Make the generic method METHOD have no implementations.
 It will leave the original generic function in place,
@@ -1292,12 +1299,17 @@
   (let* ((key (if (keywordp (car args)) (pop args)))
 	 (params (car args))
 	 (arg1 (car params))
-	 (class (if (consp arg1) (nth 1 arg1))))
-    `(eieio--defmethod ',method ',key ',class
-                       (lambda ,(if (consp arg1)
+         (args (if (consp arg1)
                                (cons (car arg1) (cdr params))
-                             params)
-                         ,@(cdr args)))))
+                 params))
+	 (class (if (consp arg1) (nth 1 arg1)))
+         (code `(lambda ,args ,@(cdr args))))
+    `(progn
+       ;; Make sure there is a generic and the byte-compiler sees it.
+       (defgeneric ,method ,args
+         ,(or (documentation code)
+              (format "Generically created method `%s'." method)))
+       (eieio--defmethod ',method ',key ',class ',code))))
 
 (defun eieio--defmethod (method kind argclass code)
   "Work part of the `defmethod' macro defining METHOD with ARGS."
@@ -1317,11 +1329,11 @@
 		 method-static)
 		;; Primary key
                (t method-primary))))
-    ;; make sure there is a generic
-    (eieio-defgeneric
-     method
-     (or (documentation code)
-         (format "Generically created method `%s'." method)))
+    ;; Make sure there is a generic (when called from defclass).
+    (eieio--defalias
+     method (eieio--defgeneric-init-form
+             method (or (documentation code)
+                        (format "Generically created method `%s'." method))))
     ;; create symbol for property to bind to.  If the first arg is of
     ;; the form (varname vartype) and `vartype' is a class, then
     ;; that class will be the type symbol.  If not, then it will fall