fix source information for functions from EVAL
authorNikodemus Siivola <nikodemus@random-state.net>
Thu, 17 May 2012 12:22:22 +0000 (15:22 +0300)
committerNikodemus Siivola <nikodemus@random-state.net>
Mon, 21 May 2012 05:41:25 +0000 (08:41 +0300)
 * Removed the breathtaking (NAMED-LAMBDA (EVAL (DEFUN FOO)) ...) hack, which
   caused inconsistent source locations, and broke the built-in debugger
   source command for evaluated functions.

   Replace it with *SOURCE-FORM-CONTEXT-ALIST*, which allows the simple
   evaluator to communicate the original context to the compiler without
   messing with the function source.

   This also means we no longer have to wrap named-lambdas and lambdas in
   another lambda, but can instead compile them directly -- which in turn
   allows FUNCTION-LAMBDA-EXPRESSION to work correctly for definitions from
   EVAL and LOAD.

 * Additionally, use a handler to muffle any compiler notes from EVAL instead
   of using a declaration: those can leak to the user via F-L-E.

 * Change ACTUALLY-COMPILE to return a function signaling an error instead of
   returning NIL when compilation fails fatally. Doing this in
   ACTUALLY-COMPILE allows us to rely on COMPILE-IN-LEXENV always returning a
   function, and gives easy access to a better error message.

 * Properly associate COMPILER-ERRORS with their SIGNAL-ERROR restart.

 * Adjust debug.impure.lisp to be less dependent on the details of
   %SIMPLE-EVAL.

 * Test cases. Pay special attention to TEST-DEBUGGER in debug.impure.lisp.

 * New docstring for COMPILE. Added a sneaky teaser about COMPILE being able
   to recompile things, which is new starting to look feasible.

NEWS
src/code/eval.lisp
src/compiler/compiler-error.lisp
src/compiler/debug-dump.lisp
src/compiler/ir1report.lisp
src/compiler/target-main.lisp
tests/debug.impure.lisp
tests/eval.impure.lisp

diff --git a/NEWS b/NEWS
index 9513f4f..04059a8 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,8 @@ changes relative to sbcl-1.0.57:
     typechecks when dependency graph had loops. (lp#1001799)
   * bug fix: error forms reported with some program-errors were not escaped
     properly.
+  * bug fix: functions from EVAL are now on more equal footing with functions
+    from COMPILE. (lp#1000783, lp#851170, lp#922408)
 
 changes in sbcl-1.0.57 relative to sbcl-1.0.56:
   * RANDOM enhancements and bug fixes:
index dc74c2c..1839520 100644 (file)
 (defvar *eval-tlf-index* nil)
 (defvar *eval-source-info* nil)
 
+;;;; Turns EXPR into a lambda-form we can pass to COMPILE. Returns
+;;;; a secondary value of T if we must call the resulting function
+;;;; to evaluate EXPR -- if EXPR is already a lambda form, there's
+;;;; no need.
 (defun make-eval-lambda (expr)
-  `(named-lambda
-       ;; This name is used to communicate the original context
-       ;; for the compiler, and identifies the lambda for use of
-       ;; EVAL-LAMBDA-SOURCE-LAMBDA below.
-       (eval ,(sb!c::source-form-context *eval-source-context*)) ()
-     (declare (muffle-conditions compiler-note))
-     ;; why PROGN?  So that attempts to eval free declarations
-     ;; signal errors rather than return NIL.  -- CSR, 2007-05-01
-     (progn ,expr)))
-
-(defun eval-lambda-p (form)
-  (when (and (consp form) (eq 'named-lambda (first form)))
-    (let ((name (second form)))
-      (when (and (consp name) (eq 'eval (first name)))
-        t))))
-
-(defun eval-lambda-source-lambda (eval-lambda)
-  (if (eval-lambda-p eval-lambda)
-      (destructuring-bind (named-lambda name lambda-list decl (progn expr))
-          eval-lambda
-        (declare (ignore named-lambda name lambda-list decl progn))
-        (when (and (consp expr) (member (car expr) '(lambda named-lambda)))
-          expr))
-      eval-lambda))
+  (if (typep expr `(cons (member lambda named-lambda lambda-with-lexenv)))
+      (values expr nil)
+      (values `(lambda ()
+                 ;; why PROGN? So that attempts to eval free declarations
+                 ;; signal errors rather than return NIL. -- CSR, 2007-05-01
+                 (progn ,expr))
+              t)))
 
 ;;; general case of EVAL (except in that it can't handle toplevel
 ;;; EVAL-WHEN magic properly): Delegate to #'COMPILE.
 (defun %simple-eval (expr lexenv)
-  ;; FIXME: It might be nice to quieten the toplevel by muffling
-  ;; warnings generated by this compilation (since we're about to
-  ;; execute the results irrespective of the warnings).  We might want
-  ;; to be careful about not muffling warnings arising from inner
-  ;; evaluations/compilations, though [e.g. the ignored variable in
-  ;; (DEFUN FOO (X) 1)].  -- CSR, 2003-05-13
-  ;;
-  ;; As of 1.0.21.6 we muffle compiler notes lexically here, which seems
-  ;; always safe. --NS
-  (let* ((lambda (make-eval-lambda expr))
-         (fun (sb!c:compile-in-lexenv
-               nil lambda lexenv *eval-source-info* *eval-tlf-index*)))
-    (funcall fun)))
+  (multiple-value-bind (lambda call) (make-eval-lambda expr)
+    (let ((fun
+            ;; This tells the compiler where the lambda comes from, in case it
+            ;; wants to report any problems.
+            (let ((sb!c::*source-form-context-alist*
+                    (acons lambda *eval-source-context*
+                           sb!c::*source-form-context-alist*)))
+              (handler-bind (;; Compiler notes just clutter up the REPL:
+                             ;; anyone caring about performance should not
+                             ;; be using EVAL.
+                             (compiler-note #'muffle-warning))
+                (sb!c:compile-in-lexenv
+                 nil lambda lexenv *eval-source-info* *eval-tlf-index* (not call))))))
+      (declare (function fun))
+      (if call
+          (funcall fun)
+          fun))))
 
 ;;; Handle PROGN and implicit PROGN.
 (defun simple-eval-progn-body (progn-body lexenv)
index 4316041..ac0a366 100644 (file)
   (let ((condition (coerce-to-condition datum arguments
                                         'simple-program-error 'compiler-error)))
     (restart-case
-        (progn
-          (cerror "Replace form with call to ERROR."
-                  'compiler-error
-                  :condition condition)
-          (funcall *compiler-error-bailout* condition)
-          (bug "Control returned from *COMPILER-ERROR-BAILOUT*."))
+        (cerror "Replace form with call to ERROR."
+                'compiler-error
+                :condition condition)
       (signal-error ()
-        (error condition)))))
+        (error condition)))
+    (funcall *compiler-error-bailout* condition)
+    (bug "Control returned from *COMPILER-ERROR-BAILOUT*.")))
 
 (defun compiler-warn (datum &rest arguments)
   (apply #'warn datum arguments)
index 2b3d604..f6db341 100644 (file)
 
      :form (let ((direct-file-info (source-info-file-info info)))
              (when (eq :lisp (file-info-name direct-file-info))
-               (let ((form (elt (file-info-forms direct-file-info) 0)))
-                 ;; The form COMPILE saves may include gunk
-                 ;; from %SIMPLE-EVAL -- this gets rid of that.
-                 (sb!impl::eval-lambda-source-lambda form))))
+               (elt (file-info-forms direct-file-info) 0)))
      :function function)))
 
 ;;; Given an arbitrary sequence, coerce it to an unsigned vector if
index a87cd22..d5864d6 100644 (file)
       (second name)
       `(named-lambda ,name)))
 
+(defvar *source-form-context-alist* nil)
+
 ;;; Return the first two elements of FORM if FORM is a list. Take the
 ;;; CAR of the second form if appropriate.
 (defun source-form-context (form)
-  (cond ((atom form) nil)
-        ((>= (length form) 2)
-         (let* ((context-fun-default (lambda (x)
-                                       (declare (ignore x))
-                                       (list (first form) (second form))))
-                (context-fun (gethash (first form)
-                                      *source-context-methods*
-                                      context-fun-default)))
-           (declare (type function context-fun))
-           (funcall context-fun (rest form))))
-        (t
-         form)))
+  (flet ((get-it (form)
+           (cond ((atom form) nil)
+                 ((>= (length form) 2)
+                  (let* ((context-fun-default
+                           (lambda (x)
+                             (declare (ignore x))
+                             (list (first form) (second form))))
+                         (context-fun
+                           (gethash (first form)
+                                    *source-context-methods*
+                                    context-fun-default)))
+                    (declare (type function context-fun))
+                    (funcall context-fun (rest form))))
+                 (t
+                  form))))
+    (get-it (or (cdr (assoc form *source-form-context-alist* :test #'eq))
+                form))))
 
 ;;; Given a source path, return the original source form and a
 ;;; description of the interesting aspects of the context in which it
index 6614c67..46ec5eb 100644 (file)
         definition)))
 
 ;;; Handle the nontrivial case of CL:COMPILE.
-(defun actually-compile (name definition *lexenv* source-info tlf)
+;;;
+;;; If ERRORP is true signals an error immediately -- otherwise returns
+;;; a function that will signal the error.
+(defun actually-compile (name definition *lexenv* source-info tlf errorp)
   (let ((source-paths (when source-info *source-paths*)))
     (with-compilation-values
      (sb!xc:with-compilation-unit ()
        ;; macro, or perhaps both merged into one of the existing utility
        ;; macros SB-C::WITH-COMPILATION-VALUES or
        ;; CL:WITH-COMPILATION-UNIT.
-       (let* ((tlf (or tlf 0))
-              ;; If we have a source-info from LOAD, we will
-              ;; also have a source-paths already set up -- so drop
-              ;; the ones from WITH-COMPILATION-VALUES.
-              (*source-paths* (or source-paths *source-paths*))
-              ;; FIXME: Do we need the *INFO-ENVIRONMENT* rebinding
-              ;; here? It's a literal translation of the old CMU CL
-              ;; rebinding to (OR *BACKEND-INFO-ENVIRONMENT*
-              ;; *INFO-ENVIRONMENT*), and it's not obvious whether the
-              ;; rebinding to itself is needed now that SBCL doesn't
-              ;; need *BACKEND-INFO-ENVIRONMENT*.
-              (*info-environment* *info-environment*)
-              (form (get-lambda-to-compile definition))
-              (*source-info* (or source-info
-                                 (make-lisp-source-info
-                                  form :parent *source-info*)))
-              (*toplevel-lambdas* ())
-              (*block-compile* nil)
-              (*allow-instrumenting* nil)
-              (*code-coverage-records* nil)
-              (*code-coverage-blocks* nil)
-              (*compiler-error-bailout*
-               (lambda (&optional error)
-                 (declare (ignore error))
-                 (compiler-mumble
-                  "~2&fatal error, aborting compilation~%")
-                 (return-from actually-compile (values nil t nil))))
-              (*current-path* nil)
-              (*last-source-context* nil)
-              (*last-original-source* nil)
-              (*last-source-form* nil)
-              (*last-format-string* nil)
-              (*last-format-args* nil)
-              (*last-message-count* 0)
-              (*last-error-context* nil)
-              (*gensym-counter* 0)
-              ;; KLUDGE: This rebinding of policy is necessary so that
-              ;; forms such as LOCALLY at the REPL actually extend the
-              ;; compilation policy correctly.  However, there is an
-              ;; invariant that is potentially violated: future
-              ;; refactoring must not allow this to be done in the file
-              ;; compiler.  At the moment we're clearly alright, as we
-              ;; call %COMPILE with a core-object, not a fasl-stream,
-              ;; but caveat future maintainers. -- CSR, 2002-10-27
-              (*policy* (lexenv-policy *lexenv*))
-              ;; see above
-              (*handled-conditions* (lexenv-handled-conditions *lexenv*))
-              ;; ditto
-              (*disabled-package-locks* (lexenv-disabled-package-locks *lexenv*))
-              ;; FIXME: ANSI doesn't say anything about CL:COMPILE
-              ;; interacting with these variables, so we shouldn't. As
-              ;; of SBCL 0.6.7, COMPILE-FILE controls its verbosity by
-              ;; binding these variables, so as a quick hack we do so
-              ;; too. But a proper implementation would have verbosity
-              ;; controlled by function arguments and lexical variables.
-              (*compile-verbose* nil)
-              (*compile-print* nil))
-         (handler-bind (((satisfies handle-condition-p) #'handle-condition-handler))
-           (clear-stuff)
-           (unless source-paths
-             (find-source-paths form tlf))
-           (%compile form (make-core-object)
-                     :name name
-                     :path `(original-source-start 0 ,tlf))))))))
+       (prog* ((tlf (or tlf 0))
+               ;; If we have a source-info from LOAD, we will
+               ;; also have a source-paths already set up -- so drop
+               ;; the ones from WITH-COMPILATION-VALUES.
+               (*source-paths* (or source-paths *source-paths*))
+               ;; FIXME: Do we need the *INFO-ENVIRONMENT* rebinding
+               ;; here? It's a literal translation of the old CMU CL
+               ;; rebinding to (OR *BACKEND-INFO-ENVIRONMENT*
+               ;; *INFO-ENVIRONMENT*), and it's not obvious whether the
+               ;; rebinding to itself is needed now that SBCL doesn't
+               ;; need *BACKEND-INFO-ENVIRONMENT*.
+               (*info-environment* *info-environment*)
+               (form (get-lambda-to-compile definition))
+               (*source-info* (or source-info
+                               (make-lisp-source-info
+                                form :parent *source-info*)))
+               (*toplevel-lambdas* ())
+               (*block-compile* nil)
+               (*allow-instrumenting* nil)
+               (*code-coverage-records* nil)
+               (*code-coverage-blocks* nil)
+               (*current-path* nil)
+               (*last-source-context* nil)
+               (*last-original-source* nil)
+               (*last-source-form* nil)
+               (*last-format-string* nil)
+               (*last-format-args* nil)
+               (*last-message-count* 0)
+               (*last-error-context* nil)
+               (*gensym-counter* 0)
+               ;; KLUDGE: This rebinding of policy is necessary so that
+               ;; forms such as LOCALLY at the REPL actually extend the
+               ;; compilation policy correctly.  However, there is an
+               ;; invariant that is potentially violated: future
+               ;; refactoring must not allow this to be done in the file
+               ;; compiler.  At the moment we're clearly alright, as we
+               ;; call %COMPILE with a core-object, not a fasl-stream,
+               ;; but caveat future maintainers. -- CSR, 2002-10-27
+               (*policy* (lexenv-policy *lexenv*))
+               ;; see above
+               (*handled-conditions* (lexenv-handled-conditions *lexenv*))
+               ;; ditto
+               (*disabled-package-locks* (lexenv-disabled-package-locks *lexenv*))
+               ;; FIXME: ANSI doesn't say anything about CL:COMPILE
+               ;; interacting with these variables, so we shouldn't. As
+               ;; of SBCL 0.6.7, COMPILE-FILE controls its verbosity by
+               ;; binding these variables, so as a quick hack we do so
+               ;; too. But a proper implementation would have verbosity
+               ;; controlled by function arguments and lexical variables.
+               (*compile-verbose* nil)
+               (*compile-print* nil)
+               (oops nil))
+          (handler-bind (((satisfies handle-condition-p) #'handle-condition-handler))
+            (clear-stuff)
+            (unless source-paths
+              (find-source-paths form tlf))
+            (let ((*compiler-error-bailout*
+                    (lambda (e)
+                      (setf oops e)
+                      ;; Unwind the compiler frames: users want the know where
+                      ;; the error came from, not how the compiler got there.
+                      (go :error))))
+              (return (%compile form (make-core-object)
+                                :name name
+                                :path `(original-source-start 0 ,tlf)))))
+        :error
+          ;; Either signal the error right away, or return a function that
+          ;; will signal the corresponding COMPILED-PROGRAM-ERROR. This is so
+          ;; that we retain our earlier behaviour when called with erronous
+          ;; lambdas via %SIMPLE-EVAL. We could legally do just either one
+          ;; always, but right now keeping the old behaviour seems like less
+          ;; painful option: compiler.pure.lisp is full of tests that make all
+          ;; sort of assumptions about when which things are signalled. FIXME,
+          ;; probably.
+          (if errorp
+              (error oops)
+              (let ((message (princ-to-string oops))
+                    (source (source-to-string form)))
+                (return
+                  (lambda (&rest arguments)
+                    (declare (ignore arguments))
+                    (error 'compiled-program-error
+                           :message message
+                           :source source))))))))))
 
 (defun compile-in-lexenv (name definition lexenv
-                          &optional source-info tlf)
+                          &optional source-info tlf errorp)
   (multiple-value-bind (compiled-definition warnings-p failure-p)
       (cond
         #!+sb-eval
         ((sb!eval:interpreted-function-p definition)
          (multiple-value-bind (definition lexenv)
              (sb!eval:prepare-for-compile definition)
-           (actually-compile name definition lexenv source-info tlf)))
+           (actually-compile name definition lexenv source-info tlf errorp)))
         ((compiled-function-p definition)
          (values definition nil nil))
         (t
-         (actually-compile name definition lexenv source-info tlf)))
+         (actually-compile name definition lexenv source-info tlf errorp)))
+    (check-type compiled-definition compiled-function)
     (cond (name
            (if (and (symbolp name)
                     (macro-function name))
 (defun compile (name &optional (definition (or (macro-function name)
                                                (fdefinition name))))
   #!+sb-doc
-  "Coerce DEFINITION (by default, the function whose name is NAME)
-  to a compiled function, returning (VALUES THING WARNINGS-P FAILURE-P),
-  where if NAME is NIL, THING is the result of compilation, and
-  otherwise THING is NAME. When NAME is not NIL, the compiled function
-  is also set into (MACRO-FUNCTION NAME) if NAME names a macro, or into
-  (FDEFINITION NAME) otherwise."
-  (multiple-value-bind (function warnings-p failure-p)
-      (compile-in-lexenv name definition (make-null-lexenv))
-    (values (or function
-                name
-                (lambda (&rest arguments)
-                  (error 'simple-program-error
-                         :format-control
-                         "Called function compiled with errors. Original ~
-                          definition:~%  ~S~@[~%Arguments:~% ~{ ~S~}~]"
-                         :format-arguments (list definition arguments))))
-            warnings-p
-            failure-p)))
+  "Produce a compiled function from DEFINITION. If DEFINITION is a
+lambda-expression, it is coerced to a function. If DEFINITION is an
+interpreted function, it is compiled. If DEFINITION is already a compiled
+function, it is used as-is. (Future versions of SBCL might try to
+recompile the existing definition, but this is not currently supported.)
+
+If NAME is NIL, the compiled function is returned as the primary value.
+Otherwise the resulting compiled function replaces existing function
+definition of NAME, and NAME is returned as primary value; if NAME is a symbol
+tha names a macro, its macro function is replaced and NAME is returned as
+primary value.
+
+Also returns secondary value which is true if any conditions of type WARNING
+occur during the compilation, and NIL otherwise.
+
+Tertiary value is true if any conditions of type ERROR, or WARNING that are
+not STYLE-WARNINGs occur during compilation, and NIL otherwise.
+"
+  (compile-in-lexenv name definition (make-null-lexenv)))
index 52c106c..b902b74 100644 (file)
                             '((oops ? ? ? ? ? ?)))))
 
 (defmacro defbt (n ll &body body)
-  `(progn
-     ;; normal debug info
-     (defun ,(intern (format nil "BT.~A.1" n)) ,ll
-       ,@body)
-     ;; no arguments saved
-     (defun ,(intern (format nil "BT.~A.2" n)) ,ll
-       (declare (optimize (debug 1) (speed 3)))
-       ,@body)
-     ;; no lambda-list saved
-     (defun ,(intern (format nil "BT.~A.3" n)) ,ll
-       (declare (optimize (debug 0)))
-       ,@body)))
+  ;; WTF is this? This is a way to make these tests not depend so much on the
+  ;; details of LOAD/EVAL. Around 1.0.57 we changed %SIMPLE-EVAL to be
+  ;; slightly smarter, which meant that things which used to have xeps
+  ;; suddently had tl-xeps, etc. This takes care of that.
+  `(funcall
+    (compile nil
+             '(lambda ()
+               (progn
+                 ;; normal debug info
+                 (defun ,(intern (format nil "BT.~A.1" n)) ,ll
+                   ,@body)
+                 ;; no arguments saved
+                 (defun ,(intern (format nil "BT.~A.2" n)) ,ll
+                   (declare (optimize (debug 1) (speed 3)))
+                   ,@body)
+                 ;; no lambda-list saved
+                 (defun ,(intern (format nil "BT.~A.3" n)) ,ll
+                   (declare (optimize (debug 0)))
+                   ,@body))))))
 
 (defbt 1 (&key key)
   (list key))
          (vector-data-address (sb-sys:sap-int (sb-kernel::vector-sap memory)))
          (object-base-address (logandc2 (+ vector-data-address sb-vm:lowtag-mask) sb-vm:lowtag-mask))
          (object-tagged-address (+ object-base-address sb-vm:list-pointer-lowtag)))
-    (multiple-value-bind
-          (object valid-p)
+    (multiple-value-bind (object valid-p)
         (sb-kernel:make-lisp-obj object-tagged-address nil)
+      (declare (ignore object))
       (assert (not valid-p)))))
 
+(defun test-debugger (control form &rest targets)
+  (let ((out (make-string-output-stream))
+        (oops t))
+    (unwind-protect
+         (progn
+           (with-simple-restart (debugger-test-done! "Debugger Test Done!")
+             (let* ((*debug-io* (make-two-way-stream
+                                 (make-string-input-stream control)
+                                 (make-broadcast-stream out #+nil *standard-output*)))
+                    ;; Initial announcement goes to *ERROR-OUTPUT*
+                    (*error-output* *debug-io*)
+                    (*invoke-debugger-hook* nil))
+               (handler-bind ((error #'invoke-debugger))
+                 (eval form))))
+           (setf oops nil))
+      (when oops
+        (error "Uncontrolled unwind from debugger test.")))
+    ;; For sanity's sake this is outside the *debug-io* rebinding -- otherwise
+    ;; it could swallow our asserts!
+    (with-input-from-string (s (get-output-stream-string out))
+      (loop for line = (read-line s nil)
+            while line
+            do (assert targets)
+               #+nil
+               (format *error-output* "Got: ~A~%" line)
+               (let ((match (pop targets)))
+                 (if (eq '* match)
+                     ;; Whatever, till the next line matches.
+                     (let ((text (pop targets)))
+                       (unless (search text line)
+                         (push text targets)
+                         (push match targets)))
+                     (unless (search match line)
+                       (format *error-output* "~&Wanted: ~S~%   Got: ~S~%" match line)
+                       (setf oops t))))))
+    ;; Check that we saw everything we wanted
+    (when targets
+      (error "Missed: ~S" targets))
+    (assert (not oops))))
+
+(with-test (:name (:debugger :source 1))
+  (test-debugger
+   "d
+    source 0
+    debugger-test-done!"
+   `(progn
+      (defun this-will-break (x)
+               (declare (optimize debug))
+               (let* ((y (- x x))
+                      (z (/ x y)))
+                 (+ x z)))
+      (this-will-break 1))
+   '*
+   "debugger invoked"
+   '*
+   "DIVISION-BY-ZERO"
+   "operands (1 0)"
+   '*
+   "INTEGER-/-INTEGER"
+   "(THIS-WILL-BREAK 1)"
+   "1]"
+   "(/ X Y)"
+   "1]"))
+
+(with-test (:name (:debugger :source 2))
+  (test-debugger
+   "d
+    source 0
+    debugger-test-done!"
+   `(locally (declare (optimize (speed 0) (safety 3) (debug 3)))
+      (let ((f #'(lambda (x cont)
+                   (print x (make-broadcast-stream))
+                   (if (zerop x)
+                       (error "foo")
+                       (funcall cont (1- x) cont)))))
+        (funcall f 10 f)))
+   '*
+   "debugger"
+   '*
+   "foo"
+   '*
+   "source: (ERROR \"foo\")"
+   '*
+   "(LAMBDA (X CONT)"
+   '*
+   "(FUNCALL CONT (1- X) CONT)"
+   "1]"))
+
+(with-test (:name (disassemble :high-debug-eval))
+  (eval `(defun this-will-be-disassembled (x)
+           (declare (optimize debug))
+           (+ x x)))
+  (let* ((oopses (make-string-output-stream))
+         (disassembly
+           (let ((*error-output* oopses))
+             (with-output-to-string (*standard-output*)
+               (disassemble 'this-will-be-disassembled)))))
+    (with-input-from-string (s disassembly)
+      (assert (search "; disassembly for THIS-WILL-BE-DISASSEMBLED"
+                      (read-line s))))
+    (let ((problems (get-output-stream-string oopses)))
+      (unless (zerop (length problems))
+        (error problems)))))
+
+(defun this-too-will-be-disasssembled (x)
+  (declare (optimize debug))
+  (+ x x))
+
+(with-test (:name (disassemble :high-debug-load))
+  (let* ((oopses (make-string-output-stream))
+         (disassembly
+           (let ((*error-output* oopses))
+             (with-output-to-string (*standard-output*)
+               (disassemble 'this-too-will-be-disasssembled)))))
+    (with-input-from-string (s disassembly)
+      (assert (equal "; disassembly for THIS-TOO-WILL-BE-DISASSSEMBLED"
+                     (read-line s))))
+    (let ((problems (get-output-stream-string oopses)))
+      (unless (zerop (length problems))
+        (error problems)))))
+
 (write-line "/debug.impure.lisp done")
index 0baf4e9..f70216c 100644 (file)
     (eval `(defun empty-let-is-not-toplevel-x () :fun))
     (assert (eq :fun (empty-let-is-not-toplevel-fun)))))
 
+(with-test (:name (eval function-lambda-expression))
+  (assert (equal `(sb-int:named-lambda eval-fle-1 (x)
+                    (block eval-fle-1
+                      (+ x 1)))
+                 (function-lambda-expression
+                  (eval `(progn
+                           (defun eval-fle-1 (x) (+ x 1))
+                           #'eval-fle-1)))))
+  (assert (equal `(lambda (x y z) (+ x 1 y z))
+                 (function-lambda-expression
+                  (eval `(lambda (x y z) (+ x 1 y z)))))))
+
 ;;; success