jourdan@MINOS.INRIA.FR (11/10/89)
Dear GNU Emacs developpers, This is to report a bug in Emacs' byte compiler. I followed all the instructions in the "Bugs" section of Emacs' on-line manual (info), so I am pretty sure this IS a bug. Setting: GNU Emacs 18.55 compiled and running on a Sun-3/60 under SunOS 3.4 ("berkeley-unix", says M-x emacs-version) and X11R3. The problem is reproducible and does not depend on my .emacs file. Is is not linked to any visited file. * HOW TO REPRODUCE THE BUG Go to the *scratch* buffer (or any Lisp Interaction buffer). Make sure the byte-compiler is loaded (M-x load-library bytecomp). Then type (lines to type have a leading % to strip): %(defun truc (z) % ((lambda (x) (+ x x)) (* z z)))^J truc %(truc 2)^J 8 %(symbol-function 'truc)^J (lambda (z) ((lambda (x) (+ x x)) (* z z))) %(byte-compile 'truc) This produces a Lisp error; here is the backtrace: Signalling: (wrong-type-argument symbolp (lambda (x) (+ x x))) byte-code("F \" G= byte-compile-form(((lambda (x) (+ x x)) (* z z))) byte-code("? byte-compile-body((((lambda (x) (+ x x)) (* z z)))) byte-code("AA!" [form byte-compile-body] 2) byte-compile-progn((progn ((lambda (x) (+ x x)) (* z z)))) funcall(byte-compile-progn (progn ((lambda (x) (+ x x)) (* z z)))) byte-code("F \" G= byte-compile-form((progn ((lambda (x) (+ x x)) (* z z)))) byte-code("AAMMMAAAAAN\n O! \n\n@ P\nA! U byte-compile-top-level((progn ((lambda (x) (+ x x)) (* z z)))) byte-code(" ADEA\"FA@; byte-compile-lambda((lambda (z) ((lambda (x) (+ x x)) (* z z)))) byte-code("A! byte-compile(truc) eval-region(858 878 #<buffer *scratch*>) byte-code("BCD E eval-print-last-sexp(nil) call-interactively(eval-print-last-sexp) The same bug occurs in the same conditions when I use the interpreted version of the byte compiler (i.e., after having executed M-x load-file /usr/local/GNU/emacs-18.55/lisp/bytecomp.el). Here is the corresponding backtrace: Signalling: (wrong-type-argument symbolp (lambda (x) (+ x x))) get((lambda (x) (+ x x)) byte-compile) (let ...) (cond ...) byte-compile-form(((lambda (x) (+ x x)) (* z z))) (while ...) (if ...) byte-compile-body((((lambda (x) (+ x x)) (* z z)))) byte-compile-progn((progn ((lambda (x) (+ x x)) (* z z)))) funcall(byte-compile-progn (progn ((lambda (x) (+ x x)) (* z z)))) (if ...) (let ...) (cond ...) byte-compile-form((progn ((lambda (x) (+ x x)) (* z z)))) (let ...) byte-compile-top-level((progn ((lambda (x) (+ x x)) (* z z)))) (list ...computing arguments...) (setq ...) (let* ...) byte-compile-lambda((lambda (z) ((lambda (x) (+ x x)) (* z z)))) (fset ...computing arguments...) (if ...) byte-compile(truc) eval-region(3393 3413 #<buffer *scratch*>) byte-code("BCD E eval-print-last-sexp(nil) call-interactively(eval-print-last-sexp) Of course the same error occurs if you put the definition of "truc" in a file and try to byte-compile this file (this is how I actually stumbled on the bug). * MY COMMENTS ON THE BUG My interpretation of the bug is that the interpreter allows lambda-expressions in function positions, i.e., as the first element of a list to evaluate, as said in the Emacs Lisp manual (section 3.2.2), but that, for some unknown reason (I can program in Lisp but I don't know how to compile Lisp!), the byte compiler crashes on such forms. I suspect that it actually crashes on every list in function position, but I don't know whether it can validly be something else than a lambda-expression (even when interpreted, I mean). This behaviour is, IMHO, *not* acceptable. I could accept that the compiler leaves those forms untouched for the interpreter to process --although they are equivalent to "let" constructs, which the compiler knows how to compile--, but I certainly cannot accept that it crashes or produces incorrect code. * MORE INFORMATION ON HOW I DISCOVERED THE BUG AND A NOT-QUITE-RELATED BUT RELEVANT SUGGESTION I stumbled on the bug while reworking "x-mouse.el". I bind to my mouse-clicks a number of functions which have different effects according to the position of the mouse (in the "body" of the window, in the "right margin" or in the "title bar", a.k.a. mode line). For instance, when I click the left button with no modifiers, I set the point if the mouse is in the window, I put the line under the mouse at the top of the window if the mouse is in the right margin, or I scroll one page up if the mouse is in the bar. This behaviour (and the corresponding code) was stolen from that of Emacs running under Emacstool. All these functions have the same structure: select the window under the mouse, determine in which of the three "parts" (body, margin or bar) it actually is and do something according to this part. So I imagined I could concentrate the common structure into a generic function or a macro, to which I pass the three functions corresponding to what to do in the three parts of the window. First I wrote a function, then I wrote a macro producing a function definition, then I wrote a macro producing the body of a function definition. Here is the latter: (defmacro x-mouse-generic-macro (win-fn marg-fn bar-fn) "Macro to create the body of functions for handling x-mouse button presses. Must be used as: (defun my-function (arg) \"the doc\" (x-mouse-generic-macro win-fn marg-fn bar-fn)) The argument of the function being defun'ed MUST be called \"arg\". \"win-fn\" is the function to call when the pointer is really in a window, with arguments the X and Y relative coordinates of the pointer in the window. \"marg-fn\" is the function to call when the pointer is in the right margin, with argument the Y relative coordinate of the pointer in the margin. \"bar-fn\" is the function to call when the pointer is in the title bar, with argument the X relative coordinate of the pointer in the bar. First the window in which the pointer is is selected. Then, according to the position of the pointer, one of the functions is called, with the corresponding argument(s). The \"functions\" may be function symbols or lambda expressions." (` (let ((relative-coordinate (x-mouse-select arg))) (if relative-coordinate (let ((x (car relative-coordinate)) (y (car (cdr relative-coordinate)))) (if (PhD-x-mouse-in-right-margin x y) ((, marg-fn) y) ((, win-fn) x y))) (x-mouse-select (list (car arg) (1- (car (cdr arg))))) ((, bar-fn) (car arg)))))) and an example of use: (defun PhD-x-mouse-set-point (arg) "Select Emacs window mouse is on, and move point to mouse position. Or if in simulated scroll-bar, scroll up. Or if in simulated right margin, this line to top." (x-mouse-generic-macro (lambda (x y) ; win-fn (move-to-window-line y) (move-to-column (+ x (current-column)))) scroll-up ; marg-fn (lambda (x) ; bar-fn (scroll-up nil)))) When interpreted, this works fine, even with lambda expressions as functions, but the compiler fails to compile that. My suggestion is related to the second trial I made, using a macro producing a defun. I do not show the macro itself but an example of its use: (x-mouse-generic-defun PhD-x-mouse-set-point "Select Emacs window mouse is on, and move point to mouse position. Or if in simulated scroll-bar, scroll up. Or if in simulated right margin, this line to top." (lambda (x y) ; win-fn (move-to-window-line y) (move-to-column (+ x (current-column)))) scroll-up ; marg-fn (lambda (x) ; bar-fn (scroll-up nil))) I find this more elegant than the previous version and it should be strictly equivalent (even more efficient, since the expansion of the macro is done only once at function-definition time rather than at each function invocation), but unfortunately the compiler leaves the uses absolutely untouched and does not compile the resulting defuns. My suggestion is that the compiler look at the first atom of a (top-level) list to compile, check whether it is a macro it knows of and, if so, expands it and recursively compile the result, as it currently does for more deeply nested forms. Briefly said, macro expansion should also occur at top level during compilation. Many thanks in advance. Best regards, Martin Jourdan