0.9.16.27:
authorJuho Snellman <jsnell@iki.fi>
Wed, 13 Sep 2006 15:59:31 +0000 (15:59 +0000)
committerJuho Snellman <jsnell@iki.fi>
Wed, 13 Sep 2006 15:59:31 +0000 (15:59 +0000)
        Add an interpreting EVAL, for cases where the compiler is
        unsuitable due to e.g. compilation overhead.

        * The old EVAL is still the default. To use the new one,
          (SETF SB-EXT:*EVALUATOR-MODE* :INTERPRET).

          Making the interpreter the default might be the purer
          choice, since there's a standard way of ensuring that code
          is compiled, and no standard way of ensuring that it's
          not. On the other hand, there are practical reasons for
          keeping the compiler as the default. The interpreter is very
          slow, doesn't have proper debugger support (either for
          backtraces or inspecting frames), and it doesn't have
          stepper support.

        * The interpreter doesn't treat THE or type declarations for
          lexical variables as assertions. The regression tests that
          assume otherwise have been disabled when running in
          interpreted mode. The intepreter will however type-check the
          proclaimed types of specials.

47 files changed:
CREDITS
NEWS
base-target-features.lisp-expr
build-order.lisp-expr
contrib/sb-cltl2/compiler-let.lisp
contrib/sb-introspect/sb-introspect.lisp
doc/manual/compiler.texinfo
package-data-list.lisp-expr
src/code/class.lisp
src/code/cold-init.lisp
src/code/defboot.lisp
src/code/deftypes-for-target.lisp
src/code/describe.lisp
src/code/early-extensions.lisp
src/code/early-full-eval.lisp [new file with mode: 0644]
src/code/eval.lisp
src/code/full-eval.lisp [new file with mode: 0644]
src/code/inspect.lisp
src/code/late-type.lisp
src/code/print.lisp
src/code/target-alieneval.lisp
src/code/target-misc.lisp
src/code/target-thread.lisp
src/code/time.lisp
src/compiler/generic/vm-tran.lisp
src/compiler/ir1util.lisp
src/compiler/target-main.lisp
src/pcl/ctor.lisp
src/pcl/defclass.lisp
src/pcl/documentation.lisp
src/pcl/low.lisp
tests/arith.pure.lisp
tests/clos.impure.lisp
tests/compiler.impure.lisp
tests/compiler.pure.lisp
tests/compiler.test.sh
tests/debug.impure.lisp
tests/defstruct.impure.lisp
tests/dynamic-extent.impure.lisp
tests/finalize.test.sh
tests/full-eval.impure.lisp [new file with mode: 0644]
tests/lambda-list.pure.lisp
tests/loop.pure.lisp
tests/package-locks.impure.lisp
tests/seq.impure.lisp
tests/type.pure.lisp
version.lisp-expr

diff --git a/CREDITS b/CREDITS
index 6394064..95b3ff5 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -559,7 +559,8 @@ Paul Dietz:
 Brian Downing:
   He fixed the linker problems for building SBCL on Mac OS X.  He
   found and fixed the cause of backtraces failing for undefined
-  functions and assembly routines.
+  functions and assembly routines. He wrote the core of SBCL's 
+  alternative interpreter-based EVAL.
 
 Miles Egan:
   He creates binary packages of SBCL releases for Red Hat and other
@@ -598,6 +599,12 @@ Matthias Hoelzl:
 Daisuke Homma:
   He added support for SunOS on x86 processors.
 
+ITA Software:
+  They hired Juho Snellman as a consultant to work on improvements to 
+  SBCL, to be released into the public domain. The work they've funded
+  includes faster compilation speeds, the interpreter-based evaluator
+  and the IR2-based single-stepper.
+
 Espen S Johnsen:
   He provided an ANSI-compliant version of CHANGE-CLASS for PCL.
 
@@ -735,12 +742,11 @@ Nikodemus Siivola:
   besides.
 
 Juho Snellman:
-  He provided several performance enhancements, including a better hash
-  function on strings, removal of unneccessary bounds checks, and
-  multiple improvements to performance of common operations on
-  bignums.  He ported and enhanced the statistical profiler written by
-  Gerd Moellmann for CMU CL.  He completed the work on the x86-64 port
-  of SBCL.
+  He provided a number of bug fixes and performance enhancements to
+  the compiler, the standard library functions, and to the garbage
+  collector. He ported and enhanced the statistical profiler written
+  by Gerd Moellmann for CMU CL. He completed the work on the x86-64
+  port of SBCL.
 
 Brian Spilsbury:
   He wrote Unicode-capable versions of SBCL's character, string, and
diff --git a/NEWS b/NEWS
index fd11ba5..4c2a480 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -18,6 +18,10 @@ changes in sbcl-0.9.17 (0.9.99?) relative to sbcl-0.9.16:
     class-precedence-lists of GENERIC-FUNCTION and
     STANDARD-GENERIC-FUNCTION comply with the requirement of ANSI
     1.4.4.5.
+  * new feature: Add a version of evaluator that uses an interpreter instead
+    of the compiler. EVAL still uses the compiler by default, to switch it
+    to use the interpreter, set the value of the variable 
+    SB-EXT:*EVALUATOR-MODE* to :INTERPRET.
   * bug fix: ENOUGH-NAMESTRING on pathnames with no name and a pattern
     for a type now works.
   * bug fix: loading of default sysinit file works. (thanks to Leonid
@@ -28,6 +32,8 @@ changes in sbcl-0.9.17 (0.9.99?) relative to sbcl-0.9.16:
     non-ASCII characters in their names (thanks to Yaroslav Kavenchuk)
   * bug fix: The :PTY argument for RUN-PROGRAM will now work on 
     systems with Unix98 pty semantics.
+  * bug fix: arguments to RUN-PROGRAM are escaped correctly on win32
+    (thanks to Yaroslav Kavenchuk)
   * bug fix: ASDF-INSTALL will now work with bsd tar.
   * bug fix: ASDF-INSTALL uses GNU tar on Solaris (thanks to Josip
     Gracin).
index c6a5479..1859218 100644 (file)
  ;; character set.
  :sb-unicode
 
+ ;; Support for a full evaluator that can execute all the CL special
+ ;; forms, as opposed to the traditional SBCL evaluator which called
+ ;; COMPILE for everything complicated.
+ :sb-eval
+
  ;; Record source location information for variables, classes, conditions,
  ;; packages, etc. Gives much better information on M-. in Slime, but
  ;; increases core size by about 100kB.
index 144b335..c95481d 100644 (file)
  ;; trace table definitions from compiler/trace-table.lisp.
  ("src/compiler/dump")
 
+ ;; early-full-eval uses !DEFSTRUCT-WITH-ALTERNATE-METACLASS and
+ ;; DEF!METHOD.  It split out from the rest of full-eval because
+ ;; defstruct/metaclass fun makes it unslammable, and to define
+ ;; INTERPRETED-FUNCTION before it is used in compiler/main.
+ #!+sb-eval
+ ("src/code/early-full-eval" :not-host)
+
  ("src/compiler/main") ; needs DEFSTRUCT FASL-OUTPUT from dump.lisp
  ("src/code/source-location")
  ("src/compiler/target-main" :not-host)
  ;; declared special.
  ("src/code/parse-defmacro-errors")
 
+ #!+sb-eval
+ ("src/code/full-eval"   :not-host) ; uses INFO, ARG-COUNT-ERROR
+
  ("src/code/bit-bash"    :not-host) ; needs %NEGATE from assembly/target/arith
 
  ("src/code/target-load" :not-host) ; needs special vars from code/load.lisp
index 932b4cb..6b82f8d 100644 (file)
                                          'compiler-let bindings walked-body)))))))
 
 (sb-walker::define-walker-template compiler-let walk-compiler-let)
+
+#+sb-eval
+(setf (getf sb-eval::*eval-dispatch-functions* 'compiler-let)
+      (lambda (form env)
+        (destructuring-bind (bindings &body body) (cdr form)
+          (loop for binding in bindings
+                if (atom binding)
+                collect binding into vars
+                and collect nil into values
+                else do (assert (proper-list-of-length-p binding 1 2))
+                and collect (first binding) into vars
+                and collect (eval (second binding)) into values
+                finally (return
+                          (let ((new-env (sb-eval::make-env
+                                          :parent env
+                                          :vars (sb-eval::special-bindings vars))))
+                            (progv vars values
+                              (sb-eval::eval-progn body new-env))))))))
index debda19..220fc43 100644 (file)
@@ -318,6 +318,11 @@ If an unsupported TYPE is requested, the function will return NIL.
                        (sb-pcl::unparse-specializers
                         (sb-mop:method-specializers object)))))
        source))
+    #+sb-eval
+    (sb-eval:interpreted-function
+     (let ((source (translate-source-location
+                    (sb-eval:interpreted-function-source-location object))))
+       source))
     (function
      (cond ((struct-accessor-p object)
             (find-definition-source
index 729865f..e9c2e82 100644 (file)
@@ -16,6 +16,7 @@ separate that they have their own chapter, @ref{Efficiency}.
 * Compiler Policy::             
 * Compiler Errors::             
 * Open Coding and Inline Expansion::  
+* Interpreter::
 @end menu
 
 @node  Diagnostic Messages
@@ -1088,3 +1089,20 @@ open-coded. Even when not open-coded, a call to a standard function
 may be transformed into a different function call (as in the last
 example) or compiled as @emph{static call}. Static function call uses
 a more efficient calling convention that forbids redefinition.
+
+@node  Interpreter
+@comment  node-name,  next,  previous,  up
+@section Interpreter
+@cindex Interpreter
+@vindex sb-ext:*evaluator-mode*
+
+By default SBCL implements @code{eval} by calling the native code
+compiler. SBCL also includes an interpreter for use in special cases
+where using the compiler is undesireable, for example due to compilation
+overhead. Unlike in some other Lisp implementations, in SBCL interpreted
+code is not safer or more debuggable than compiled code.
+
+Switching between the compiler and the interpreter is done using the
+special variable @code{sb-ext:*evaluator-mode*}. As of 0.9.17, valid
+values for @code{sb-ext:*evaluator-mode*} are @code{:compile} and
+@code{:interpret}.
index eca3957..e9d850b 100644 (file)
@@ -739,6 +739,9 @@ like *STACK-TOP-HINT* and unsupported stuff like *TRACED-FUN-LIST*."
                ;; external-format support
                "OCTETS-TO-STRING" "STRING-TO-OCTETS"
 
+               ;; Whether to use the interpreter or the compiler for EVAL
+               "*EVALUATOR-MODE*"
+
                ;; timer
                "TIMER" "MAKE-TIMER" "TIMER-NAME" "TIMER-SCHEDULED-P"
                "SCHEDULE-TIMER" "UNSCHEDULE-TIMER" "LIST-ALL-TIMERS"))
@@ -1033,6 +1036,7 @@ retained, possibly temporariliy, because it might be used internally."
                "LOOKS-LIKE-NAME-OF-SPECIAL-VAR-P"
                "POSITIVE-PRIMEP"
                "EVAL-IN-LEXENV"
+               "SIMPLE-EVAL-IN-LEXENV"
                "DEBUG-NAMIFY"
                "FORCE" "DELAY" "PROMISE-READY-P"
                "FIND-RESTART-OR-CONTROL-ERROR"
@@ -1532,7 +1536,7 @@ is a good idea, but see SB-SYS re. blurring of boundaries."
                "FIND-CLASSOID-CELL" "EXTRACT-FUN-TYPE"
                "FUNCALLABLE-STRUCTURE-CLASSOID" "%RANDOM-DOUBLE-FLOAT"
                #!+long-float "%RANDOM-LONG-FLOAT"
-               "%RANDOM-SINGLE-FLOAT" "RANDOM-PCL-CLASSOID"
+               "%RANDOM-SINGLE-FLOAT" "STATIC-CLASSOID"
                "%FUNCALLABLE-INSTANCE-INFO" "RANDOM-CHUNK" "BIG-RANDOM-CHUNK"
                "MAKE-FUNCALLABLE-STRUCTURE-CLASSOID"
                "LAYOUT-CLOS-HASH-MAX" "CLASSOID-CELL-NAME"
@@ -1544,7 +1548,7 @@ is a good idea, but see SB-SYS re. blurring of boundaries."
                "BASIC-STRUCTURE-CLASSOID" "CLASSOID-CELL-CLASSOID"
                "FUNCALLABLE-STRUCTURE-CLASSOID-P" "REGISTER-LAYOUT"
                "FUNCALLABLE-INSTANCE" "RANDOM-FIXNUM-MAX"
-               "MAKE-RANDOM-PCL-CLASSOID" "INSTANCE-LAMBDA"
+               "MAKE-STATIC-CLASSOID" "INSTANCE-LAMBDA"
                "%FUNCALLABLE-INSTANCE-LEXENV" "%MAKE-SYMBOL"
                "%FUNCALLABLE-INSTANCE-FUN" "SYMBOL-HASH"
 
@@ -2344,6 +2348,26 @@ structure representations"
                ;;"NESTED-WALK-FORM" "MACROEXPAND-ALL"
                ))
 
+   #!+sb-eval
+   #s(sb-cold:package-data
+      :name "SB!EVAL"
+      :doc "internal: the evaluator implementation used to execute code without compiling it."
+      :use ("CL" "SB!KERNEL" "SB!EXT")
+      :export ("INTERPRETED-FUNCTION"
+               "INTERPRETED-FUNCTION-P"
+               "INTERPRETED-FUNCTION-NAME"
+               "INTERPRETED-FUNCTION-LAMBDA-LIST"
+               "INTERPRETED-FUNCTION-DOCUMENTATION"
+               "INTERPRETED-FUNCTION-BODY"
+               "INTERPRETED-FUNCTION-SOURCE-LOCATION"
+               "EVAL-IN-ENVIRONMENT"
+               "MAKE-NULL-ENVIRONMENT"
+               "EVAL-IN-NATIVE-ENVIRONMENT"
+               "PREPARE-FOR-COMPILE"
+               "COUNT-EVAL-CALLS"
+               "*EVAL-LEVEL*"
+               "*EVAL-CALLS*"))
+
    #!+win32
    #s(sb-cold:package-data
       :name "SB!WIN32"
index 0390b9f..9171637 100644 (file)
@@ -952,8 +952,8 @@ NIL is returned when no such class exists."
                                (:constructor make-standard-classoid)))
 ;;; a metaclass for miscellaneous PCL structure-like objects (at the
 ;;; moment, only CTOR objects).
-(def!struct (random-pcl-classoid (:include classoid)
-                                 (:constructor make-random-pcl-classoid)))
+(def!struct (static-classoid (:include classoid)
+                             (:constructor make-static-classoid)))
 \f
 ;;;; built-in classes
 
index 3ab05a9..eb223cb 100644 (file)
         *cold-init-complete-p* nil
         *type-system-initialized* nil)
 
+  ;; I'm not sure where eval is first called, so I put this first.
+  #!+sb-eval
+  (show-and-call sb!eval::!full-eval-cold-init)
+
   (show-and-call thread-init-or-reinit)
   (show-and-call !typecheckfuns-cold-init)
 
index 9dc6d46..a5e7ba0 100644 (file)
   #+nil (setf (%fun-name def) name)
 
   (when doc
-    (setf (fdocumentation name 'function) doc))
+    (setf (fdocumentation name 'function) doc)
+    #!+sb-eval
+    (when (typep def 'sb!eval:interpreted-function)
+      (setf (sb!eval:interpreted-function-documentation def)
+            doc)))
   name)
 \f
 ;;;; DEFVAR and DEFPARAMETER
index 7c8fe9a..8a066f5 100644 (file)
@@ -56,7 +56,8 @@
 
 (sb!xc:deftype bit () '(integer 0 1))
 
-(sb!xc:deftype compiled-function () 'function)
+(sb!xc:deftype compiled-function ()
+  '(and function #!+sb-eval (not sb!eval:interpreted-function)))
 
 (sb!xc:deftype atom () '(not cons))
 
index 1ec98ed..51a5cfd 100644 (file)
       (%describe-fun-name name s (%simple-fun-type x))))
   (%describe-compiled-from (sb-kernel:fun-code-header x) s))
 
+(defun %describe-fun (x s &optional (kind :function) (name nil))
+  (etypecase x
+    #+sb-eval
+    (sb-eval:interpreted-function
+     (%describe-interpreted-fun x s kind name))
+    (function
+     (%describe-compiled-fun x s kind name))))
+
 ;;; Describe a function object. KIND and NAME provide some information
 ;;; about where the function came from.
-(defun %describe-fun (x s &optional (kind :function) (name nil))
+(defun %describe-compiled-fun (x s &optional (kind :function) (name nil))
   (declare (type function x))
   (declare (type stream s))
   (declare (type (member :macro :function) kind))
                      (format s "~S is a function." x))))
     (format s "~@:_~@<Its associated name (as in ~S) is ~2I~_~S.~:>"
             'function-lambda-expression
-            (%fun-name x))
+            (nth-value 2 (function-lambda-expression x)))
     (case (widetag-of x)
       (#.sb-vm:closure-header-widetag
        (%describe-fun-compiled (%closure-fun x) s kind name)
        (format s "~@:_It is an unknown type of function."))))
   (terpri s))
 
+;; Describe an interpreted function.
+#+sb-eval
+(defun %describe-interpreted-fun (x s &optional (kind :function) (name nil))
+  (declare (type sb-eval:interpreted-function x))
+  (declare (type stream s))
+  (declare (type (member :macro :function) kind))
+  (fresh-line s)
+  (pprint-logical-block (s nil)
+    (ecase kind
+      (:macro (format s "Macro-function: ~S" x))
+      (:function (if name
+                     (format s "Function: ~S" x)
+                     (format s "~S is a function." x))))
+    (format s "~@:_~@<Its associated name (as in ~S) is ~2I~_~S.~:>"
+            'function-lambda-expression
+            (nth-value 2 (function-lambda-expression x)))
+    (format s "~&It is an interpreted function.~%")
+    (let ((args (sb-eval:interpreted-function-lambda-list x)))
+      (cond ((not args)
+             (write-string "There are no arguments." s))
+            (t
+             (format s "~&~@(The ~@[~A's ~]arguments are:~@:_~)" kind)
+             (write-string "  " s)
+             (let ((*print-pretty* t)
+                   (*print-escape* t)
+                   (*print-base* 10)
+                   (*print-radix* nil))
+               (pprint-logical-block (s nil)
+                 (pprint-indent :current 2)
+                 (format s "~A" args)))))
+      (format s "~&It was defined as: ")
+      (let ((*print-pretty* t)
+            (*print-escape* t)
+            (*print-base* 10)
+            (*print-radix* nil))
+        (pprint-logical-block (s nil)
+          (pprint-indent :current 2)
+          (format s "~A" (function-lambda-expression x))))))
+  (terpri s))
+
 (defmethod describe-object ((x function) s)
   (%describe-fun x s :function))
 
index 5115fc0..0b2b05a 100644 (file)
           (*print-level* (or (true *print-level*) 6))
           (*print-length* (or (true *print-length*) 12)))
       (funcall function))))
+
+;;; Default evaluator mode (interpeter / compiler)
+
+(declaim (type (member :compile #!+sb-eval :interpret) *evaluator-mode*))
+(defparameter *evaluator-mode* :compile
+  #!+sb-doc
+  "Toggle between different evaluator implementations. If set to :COMPILE,
+an implementation of EVAL that calls the compiler will be used. If set
+to :INTERPRET, an interpreter will be used.")
+
diff --git a/src/code/early-full-eval.lisp b/src/code/early-full-eval.lisp
new file mode 100644 (file)
index 0000000..fd7f203
--- /dev/null
@@ -0,0 +1,56 @@
+;;;; An interpreting EVAL
+
+;;;; This software is part of the SBCL system. See the README file for
+;;;; more information.
+;;;;
+;;;; This software is derived from the CMU CL system, which was
+;;;; written at Carnegie Mellon University and released into the
+;;;; public domain. The software is in the public domain and is
+;;;; provided with absolutely no warranty. See the COPYING and CREDITS
+;;;; files for more information.
+
+(in-package "SB!EVAL")
+
+(defparameter *eval-level* -1)
+(defparameter *eval-calls* 0)
+(defparameter *eval-verbose* nil)
+
+(defun !full-eval-cold-init ()
+  (setf *eval-level* -1
+        *eval-calls* 0
+        *eval-verbose* nil
+        *evaluator-mode* :compile))
+
+;; !defstruct-with-alternate-metaclass is unslammable and the
+;; RECOMPILE restart doesn't work on it.  This is the main reason why
+;; this stuff is split out into its own file.  Also, it lets the
+;; INTERPRETED-FUNCTION type be declared before it is used in
+;; compiler/main.
+(sb!kernel::!defstruct-with-alternate-metaclass
+ interpreted-function
+ :slot-names (name lambda-list env declarations documentation body source-location)
+ :boa-constructor %make-interpreted-function
+ :superclass-name function
+ :metaclass-name static-classoid
+ :metaclass-constructor make-static-classoid
+ :dd-type funcallable-structure
+ :runtime-type-checks-p nil)
+
+(defun make-interpreted-function
+    (&key name lambda-list env declarations documentation body source-location)
+  (let ((function (%make-interpreted-function
+                   name lambda-list env declarations documentation body
+                   source-location)))
+    (setf (sb!kernel:funcallable-instance-fun function)
+          #'(lambda (&rest args)
+              (interpreted-apply function args)))
+    function))
+
+(defun interpreted-function-p (function)
+  (typep function 'interpreted-function))
+
+(sb!int:def!method print-object ((obj interpreted-function) stream)
+  (print-unreadable-object (obj stream
+                            :identity (not (interpreted-function-name obj)))
+    (format stream "~A ~A" '#:interpreted-function
+            (interpreted-function-name obj))))
index 8f0d970..406dc09 100644 (file)
@@ -13,7 +13,7 @@
 
 ;;; general case of EVAL (except in that it can't handle toplevel
 ;;; EVAL-WHEN magic properly): Delegate to #'COMPILE.
-(defun %eval (expr lexenv)
+(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
@@ -26,7 +26,7 @@
     (funcall fun)))
 
 ;;; Handle PROGN and implicit PROGN.
-(defun eval-progn-body (progn-body lexenv)
+(defun simple-eval-progn-body (progn-body lexenv)
   (unless (list-with-length-p progn-body)
     (let ((*print-circle* t))
       (error 'simple-program-error
         (rest-i (rest i) (rest i)))
       (nil)
     (if rest-i ; if not last element of list
-        (eval-in-lexenv (first i) lexenv)
-        (return (eval-in-lexenv (first i) lexenv)))))
+        (simple-eval-in-lexenv (first i) lexenv)
+        (return (simple-eval-in-lexenv (first i) lexenv)))))
 
-(defun eval-locally (exp lexenv &key vars)
+(defun simple-eval-locally (exp lexenv &key vars)
   (multiple-value-bind (body decls)
       (parse-body (rest exp) :doc-string-allowed nil)
     (let ((lexenv
                                   nil
                                   :lexenv lexenv
                                   :context :eval))))
-      (eval-progn-body body lexenv))))
-
-(defun eval (original-exp)
-  #!+sb-doc
-  "Evaluate the argument in a null lexical environment, returning the
-  result or results."
-  (eval-in-lexenv original-exp (make-null-lexenv)))
+      (simple-eval-progn-body body lexenv))))
 
 ;;;; EVAL-ERROR
 ;;;;
 ;;;; Analogous to COMPILER-ERROR, but simpler.
 
-(define-condition eval-error (encapsulated-condition) ())
+(define-condition eval-error (encapsulated-condition)
+  ()
+  (:report (lambda (condition stream)
+             (print-object (encapsulated-condition condition) stream))))
 
 (defun eval-error (condition)
   (signal 'eval-error :condition condition)
   (bug "Unhandled EVAL-ERROR"))
 
 ;;; Pick off a few easy cases, and the various top level EVAL-WHEN
-;;; magical cases, and call %EVAL for the rest.
-(defun eval-in-lexenv (original-exp lexenv)
+;;; magical cases, and call %SIMPLE-EVAL for the rest.
+(defun simple-eval-in-lexenv (original-exp lexenv)
   (declare (optimize (safety 1)))
   ;; (aver (lexenv-simple-p lexenv))
   (handler-bind
              ;; with DEFINE-SYMBOL-MACRO, keeping the code walkers
              ;; happy.
              (:alien
-              (%eval original-exp lexenv))))
+              (%simple-eval original-exp lexenv))))
           (list
            (let ((name (first exp))
                  (n-args (1- (length exp))))
                            (not (consp (let ((sb!c:*lexenv* lexenv))
                                          (sb!c:lexenv-find name funs)))))
                       (%coerce-name-to-fun name)
-                      (%eval original-exp lexenv))))
+                    (%simple-eval original-exp lexenv))))
                ((quote)
                 (unless (= n-args 1)
                   (error "wrong number of args to QUOTE:~% ~S" exp))
                             ;; We duplicate the call to SET so that the
                             ;; correct value gets returned.
                             (set (first args)
-                                 (eval-in-lexenv (second args) lexenv)))
+                                 (simple-eval-in-lexenv (second args) lexenv)))
                          (set (first args)
-                              (eval-in-lexenv (second args) lexenv))))
+                              (simple-eval-in-lexenv (second args) lexenv))))
                     (let ((symbol (first name)))
                       (case (info :variable :kind symbol)
                         (:special)
-                        (t (return (%eval original-exp lexenv))))
+                        (t (return (%simple-eval original-exp lexenv))))
                       (unless (type= (info :variable :type symbol)
                                      *universal-type*)
                         ;; let the compiler deal with type checking
-                        (return (%eval original-exp lexenv)))))))
+                        (return (%simple-eval original-exp lexenv)))))))
                ((progn)
-                (eval-progn-body (rest exp) lexenv))
+                (simple-eval-progn-body (rest exp) lexenv))
                ((eval-when)
                 ;; FIXME: DESTRUCTURING-BIND returns ARG-COUNT-ERROR
                 ;; instead of PROGRAM-ERROR when there's something wrong
                     ;; PROGN; otherwise, the EVAL-WHEN form returns NIL.
                     (declare (ignore ct lt))
                     (when e
-                      (eval-progn-body body lexenv)))))
+                    (simple-eval-progn-body body lexenv)))))
                ((locally)
-                (eval-locally exp lexenv))
+              (simple-eval-locally exp lexenv))
                ((macrolet)
                 (destructuring-bind (definitions &rest body)
                     (rest exp)
                               (declare (ignore funs))
                               sb!c:*lexenv*)
                             :eval))))
-                    (eval-locally `(locally ,@body) lexenv))))
+                  (simple-eval-locally `(locally ,@body) lexenv))))
                ((symbol-macrolet)
                 (destructuring-bind (definitions &rest body) (rest exp)
                   (multiple-value-bind (lexenv vars)
                          (lambda (&key vars)
                            (values sb!c:*lexenv* vars))
                          :eval))
-                    (eval-locally `(locally ,@body) lexenv :vars vars))))
+                    (simple-eval-locally `(locally ,@body) lexenv :vars vars))))
                ((if)
                 (destructuring-bind (test then &optional else) (rest exp)
                   (eval-in-lexenv (if (eval-in-lexenv test lexenv)
                ((let let*)
                 (destructuring-bind (definitions &rest body) (rest exp)
                   (if (null definitions)
-                      (eval-locally `(locally ,@body) lexenv)
-                      (%eval exp lexenv))))
+                      (simple-eval-locally `(locally ,@body) lexenv)
+                      (%simple-eval exp lexenv))))
                (t
                 (if (and (symbolp name)
                          (eq (info :function :kind name) :function))
                       (dolist (arg (rest exp))
                         (args (eval-in-lexenv arg lexenv)))
                       (apply (symbol-function name) (args)))
-                    (%eval exp lexenv))))))
+                    (%simple-eval exp lexenv))))))
           (t
            exp))))))
+
+(defun eval-in-lexenv (exp lexenv)
+  #!+sb-eval
+  (if (eq *evaluator-mode* :compile)
+      (simple-eval-in-lexenv exp lexenv)
+      (sb!eval:eval-in-native-environment exp lexenv))
+  #!-sb-eval
+  (simple-eval-in-lexenv exp lexenv))
+
+(defun eval (original-exp)
+  #!+sb-doc
+  "Evaluate the argument in a null lexical environment, returning the
+   result or results."
+  (eval-in-lexenv original-exp (make-null-lexenv)))
+
 \f
 ;;; miscellaneous full function definitions of things which are
 ;;; ordinarily handled magically by the compiler
diff --git a/src/code/full-eval.lisp b/src/code/full-eval.lisp
new file mode 100644 (file)
index 0000000..56208a0
--- /dev/null
@@ -0,0 +1,1209 @@
+;;;; An interpreting EVAL
+
+;;;; This software is part of the SBCL system. See the README file for
+;;;; more information.
+;;;;
+;;;; This software is derived from the CMU CL system, which was
+;;;; written at Carnegie Mellon University and released into the
+;;;; public domain. The software is in the public domain and is
+;;;; provided with absolutely no warranty. See the COPYING and CREDITS
+;;;; files for more information.
+
+(in-package "SB!EVAL")
+
+;; (declaim (optimize (speed 3) (debug 1) (safety 1)))
+
+;;; Values used for marking specials/macros/etc in environments.
+(defvar *special* (gensym "SPECIAL"))
+(defvar *macro* (gensym "MACRO"))
+(defvar *symbol-macro* (gensym "SYMBOL-MACRO"))
+(defvar *not-present* (gensym "NOT-PRESENT"))
+
+(define-condition interpreted-program-error (program-error simple-condition sb!impl::encapsulated-condition)
+  ()
+  (:report (lambda (condition stream)
+             (if (slot-boundp condition 'condition)
+                 (progn
+                   (format stream "Error evaluating a form:~% ~A"
+                           (sb!impl::encapsulated-condition condition)))
+                 (format stream "Error evaluating a form:~% ~?"
+                         (simple-condition-format-control condition)
+                         (simple-condition-format-arguments condition))))))
+
+;;; ANSI defines that program syntax errors should be of type
+;;; PROGRAM-ERROR.  Therefore...
+(define-condition arg-count-program-error (sb!kernel::arg-count-error
+                                           program-error)
+  ())
+
+(defun arg-count-program-error (datum &rest arguments)
+  (declare (ignore datum))
+  (apply #'error 'arg-count-program-error arguments))
+
+;; OAOOM? (see destructuring-bind.lisp)
+(defmacro program-destructuring-bind (lambda-list arg-list &body body)
+  (let ((arg-list-name (gensym "ARG-LIST-")))
+    (multiple-value-bind (body local-decls)
+        (sb!kernel:parse-defmacro lambda-list arg-list-name body nil
+                                  'program-destructuring-bind
+                                  :anonymousp t
+                                  :doc-string-allowed nil
+                                  :wrap-block nil
+                                  :error-fun 'arg-count-program-error)
+      `(let ((,arg-list-name ,arg-list))
+         ,@local-decls
+         ,body))))
+
+(defun ip-error (format-control &rest format-arguments)
+  (error 'interpreted-program-error
+         :format-control format-control
+         :format-arguments format-arguments))
+
+(defmacro nconc-2 (a b)
+  (let ((tmp (gensym))
+        (tmp2 (gensym)))
+    `(let ((,tmp ,a)
+           (,tmp2 ,b))
+       (if ,tmp
+           (progn (setf (cdr (last ,tmp)) ,tmp2) ,tmp)
+           ,tmp2))))
+
+;;; Construct a compiler LEXENV from the same data that's used for
+;;; creating an interpreter ENV. This is needed for example when
+;;; passing the environment to macroexpanders or when compiling an
+;;; interpreted function.
+(defun fabricate-new-native-environment (old-lexenv new-funs new-expanders
+                                         new-vars new-symbol-expansions
+                                         declarations)
+  (labels ((to-native-funs (binding)
+             ;; Non-macroexpander function entries are irrelevant for
+             ;; the LEXENV. If we're using the LEXENV for
+             ;; macro-expansion any references to local non-macro
+             ;; function bindings are undefined behaviour. If we're
+             ;; compiling an interpreted function, a lexical environment
+             ;; with non-macro functions will be too hairy to compile.
+             (if (eq (cdr binding) *macro*)
+                 (cons (car binding)
+                       (cons 'sb!sys:macro
+                             (cdr (assoc (car binding) new-expanders))))
+                 (cons (car binding)
+                       :bogus)))
+           (to-native-vars (binding)
+             ;; And likewise for symbol macros.
+             (if (eq (cdr binding) *symbol-macro*)
+                 (cons (car binding)
+                       (cons 'sb!sys:macro
+                             (cdr (assoc (car binding) new-symbol-expansions))))
+                 (cons (car binding)
+                       :bogus))))
+    (let ((lexenv (sb!c::internal-make-lexenv
+                   (nconc-2 (mapcar #'to-native-funs new-funs)
+                            (sb!c::lexenv-funs old-lexenv))
+                   (nconc-2 (mapcar #'to-native-vars new-vars)
+                            (sb!c::lexenv-vars old-lexenv))
+                   nil nil nil nil nil
+                   (sb!c::lexenv-handled-conditions old-lexenv)
+                   (sb!c::lexenv-disabled-package-locks old-lexenv)
+                   (sb!c::lexenv-policy old-lexenv))))
+      (dolist (declaration declarations)
+        (unless (consp declaration)
+          (ip-error "malformed declaration specifier ~S in ~S"
+                    declaration (cons 'declare declarations)))
+        (case (car declaration)
+          ((optimize)
+           (dolist (element (cdr declaration))
+             (multiple-value-bind (quality value)
+                 (if (not (consp element))
+                     (values element 3)
+                     (program-destructuring-bind (quality value)
+                         element
+                       (values quality value)))
+               (if (sb!c::policy-quality-name-p quality)
+                   (push (cons quality value)
+                         (sb!c::lexenv-%policy lexenv))
+                   (warn "ignoring unknown optimization quality ~
+                                      ~S in ~S" quality
+                                      (cons 'declare declarations))))))
+          (sb!ext:muffle-conditions
+           (setf (sb!c::lexenv-handled-conditions lexenv)
+                 (sb!c::process-muffle-conditions-decl
+                  declaration
+                  (sb!c::lexenv-handled-conditions lexenv))))
+          (sb!ext:unmuffle-conditions
+           (setf (sb!c::lexenv-handled-conditions lexenv)
+                 (sb!c::process-unmuffle-conditions-decl
+                  declaration
+                  (sb!c::lexenv-handled-conditions lexenv))))
+          ((sb!ext:disable-package-locks sb!ext:enable-package-locks)
+           (setf (sb!c::lexenv-disabled-package-locks lexenv)
+                 (sb!c::process-package-lock-decl
+                  declaration
+                  (sb!c::lexenv-disabled-package-locks lexenv))))))
+      lexenv)))
+
+(defstruct (env
+             (:constructor %make-env
+                           (parent vars funs expanders symbol-expansions
+                            tags blocks declarations native-lexenv)))
+  parent
+  vars
+  funs
+  expanders
+  symbol-expansions
+  tags
+  blocks
+  declarations
+  native-lexenv)
+
+(defun make-env (&key parent vars funs expanders
+                 symbol-expansions tags blocks declarations)
+  (%make-env parent
+             (append vars (env-vars parent))
+             (append funs (env-funs parent))
+             (append expanders (env-expanders parent))
+             (append symbol-expansions (env-symbol-expansions parent))
+             (nconc-2 tags (env-tags parent))
+             (nconc-2 blocks (env-blocks parent))
+             declarations
+             (fabricate-new-native-environment (env-native-lexenv parent)
+                                               funs expanders
+                                               vars symbol-expansions
+                                               declarations)))
+
+(defun make-null-environment ()
+  (%make-env nil nil nil nil nil nil nil nil
+             (sb!c::internal-make-lexenv
+              nil nil
+              nil nil nil nil nil nil nil
+              sb!c::*policy*)))
+
+;;; Augment ENV with a special or lexical variable binding
+(declaim (inline push-var))
+(defun push-var (name value env)
+  (push (cons name value) (env-vars env))
+  (push (cons name :bogus) (sb!c::lexenv-vars (env-native-lexenv env))))
+
+;;; Augment ENV with a local function binding
+(declaim (inline push-fun))
+(defun push-fun (name value env)
+  (when (fboundp name)
+    (let ((sb!c:*lexenv* (env-native-lexenv env)))
+      (program-assert-symbol-home-package-unlocked
+       :eval name "binding ~A as a local function")))
+  (push (cons name value) (env-funs env))
+  (push (cons name :bogus) (sb!c::lexenv-funs (env-native-lexenv env))))
+
+(sb!int:def!method print-object ((env env) stream)
+  (print-unreadable-object (env stream :type t :identity t)))
+
+(macrolet ((define-get-binding (name accessor &key (test '#'eq))
+             ;; A macro, sadly, because an inline function here is
+             ;; "too hairy"
+             `(defmacro ,name (symbol env)
+                `(assoc ,symbol (,',accessor ,env) :test ,',test))))
+  (define-get-binding get-binding env-vars)
+  (define-get-binding get-fbinding env-funs :test #'equal)
+  (define-get-binding get-expander-binding env-expanders)
+  (define-get-binding get-symbol-expansion-binding env-symbol-expansions)
+  (define-get-binding get-tag-binding env-tags :test #'eql)
+  (define-get-binding get-block-binding env-blocks))
+
+;;; Return a list of all symbols that are declared special in the
+;;; declarations listen in DECLS.
+(defun declared-specials (decls)
+  (let ((specials nil))
+    (dolist (decl decls)
+      (when (eql (car decl) 'special)
+        (dolist (var (cdr decl))
+          (push var specials))))
+    specials))
+
+;;; Given a list of variables that should be marked as special in an
+;;; environment, return the appropriate binding forms to be given
+;;; to MAKE-ENV.
+(defun special-bindings (specials env)
+  (mapcar #'(lambda (var)
+              (let ((sb!c:*lexenv* (env-native-lexenv env)))
+                (program-assert-symbol-home-package-unlocked
+                 :eval var "declaring ~A special"))
+              (cons var *special*))
+          specials))
+
+;;; Return true if SYMBOL has been declared special either globally
+;;; or is in the DECLARED-SPECIALS list.
+(defun specialp (symbol declared-specials)
+  (let ((type (sb!int:info :variable :kind symbol)))
+    (cond
+      ((eq type :constant)
+       ;; Horrible place for this, but it works.
+       (ip-error "Can't bind constant symbol ~S" symbol))
+      ((eq type :special) t)
+      ((member symbol declared-specials :test #'eq)
+       t)
+      (t nil))))
+
+(defun binding-name (binding)
+  (if (consp binding) (first binding) binding))
+(defun binding-value (binding)
+  (if (consp binding) (second binding) nil))
+(defun supplied-p-parameter (spec)
+  (if (consp spec) (third spec) nil))
+(defun keyword-name (spec)
+  (if (consp spec)
+      (if (consp (first spec))
+          (second (first spec))
+          (first spec))
+      spec))
+(defun keyword-key (spec)
+  (if (consp spec)
+      (if (consp (first spec))
+          (first (first spec))
+          (intern (symbol-name (first spec)) "KEYWORD"))
+      (intern (symbol-name spec) "KEYWORD")))
+(defun keyword-default-value (spec)
+  (if (consp spec) (second spec) nil))
+
+;;; Given a list of ARGUMENTS and a LAMBDA-LIST, return two values:
+;;;   * An alist[*] mapping the required parameters of the function to
+;;;     the corresponding argument values
+;;;   * An alist mapping the keyword, optional and rest parameters of
+;;;     the function to the corresponding argument values (if supplied)
+;;;     or to the parameter's default expression (if not). Supplied-p
+;;;     parameters and aux variables are handled in a similar manner.
+;;;
+;;; For example given the argument list of (1 2) and the lambda-list of
+;;; (A &OPTIONAL (B A) (C (1+ A))), we'd return the values
+;;; (A . '1) and ((B . '2) (C . (1+ A))).
+;;;
+;;; Used only for implementing calls to interpreted functions.
+(defun parse-arguments (arguments lambda-list)
+  (multiple-value-bind (required optional rest-p rest keyword-p
+                        keyword allow-other-keys-p aux-p aux)
+      (sb!int:parse-lambda-list lambda-list)
+    (let* ((original-arguments arguments)
+           (arguments-present (length arguments))
+           (required-length (length required))
+           (optional-length (length optional))
+           (non-keyword-arguments (+ required-length optional-length))
+           (optionals-present (- (min non-keyword-arguments arguments-present)
+                                 required-length))
+           (keywords-present-p (> arguments-present non-keyword-arguments))
+           (let-like-bindings nil)
+           (let*-like-bindings nil))
+      (cond
+        ((< arguments-present required-length)
+         (ip-error "~@<Too few arguments in ~S to satisfy lambda list ~S.~:@>"
+                   arguments lambda-list))
+        ((and (not (or rest-p keyword-p)) keywords-present-p)
+         (ip-error "~@<Too many arguments in ~S to satisfy lambda list ~S.~:@>"
+                   arguments lambda-list))
+        ((and keyword-p keywords-present-p
+              (oddp (- arguments-present non-keyword-arguments)))
+         (ip-error "~@<Odd number of &KEY arguments in ~S for ~S.~:@>"
+                   arguments lambda-list)))
+      (dotimes (i required-length)
+        (push (cons (pop required) (pop arguments)) let-like-bindings))
+      (do ((optionals-parsed 0 (1+ optionals-parsed)))
+          ((null optional))
+        (let ((this-optional (pop optional))
+              (supplied-p (< optionals-parsed optionals-present)))
+          (push (cons (binding-name this-optional)
+                      (if supplied-p
+                          (list 'quote (pop arguments))
+                          (binding-value this-optional)))
+                let*-like-bindings)
+          (when (supplied-p-parameter this-optional)
+            (push (cons (supplied-p-parameter this-optional)
+                        (list 'quote supplied-p))
+                  let*-like-bindings))))
+      (let ((keyword-plist arguments))
+        (when rest-p
+          (push (cons rest (list 'quote keyword-plist)) let*-like-bindings))
+        (when keyword-p
+          (unless (or allow-other-keys-p
+                      (getf keyword-plist :allow-other-keys))
+            (loop for (key value) on keyword-plist by #'cddr doing
+                  (when (and (not (eq key :allow-other-keys))
+                             (not (member key keyword :key #'keyword-key)))
+                    (ip-error "~@<Unknown &KEY argument ~S in ~S for ~S.~:@>"
+                              key original-arguments lambda-list))))
+          (dolist (keyword-spec keyword)
+            (let ((supplied (getf keyword-plist (keyword-key keyword-spec)
+                                  *not-present*)))
+              (push (cons (keyword-name keyword-spec)
+                          (if (eq supplied *not-present*)
+                              (keyword-default-value keyword-spec)
+                              (list 'quote supplied)))
+                    let*-like-bindings)
+              (when (supplied-p-parameter keyword-spec)
+                (push (cons (supplied-p-parameter keyword-spec)
+                            (list 'quote (not (eq supplied *not-present*))))
+                      let*-like-bindings))))))
+      (when aux-p
+        (do ()
+            ((null aux))
+          (let ((this-aux (pop aux)))
+            (push (cons (binding-name this-aux)
+                        (binding-value this-aux))
+                  let*-like-bindings))))
+      (values (nreverse let-like-bindings) (nreverse let*-like-bindings)))))
+
+;;; Evaluate LET*-like (sequential) bindings.
+;;;
+;;; Given an alist of BINDINGS, evaluate the value form of the first
+;;; binding in ENV, bind the variable to the value in ENV, and then
+;;; evaluate the next binding form. Once all binding forms have been
+;;; handled, END-ACTION is funcalled.
+;;;
+;;; SPECIALS is a list of variables that have a bound special declaration.
+;;; These variables (and those that have been declaimed as special) are
+;;; bound as special variables.
+(defun eval-next-let*-binding (bindings specials env end-action)
+  (flet ((maybe-eval (exp)
+           ;; Pick off the easy (QUOTE x) case which is very common
+           ;; due to function calls.  (see PARSE-ARGUMENTS)
+           (if (and (consp exp) (eq (car exp) 'quote))
+               (second exp)
+               (%eval exp env))))
+    (if bindings
+        (let* ((binding-name (car (car bindings)))
+               (binding-value (cdr (car bindings))))
+          (if (specialp binding-name specials)
+              (progv
+                  (list binding-name)
+                  (list (maybe-eval binding-value))
+                ;; Mark the variable as special in this environment
+                (push-var binding-name *special* env)
+                (eval-next-let*-binding (cdr bindings)
+                                        specials env end-action))
+              (progn
+                (push-var binding-name (maybe-eval binding-value) env)
+                (eval-next-let*-binding (cdr bindings)
+                                        specials env end-action))))
+        (funcall end-action))))
+
+;;; Create a new environment based on OLD-ENV by adding the variable
+;;; bindings in BINDINGS to it, and call FUNCTION with the new environment
+;;; as the only parameter. DECLARATIONS are the declarations that were
+;;; in a source position where bound declarations for the bindings could
+;;; be introduced.
+;;;
+;;; FREE-SPECIALS-P controls whether all special declarations should
+;;; end cause the variables to be marked as special in the environment
+;;; (when true), or only bound declarations (when false). Basically
+;;; it'll be T when handling a LET, and NIL when handling a call to an
+;;; interpreted function.
+(defun call-with-new-env (old-env bindings declarations
+                          free-specials-p function)
+  (let* ((specials (declared-specials declarations))
+         (dynamic-vars nil)
+         (dynamic-values nil))
+    ;; To check for package-lock violations
+    (special-bindings specials old-env)
+    (flet ((generate-binding (binding)
+             (if (specialp (car binding) specials)
+                 ;; If the variable being bound is globally special or
+                 ;; there's a bound special declaration for it, record it
+                 ;; in DYNAMIC-VARS / -VALUES separately:
+                 ;;   * To handle the case of FREE-SPECIALS-P == T more
+                 ;;     cleanly.
+                 ;;   * The dynamic variables will be bound with PROGV just
+                 ;;     before funcalling
+                 (progn
+                   (push (car binding) dynamic-vars)
+                   (push (cdr binding) dynamic-values)
+                   nil)
+                 ;; Otherwise it's a lexical binding, and the value
+                 ;; will be recorded in the environment.
+                 (list binding))))
+      (let ((new-env (make-env
+                      :parent old-env
+                      :vars (mapcan #'generate-binding bindings)
+                      :declarations declarations)))
+        (dolist (special (if free-specials-p specials dynamic-vars))
+          (push-var special *special* new-env))
+        (if dynamic-vars
+            (progv dynamic-vars dynamic-values
+              (funcall function new-env))
+            ;; When there are no specials, the PROGV would be a no-op,
+            ;; but it's better to elide it completely, since the
+            ;; funcall is then in tail position.
+            (funcall function new-env))))))
+
+;;; Create a new environment based on OLD-ENV by binding the argument
+;;; list ARGUMENTS to LAMBDA-LIST, and call FUNCTION with the new
+;;; environment as argument. DECLARATIONS are the declarations that
+;;; were in a source position where bound declarations for the
+;;; bindings could be introduced.
+(defun call-with-new-env-full-parsing
+    (old-env lambda-list arguments declarations function)
+  (multiple-value-bind (let-like-bindings let*-like-binding)
+      (parse-arguments arguments lambda-list)
+    (let ((specials (declared-specials declarations))
+          var-specials free-specials)
+      ;; Separate the bound and free special declarations
+      (dolist (special specials)
+        (if (or (member special let-like-bindings :key #'car)
+                (member special let*-like-binding :key #'car))
+            (push special var-specials)
+            (push special free-specials)))
+      ;; First introduce the required parameters into the environment
+      ;; with CALL-WITH-NEW-ENV
+      (call-with-new-env
+       old-env let-like-bindings declarations nil
+       #'(lambda (env)
+           ;; Then deal with optionals / keywords / etc.
+           (eval-next-let*-binding
+            let*-like-binding var-specials env
+            #'(lambda ()
+                ;; And now that we have evaluated all the
+                ;; initialization forms for the bindings, add the free
+                ;; special declarations to the environment. To see why
+                ;; this is the right thing to do (instead of passing
+                ;; FREE-SPECIALS-P == T to CALL-WITH-NEW-ENV),
+                ;; consider:
+                ;;
+                ;;   (eval '(let ((*a* 1))
+                ;;     (declare (special *a*))
+                ;;     (let ((*a* 2))
+                ;;       (funcall (lambda (&optional (b *a*))
+                ;;                  (declare (special *a*))
+                ;;                  (values b *a*))))))
+                ;;
+                ;; *A* should be special in the body of the lambda, but
+                ;; not when evaluating the default value of B.
+                (dolist (special free-specials)
+                  (push-var special *special* env))
+                (funcall function env))))))))
+
+;;; Set the VALUE of the binding (either lexical or special) of the
+;;; variable named by SYMBOL in the environment ENV.
+(defun set-variable (symbol value env)
+  (let ((binding (get-binding symbol env)))
+    (if binding
+        (cond
+          ((eq (cdr binding) *special*)
+           (setf (symbol-value symbol) value))
+          ((eq (cdr binding) *symbol-macro*)
+           (error "Tried to set a symbol-macrolet!"))
+          (t (setf (cdr binding) value)))
+        (case (sb!int:info :variable :kind symbol)
+          (:macro (error "Tried to set a symbol-macrolet!"))
+          (:alien (let ((type (sb!int:info :variable :alien-info symbol)))
+                    (setf (sb!alien::%heap-alien type) value)))
+          (t
+           (let ((type (sb!c::info :variable :type symbol)))
+             (when type
+               (let ((type-specifier (sb!kernel:type-specifier type)))
+                 (unless (typep value type-specifier)
+                   (error 'type-error
+                          :datum value
+                          :expected-type type-specifier))))
+             (setf (symbol-value symbol) value)))))))
+
+;;; Retrieve the value of the binding (either lexical or special) of
+;;; the variable named by SYMBOL in the environment ENV. For symbol
+;;; macros the expansion is returned instead.
+(defun get-variable (symbol env)
+  (let ((binding (get-binding symbol env)))
+    (if binding
+        (cond
+          ((eq (cdr binding) *special*)
+           (values (symbol-value symbol) :variable))
+          ((eq (cdr binding) *symbol-macro*)
+           (values (cdr (get-symbol-expansion-binding symbol env))
+                   :expansion))
+          (t (values (cdr binding) :variable)))
+        (case (sb!int:info :variable :kind symbol)
+          (:macro (values (macroexpand-1 symbol) :expansion))
+          (:alien (let ((type (sb!int:info :variable :alien-info symbol)))
+                    (values (sb!alien::%heap-alien type)
+                            :variable)))
+          (t (values (symbol-value symbol) :variable))))))
+
+;;; Retrieve the function/macro binding of the symbol NAME in
+;;; environment ENV. The second return value will be :MACRO for macro
+;;; bindings, :FUNCTION for function bindings.
+(defun get-function (name env)
+  (let ((binding (get-fbinding name env)))
+    (if binding
+        (cond
+          ((eq (cdr binding) *macro*)
+           (values (cdr (get-expander-binding name env)) :macro))
+          (t (values (cdr binding) :function)))
+        (cond
+          ((and (symbolp name) (macro-function name))
+           (values (macro-function name) :macro))
+          (t (values (%coerce-name-to-fun name) :function))))))
+
+;;; Return true if EXP is a lambda form.
+(defun lambdap (exp)
+  (case (car exp) ((lambda
+                    sb!int:named-lambda
+                    sb!kernel:instance-lambda)
+                   t)))
+
+;;; Split off the declarations (and the docstring, if
+;;; DOC-STRING-ALLOWED is true) from the actual forms of BODY.
+;;; Returns three values: the cons in BODY containing the first
+;;; non-header subform, the docstring, and a list of the declarations.
+;;;
+;;; FIXME: The name of this function is somewhat misleading. It's not
+;;; used just for parsing the headers from lambda bodies, but for all
+;;; special forms that have attached declarations.
+(defun parse-lambda-headers (body &key doc-string-allowed)
+  (loop with documentation = nil
+        with declarations = nil
+        for form on body do
+        (cond
+          ((and doc-string-allowed (stringp (car form)))
+           (if (cdr form)               ; CLHS 3.4.11
+               (if documentation
+                   (ip-error "~@<Duplicate doc string ~S.~:@>" (car form))
+                   (setf documentation (car form)))
+               (return (values form documentation declarations))))
+          ((and (consp (car form)) (eql (caar form) 'declare))
+           (setf declarations (append declarations (cdar form))))
+          (t (return (values form documentation declarations))))
+        finally (return (values nil documentation declarations))))
+
+;;; Create an interpreted function from the lambda-form EXP evaluated
+;;; in the environment ENV.
+(defun eval-lambda (exp env)
+  (case (car exp)
+    ((lambda sb!kernel:instance-lambda)
+     (multiple-value-bind (body documentation declarations)
+         (parse-lambda-headers (cddr exp) :doc-string-allowed t)
+       (make-interpreted-function :lambda-list (second exp)
+                                  :env env :body body
+                                  :documentation documentation
+                                  :source-location (sb!c::make-definition-source-location)
+                                  :declarations declarations)))
+    ((sb!int:named-lambda)
+     (multiple-value-bind (body documentation declarations)
+         (parse-lambda-headers (cdddr exp) :doc-string-allowed t)
+       (make-interpreted-function :name (second exp)
+                                  :lambda-list (third exp)
+                                  :env env :body body
+                                  :documentation documentation
+                                  :source-location (sb!c::make-definition-source-location)
+                                  :declarations declarations)))))
+
+(defun eval-progn (body env)
+  (let ((previous-exp nil))
+    (dolist (exp body)
+      (if previous-exp
+          (%eval previous-exp env))
+      (setf previous-exp exp))
+    ;; Preserve tail call
+    (%eval previous-exp env)))
+
+(defun eval-if (body env)
+  (program-destructuring-bind (test if-true &optional if-false) body
+    (if (%eval test env)
+        (%eval if-true env)
+        (%eval if-false env))))
+
+(defun eval-let (body env)
+  (program-destructuring-bind (bindings &body body) body
+    ;; First evaluate the bindings in parallel
+    (let ((bindings (mapcar
+                     #'(lambda (binding)
+                         (cons (binding-name binding)
+                               (%eval (binding-value binding) env)))
+                     bindings)))
+      (multiple-value-bind (body documentation declarations)
+          (parse-lambda-headers body :doc-string-allowed nil)
+        (declare (ignore documentation))
+        ;; Then establish them into the environment, and evaluate the
+        ;; body.
+        (call-with-new-env env bindings declarations t
+                           #'(lambda (env)
+                               (eval-progn body env)))))))
+
+(defun eval-let* (body old-env)
+  (program-destructuring-bind (bindings &body body) body
+    (multiple-value-bind (body documentation declarations)
+        (parse-lambda-headers body :doc-string-allowed nil)
+      (declare (ignore documentation))
+      ;; First we separate the special declarations into bound and
+      ;; free declarations.
+      (let ((specials (declared-specials declarations))
+            var-specials free-specials)
+        (dolist (special specials)
+          (if (member special bindings :key #'binding-name)
+              (push special var-specials)
+              (push special free-specials)))
+        (let ((env (make-env :parent old-env
+                             :declarations declarations)))
+          ;; Then we establish the bindings into the environment
+          ;; sequentially.
+          (eval-next-let*-binding
+           (mapcar #'(lambda (binding)
+                       (cons (binding-name binding)
+                             (binding-value binding)))
+                   bindings)
+           var-specials env
+           #'(lambda ()
+               ;; Now that we're done evaluating the bindings, add the
+               ;; free special declarations. See also
+               ;; CALL-WITH-NEW-ENV-FULL-PARSING.
+               (dolist (special free-specials)
+                 (push-var special *special* env))
+               (eval-progn body env))))))))
+
+;; Return a named local function in the environment ENV, made from the
+;; definition form FUNCTION-DEF.
+(defun eval-local-function-def (function-def env)
+  (program-destructuring-bind (name lambda-list &body local-body) function-def
+    (multiple-value-bind (local-body documentation declarations)
+        (parse-lambda-headers local-body :doc-string-allowed t)
+      (%eval `#'(sb!int:named-lambda ,name ,lambda-list
+                  ,@(if documentation
+                        (list documentation)
+                        nil)
+                  (declare ,@declarations)
+                  (block ,(cond ((consp name) (second name))
+                                (t name))
+                    ,@local-body))
+             env))))
+
+(defun eval-flet (body env)
+  (program-destructuring-bind ((&rest local-functions) &body body) body
+    (multiple-value-bind (body documentation declarations)
+        (parse-lambda-headers body :doc-string-allowed nil)
+      (declare (ignore documentation))
+      (let* ((specials (declared-specials declarations))
+             (new-env (make-env :parent env
+                                :vars (special-bindings specials env)
+                                :declarations declarations)))
+        (dolist (function-def local-functions)
+          (push-fun (car function-def)
+                    ;; Evaluate the function definitions in ENV.
+                    (eval-local-function-def function-def env)
+                    ;; But add the bindings to the child environment.
+                    new-env))
+        (eval-progn body new-env)))))
+
+(defun eval-labels (body old-env)
+  (program-destructuring-bind ((&rest local-functions) &body body) body
+    (multiple-value-bind (body documentation declarations)
+        (parse-lambda-headers body :doc-string-allowed nil)
+      (declare (ignore documentation))
+      ;; Create a child environment, evaluate the function definitions
+      ;; in it, and add them into the same environment.
+      (let ((env (make-env :parent old-env
+                           :declarations declarations)))
+        (dolist (function-def local-functions)
+          (push-fun (car function-def)
+                    (eval-local-function-def function-def env)
+                    env))
+        ;; And then add an environment for the body of the LABELS.  A
+        ;; separate environment from the one where we added the
+        ;; functions to is needed, since any special variable
+        ;; declarations need to be in effect in the body, but not in
+        ;; the bodies of the local functions.
+        (let* ((specials (declared-specials declarations))
+               (new-env (make-env :parent env
+                                  :vars (special-bindings specials env))))
+          (eval-progn body new-env))))))
+
+;; Return a local macro-expander in the environment ENV, made from the
+;; definition form FUNCTION-DEF.
+(defun eval-local-macro-def (function-def env)
+  (program-destructuring-bind (name lambda-list &body local-body) function-def
+    (multiple-value-bind (local-body documentation declarations)
+        (parse-lambda-headers local-body :doc-string-allowed t)
+      ;; HAS-ENVIRONMENT and HAS-WHOLE will be either NIL or the name
+      ;; of the variable. (Better names?)
+      (let (has-environment has-whole)
+        ;; Filter out &WHOLE and &ENVIRONMENT from the lambda-list, and
+        ;; do some syntax checking.
+        (when (eq (car lambda-list) '&whole)
+          (setf has-whole (second lambda-list))
+          (setf lambda-list (cddr lambda-list)))
+        (setf lambda-list
+              (loop with skip = 0
+                    for element in lambda-list
+                    if (cond
+                         ((/= skip 0)
+                          (decf skip)
+                          (setf has-environment element)
+                          nil)
+                         ((eq element '&environment)
+                          (if has-environment
+                              (ip-error "Repeated &ENVIRONMENT.")
+                              (setf skip 1))
+                          nil)
+                         ((eq element '&whole)
+                          (ip-error "&WHOLE may only appear first ~
+                                     in MACROLET lambda-list."))
+                         (t t))
+                    collect element))
+        (let ((outer-whole (gensym "WHOLE"))
+              (environment (or has-environment (gensym "ENVIRONMENT")))
+              (macro-name (gensym "NAME")))
+          (%eval `#'(lambda (,outer-whole ,environment)
+                      ,@(if documentation
+                            (list documentation)
+                            nil)
+                      (declare ,@(unless has-environment
+                                         `((ignore ,environment))))
+                      (program-destructuring-bind
+                          (,@(if has-whole
+                                 (list '&whole has-whole)
+                                 nil)
+                             ,macro-name ,@lambda-list)
+                          ,outer-whole
+                        (declare (ignore ,macro-name)
+                                 ,@declarations)
+                        (block ,name ,@local-body)))
+                 env))))))
+
+(defun eval-macrolet (body env)
+  (program-destructuring-bind ((&rest local-functions) &body body) body
+    (flet ((generate-fbinding (macro-def)
+             (cons (car macro-def) *macro*))
+           (generate-mbinding (macro-def)
+             (let ((name (car macro-def))
+                   (sb!c:*lexenv* (env-native-lexenv env)))
+               (when (fboundp name)
+                 (program-assert-symbol-home-package-unlocked
+                  :eval name "binding ~A as a local macro"))
+               (cons name (eval-local-macro-def macro-def env)))))
+      (multiple-value-bind (body documentation declarations)
+          (parse-lambda-headers body :doc-string-allowed nil)
+        (declare (ignore documentation))
+        (let* ((specials (declared-specials declarations))
+               (new-env (make-env :parent env
+                                  :vars (special-bindings specials env)
+                                  :funs (mapcar #'generate-fbinding
+                                                local-functions)
+                                  :expanders (mapcar #'generate-mbinding
+                                                     local-functions)
+                                  :declarations declarations)))
+          (eval-progn body new-env))))))
+
+(defun eval-symbol-macrolet (body env)
+  (program-destructuring-bind ((&rest bindings) &body body) body
+    (flet ((generate-binding (binding)
+             (cons (car binding) *symbol-macro*))
+           (generate-sm-binding (binding)
+             (let ((name (car binding))
+                   (sb!c:*lexenv* (env-native-lexenv env)))
+               (when (or (boundp name)
+                         (eq (sb!int:info :variable :kind name) :macro))
+                 (program-assert-symbol-home-package-unlocked
+                  :eval name "binding ~A as a local symbol-macro"))
+               (cons name (second binding)))))
+      (multiple-value-bind (body documentation declarations)
+          (parse-lambda-headers body :doc-string-allowed nil)
+        (declare (ignore documentation))
+        (let ((specials (declared-specials declarations)))
+          (dolist (binding bindings)
+            (when (specialp (binding-name binding) specials)
+              (ip-error "~@<Can't bind SYMBOL-MACROLET of special ~
+                         variable ~S.~:@>"
+                        (binding-name binding)))))
+        (let* ((specials (declared-specials declarations))
+               (new-env (make-env :parent env
+                                  :vars (nconc-2 (mapcar #'generate-binding
+                                                         bindings)
+                                                 (special-bindings specials env))
+                                  :symbol-expansions (mapcar
+                                                      #'generate-sm-binding
+                                                      bindings)
+                                  :declarations declarations)))
+          (eval-progn body new-env))))))
+
+(defun eval-progv (body env)
+  (program-destructuring-bind (vars vals &body body) body
+    (progv (%eval vars env) (%eval vals env)
+      (eval-progn body env))))
+
+(defun eval-function (body env)
+  (program-destructuring-bind (name) body
+    (cond
+      ;; LAMBDAP assumes that the argument is a cons, so we need the
+      ;; initial symbol case, instead of relying on the fall-through
+      ;; case that has the same function body.
+      ((symbolp name) (nth-value 0 (get-function name env)))
+      ((lambdap name) (eval-lambda name env))
+      (t (nth-value 0 (get-function name env))))))
+
+(defun eval-eval-when (body env)
+  (program-destructuring-bind ((&rest situation) &body body) body
+    ;; FIXME: check that SITUATION only contains valid situations
+    (if (or (member :execute situation)
+            (member 'eval situation))
+        (eval-progn body env))))
+
+(defun eval-quote (body env)
+  (declare (ignore env))
+  (program-destructuring-bind (object) body
+    object))
+
+(defun eval-setq (pairs env)
+  (when (oddp (length pairs))
+    (ip-error "~@<Odd number of args to SETQ: ~S~:@>" (cons 'setq pairs)))
+  (let ((last nil))
+    (loop for (var new-val) on pairs by #'cddr do
+          (handler-case
+              (multiple-value-bind (expansion type) (get-variable var env)
+                (ecase type
+                  (:expansion
+                   (setf last
+                         (%eval (list 'setf expansion new-val) env)))
+                  (:variable
+                   (setf last (set-variable var (%eval new-val env)
+                                            env)))))
+            (unbound-variable (c)
+              (declare (ignore c))
+              (setf last (setf (symbol-value var)
+                               (%eval new-val env))))))
+    last))
+
+(defun eval-multiple-value-call (body env)
+  (program-destructuring-bind (function-form &body forms) body
+    (%apply (%eval function-form env)
+            (loop for form in forms
+                  nconc (multiple-value-list (%eval form env))))))
+
+(defun eval-multiple-value-prog1 (body env)
+  (program-destructuring-bind (first-form &body forms) body
+    (multiple-value-prog1 (%eval first-form env)
+      (eval-progn forms env))))
+
+(defun eval-catch (body env)
+  (program-destructuring-bind (tag &body forms) body
+    (catch (%eval tag env)
+      (eval-progn forms env))))
+
+(defun eval-tagbody (body old-env)
+  (let ((env (make-env :parent old-env))
+        (tags nil)
+        (start body)
+        (target-tag nil))
+    (tagbody
+       (flet ((go-to-tag (tag)
+                (setf target-tag tag)
+                (go go-to-tag)))
+         ;; For each tag, store a trampoline function into the environment
+         ;; and the location in the body into the TAGS alist.
+         (do ((form body (cdr form)))
+             ((null form) nil)
+           (when (atom (car form))
+             ;; FIXME: detect duplicate tags
+             (push (cons (car form) (cdr form)) tags)
+             (push (cons (car form) #'go-to-tag) (env-tags env)))))
+       ;; And then evaluate the forms in the body, starting from the
+       ;; first one.
+       (go execute)
+     go-to-tag
+       ;; The trampoline has set the TARGET-TAG. Restart evaluation of
+       ;; the body from the location in body that matches the tag.
+       (setf start (cdr (assoc target-tag tags)))
+     execute
+       (dolist (form start)
+         (when (not (atom form))
+           (%eval form env))))))
+
+(defun eval-go (body env)
+  (program-destructuring-bind (tag) body
+    (let ((target (get-tag-binding tag env)))
+      (if target
+          ;; Call the GO-TO-TAG trampoline
+          (funcall (cdr target) tag)
+          (ip-error "~@<Attempt to GO to nonexistent tag: ~S~:@>" tag)))))
+
+(defun eval-block (body old-env)
+  (flet ((return-from-eval-block (&rest values)
+           (return-from eval-block (values-list values))))
+    (program-destructuring-bind (name &body body) body
+      (unless (symbolp name)
+        (ip-error "~@<The block name ~S is not a symbol.~:@>" name))
+      (let ((env (make-env
+                  :blocks (list (cons name #'return-from-eval-block))
+                  :parent old-env)))
+        (eval-progn body env)))))
+
+(defun eval-return-from (body env)
+  (program-destructuring-bind (name &optional result) body
+    (let ((target (get-block-binding name env)))
+      (if target
+          (multiple-value-call (cdr target) (%eval result env))
+          (ip-error "~@<Return for unknown block: ~S~:@>" name)))))
+
+(defun eval-the (body env)
+  (program-destructuring-bind (value-type form) body
+    (declare (ignore value-type))
+    ;; FIXME: We should probably check the types here, even though
+    ;; the consequences of the values not being of the asserted types
+    ;; are formally undefined.
+    (%eval form env)))
+
+(defun eval-unwind-protect (body env)
+  (program-destructuring-bind (protected-form &body cleanup-forms) body
+    (unwind-protect (%eval protected-form env)
+      (eval-progn cleanup-forms env))))
+
+(defun eval-throw (body env)
+  (program-destructuring-bind (tag result-form) body
+    (throw (%eval tag env)
+      (%eval result-form env))))
+
+(defun eval-load-time-value (body env)
+  (program-destructuring-bind (form &optional read-only-p) body
+    (declare (ignore read-only-p))
+    (%eval form env)))
+
+(defun eval-locally (body env)
+  (multiple-value-bind (body documentation declarations)
+      (parse-lambda-headers body :doc-string-allowed nil)
+    (declare (ignore documentation))
+    (let* ((specials (declared-specials declarations))
+           (new-env (if (or specials declarations)
+                        (make-env :parent env
+                                  :vars (special-bindings specials env)
+                                  :declarations declarations)
+                        env)))
+      (eval-progn body new-env))))
+
+(defun eval-args (args env)
+  (mapcar #'(lambda (arg) (%eval arg env)) args))
+
+;;; The expansion of SB-SYS:WITH-PINNED-OBJECTS on GENCGC uses some
+;;; VOPs which can't be reasonably implemented in the interpreter. So
+;;; we special-case the macro.
+(defun eval-with-pinned-objects (args env)
+  (program-destructuring-bind (values &body body) args
+    (if (null values)
+        (eval-progn body env)
+        (sb!sys:with-pinned-objects ((car values))
+          (eval-with-pinned-objects (cons (cdr values) body) env)))))
+
+(define-condition macroexpand-hook-type-error (type-error)
+  ()
+  (:report (lambda (condition stream)
+             (format stream "The value of *MACROEXPAND-HOOK* is not a designator for a compiled function: ~A"
+                     (type-error-datum condition)))))
+
+(defvar *eval-dispatch-functions* nil)
+
+;;; Dispatch to the appropriate EVAL-FOO function based on the contents of EXP.
+(declaim (inline %%eval))
+(defun %%eval (exp env)
+  (cond
+    ((symbolp exp)
+     ;; CLHS 3.1.2.1.1 Symbols as Forms
+     (multiple-value-bind (value kind) (get-variable exp env)
+       (ecase kind
+         (:variable value)
+         (:expansion (%eval value env)))))
+    ;; CLHS 3.1.2.1.3 Self-Evaluating Objects
+    ((atom exp) exp)
+    ;; CLHS 3.1.2.1.2 Conses as Forms
+    ((consp exp)
+     (case (car exp)
+       ;; CLHS 3.1.2.1.2.1 Special Forms
+       ((block)                (eval-block (cdr exp) env))
+       ((catch)                (eval-catch (cdr exp) env))
+       ((eval-when)            (eval-eval-when (cdr exp) env))
+       ((flet)                 (eval-flet (cdr exp) env))
+       ((function)             (eval-function (cdr exp) env))
+       ((go)                   (eval-go (cdr exp) env))
+       ((if)                   (eval-if (cdr exp) env))
+       ((labels)               (eval-labels (cdr exp) env))
+       ((let)                  (eval-let (cdr exp) env))
+       ((let*)                 (eval-let* (cdr exp) env))
+       ((load-time-value)      (eval-load-time-value (cdr exp) env))
+       ((locally)              (eval-locally (cdr exp) env))
+       ((macrolet)             (eval-macrolet (cdr exp) env))
+       ((multiple-value-call)  (eval-multiple-value-call (cdr exp) env))
+       ((multiple-value-prog1) (eval-multiple-value-prog1 (cdr exp) env))
+       ((progn)                (eval-progn (cdr exp) env))
+       ((progv)                (eval-progv (cdr exp) env))
+       ((quote)                (eval-quote (cdr exp) env))
+       ((return-from)          (eval-return-from (cdr exp) env))
+       ((setq)                 (eval-setq (cdr exp) env))
+       ((symbol-macrolet)      (eval-symbol-macrolet (cdr exp) env))
+       ((tagbody)              (eval-tagbody (cdr exp) env))
+       ((the)                  (eval-the (cdr exp) env))
+       ((throw)                (eval-throw (cdr exp) env))
+       ((unwind-protect)       (eval-unwind-protect (cdr exp) env))
+       ;; SBCL-specific:
+       ((sb!ext:truly-the)     (eval-the (cdr exp) env))
+       ;; Not a special form, but a macro whose expansion wouldn't be
+       ;; handled correctly by the evaluator.
+       ((sb!sys:with-pinned-objects) (eval-with-pinned-objects (cdr exp) env))
+       (t
+        (let ((dispatcher (getf *eval-dispatch-functions* (car exp))))
+          (cond
+            (dispatcher
+             (funcall dispatcher exp env))
+            ;; CLHS 3.1.2.1.2.4 Lambda Forms
+            ((and (consp (car exp)) (eq (caar exp) 'lambda))
+             (interpreted-apply (eval-function (list (car exp)) env)
+                                (eval-args (cdr exp) env)))
+            (t
+             (multiple-value-bind (function kind) (get-function (car exp) env)
+               (ecase kind
+                 ;; CLHS 3.1.2.1.2.3 Function Forms
+                 (:function (%apply function (eval-args (cdr exp) env)))
+                 ;; CLHS 3.1.2.1.2.2 Macro Forms
+                 (:macro
+                  (let ((hook *macroexpand-hook*))
+                    ;; Having an interpreted function as the
+                    ;; macroexpander hook could cause an infinite
+                    ;; loop.
+                    (unless (compiled-function-p
+                             (etypecase hook
+                               (function hook)
+                               (symbol (symbol-function hook))))
+                      (error 'macroexpand-hook-type-error
+                             :datum hook
+                             :expected-type 'compiled-function))
+                    (%eval (funcall hook
+                                    function
+                                    exp
+                                    (env-native-lexenv env))
+                           env)))))))))))))
+
+(defun %eval (exp env)
+  (incf *eval-calls*)
+  (if *eval-verbose*
+      ;; Dynamically binding *EVAL-LEVEL* will prevent tail call
+      ;; optimization. So only do it when its value will be used for
+      ;; printing debug output.
+      (let ((*eval-level* (1+ *eval-level*)))
+        (let ((*print-circle* t))
+          (format t "~&~vA~S~%" *eval-level* "" `(%eval ,exp)))
+        (%%eval exp env))
+      (%%eval exp env)))
+
+(defun %apply (fun args)
+  (etypecase fun
+    (interpreted-function (interpreted-apply fun args))
+    (function (apply fun args))
+    (symbol (apply fun args))))
+
+(defun interpreted-apply (fun args)
+  (let ((lambda-list (interpreted-function-lambda-list fun))
+        (env (interpreted-function-env fun))
+        (body (interpreted-function-body fun))
+        (declarations (interpreted-function-declarations fun)))
+    (call-with-new-env-full-parsing
+     env lambda-list args declarations
+     #'(lambda (env)
+         (eval-progn body env)))))
+
+;;; We need separate conditions for the different *-TOO-COMPLEX-ERRORs to
+;;; avoid spuriously triggering the handler in EVAL-IN-NATIVE-ENVIRONMENT
+;;; on code like:
+;;;
+;;;   (let ((sb-ext:*evaluator-mode* :interpret))
+;;;     (let ((fun (eval '(let ((a 1)) (lambda () a)))))
+;;;         (eval `(compile nil ,fun))))
+;;;
+;;; FIXME: should these be exported?
+(define-condition interpreter-environment-too-complex-error (simple-error)
+  ())
+(define-condition compiler-environment-too-complex-error (simple-error)
+  ())
+
+;;; Try to compile an interpreted function. If the environment
+;;; contains local functions or lexical variables we'll punt on
+;;; compiling it.
+(defun prepare-for-compile (function)
+  (let ((env (interpreted-function-env function)))
+    (when (or (env-tags env)
+              (env-blocks env)
+              (find-if-not #'(lambda (x) (eq x *macro*))
+                           (env-funs env) :key #'cdr)
+              (find-if-not #'(lambda (x) (eq x *symbol-macro*))
+                           (env-vars env)
+                           :key #'cdr))
+      (error 'interpreter-environment-too-complex-error
+             :format-control
+             "~@<Lexical environment of ~S is too complex to compile.~:@>"
+             :format-arguments
+             (list function)))
+    (values
+     `(sb!int:named-lambda ,(interpreted-function-name function)
+          ,(interpreted-function-lambda-list function)
+        (declare ,@(interpreted-function-declarations function))
+        ,@(interpreted-function-body function))
+     (env-native-lexenv env))))
+
+;;; Convert a compiler LEXENV to an interpreter ENV. This is needed
+;;; for EVAL-IN-LEXENV.
+(defun make-env-from-native-environment (lexenv)
+  (let ((native-funs (sb!c::lexenv-funs lexenv))
+        (native-vars (sb!c::lexenv-vars lexenv)))
+    (flet ((is-macro (thing)
+             (and (consp thing) (eq (car thing) 'sb!sys:macro))))
+      (when (or (sb!c::lexenv-blocks lexenv)
+                (sb!c::lexenv-cleanup lexenv)
+                (sb!c::lexenv-lambda lexenv)
+                (sb!c::lexenv-tags lexenv)
+                (sb!c::lexenv-type-restrictions lexenv)
+                (find-if-not #'is-macro native-funs :key #'cdr)
+                (find-if-not #'is-macro native-vars :key #'cdr))
+        (error 'compiler-environment-too-complex-error
+               :format-control
+               "~@<Lexical environment is too complex to evaluate in: ~S~:@>"
+               :format-arguments
+               (list lexenv))))
+    (flet ((make-binding (native)
+             (cons (car native) *symbol-macro*))
+           (make-sm-binding (native)
+             (cons (car native) (cddr native)))
+           (make-fbinding (native)
+             (cons (car native) *macro*))
+           (make-mbinding (native)
+             (cons (car native) (cddr native))))
+      (%make-env nil
+                 (mapcar #'make-binding native-vars)
+                 (mapcar #'make-fbinding native-funs)
+                 (mapcar #'make-mbinding native-funs)
+                 (mapcar #'make-sm-binding native-vars)
+                 nil
+                 nil
+                 nil
+                 lexenv))))
+
+(defun eval-in-environment (form env)
+  (%eval form env))
+
+(defun eval-in-native-environment (form lexenv)
+  (handler-bind
+      ((sb!impl::eval-error
+        (lambda (condition)
+          (error 'interpreted-program-error
+                 :condition (sb!int:encapsulated-condition condition)
+                 :form form)))
+       (sb!c:compiler-error
+        (lambda (c)
+          (if (boundp 'sb!c::*compiler-error-bailout*)
+              ;; if we're in the compiler, delegate either to a higher
+              ;; authority or, if that's us, back down to the
+              ;; outermost compiler handler...
+              (progn
+                (signal c)
+                nil)
+              ;; ... if we're not in the compiler, better signal the
+              ;; error straight away.
+              (invoke-restart 'sb!c::signal-error)))))
+    (handler-case
+        (let ((env (make-env-from-native-environment lexenv)))
+          (%eval form env))
+      (compiler-environment-too-complex-error (condition)
+        (declare (ignore condition))
+        ;; FIXME: this could be a really annoying warning. It should
+        ;; have its own class.
+        (sb!int:style-warn
+         "~@<Native lexical environment too complex for SB-EVAL ~
+       to evaluate ~S, falling back to SIMPLE-EVAL-IN-LEXENV.  ~
+       Lexenv: ~S~:@>"
+         form lexenv)
+        (sb!int:simple-eval-in-lexenv form lexenv)))))
index 6d3b0a5..1aebddb 100644 (file)
@@ -205,19 +205,32 @@ evaluated expressions.
           (inspected-standard-object-elements object)))
 
 (defmethod inspected-parts ((object function))
-           (values (format nil "The object is a ~A named ~S.~%"
-                           (if (closurep object) 'closure 'function)
-                           (%fun-name object))
-                   t
-                   ;; Defined-from stuff used to be here. Someone took
-                   ;; it out. FIXME: We should make it easy to get
-                   ;; to DESCRIBE from the inspector.
-                   (list*
-                    (cons "Lambda-list" (%fun-lambda-list object))
-                    (cons "Ftype" (%fun-type object))
-                    (when (closurep object)
-                      (list
-                       (cons "Closed over values" (%closure-values object)))))))
+  (values (format nil "The object is a ~A named ~S.~%"
+                  (if (closurep object) 'closure 'function)
+                  (nth-value 2 (function-lambda-expression object)))
+          t
+          ;; Defined-from stuff used to be here. Someone took
+          ;; it out. FIXME: We should make it easy to get
+          ;; to DESCRIBE from the inspector.
+          (list*
+           (cons "Lambda-list" (%fun-lambda-list object))
+           (cons "Ftype" (%fun-type object))
+           (when (closurep object)
+             (list
+              (cons "Closed over values" (%closure-values object)))))))
+
+#+sb-eval
+(defmethod inspected-parts ((object sb-eval:interpreted-function))
+  (values (format nil "The object is an interpreted function named ~S.~%"
+                  (nth-value 2 (function-lambda-expression object)))
+          t
+          ;; Defined-from stuff used to be here. Someone took
+          ;; it out. FIXME: We should make it easy to get
+          ;; to DESCRIBE from the inspector.
+          (list
+           (cons "Lambda-list" (sb-eval:interpreted-function-lambda-list object))
+           (cons "Definition" (function-lambda-expression object))
+           (cons "Documentation" (sb-eval:interpreted-function-documentation object)))))
 
 (defmethod inspected-parts ((object vector))
   (values (format nil
index e1d2e25..b253984 100644 (file)
@@ -2659,7 +2659,7 @@ used for a COMPLEX component.~:@>"
 ;;; mechanically unparsed.
 (!define-type-method (intersection :unparse) (type)
   (declare (type ctype type))
-  (or (find type '(ratio keyword) :key #'specifier-type :test #'type=)
+  (or (find type '(ratio keyword compiled-function) :key #'specifier-type :test #'type=)
       `(and ,@(mapcar #'type-specifier (intersection-type-types type)))))
 
 ;;; shared machinery for type equality: true if every type in the set
index c17846b..d3096e8 100644 (file)
             (default-structure-print object stream *current-level-in-print*))
            (t
             (write-string "#<INSTANCE but not STRUCTURE-OBJECT>" stream))))
+    (funcallable-instance
+     (cond
+       ((not (and (boundp '*print-object-is-disabled-p*)
+                  *print-object-is-disabled-p*))
+        (print-object object stream))
+       (t (output-fun object stream))))
     (function
-     (unless (and (funcallable-instance-p object)
-                  (printed-as-funcallable-standard-class object stream))
-       (output-fun object stream)))
+     (output-fun object stream))
     (symbol
      (output-symbol object stream))
     (number
index c4f4e2f..6784be3 100644 (file)
@@ -885,8 +885,9 @@ one."
                            ,function
                            (or (gethash ',specifier *alien-callback-wrappers*)
                                (setf (gethash ',specifier *alien-callback-wrappers*)
-                                     ,(alien-callback-lisp-wrapper-lambda
-                                       specifier result-type argument-types env))))
+                                     (compile nil
+                                              ',(alien-callback-lisp-wrapper-lambda
+                                                 specifier result-type argument-types env)))))
       ',(parse-alien-type specifier env))))
 
 (defun alien-callback-p (alien)
index c636770..cd4c4a8 100644 (file)
   to COMPILE otherwise, CLOSURE-P is non-NIL if the function's definition
   might have been enclosed in some non-null lexical environment, and
   NAME is some name (for debugging only) or NIL if there is no name."
-    (declare (type function fun))
-    (let* ((fun (%simple-fun-self fun))
-           (name (%fun-name fun))
-           (code (sb!di::fun-code-header fun))
-           (info (sb!kernel:%code-debug-info code)))
-      (if info
-        (let ((source (sb!c::debug-info-source info)))
-          (cond ((and (eq (sb!c::debug-source-from source) :lisp)
-                      (eq (sb!c::debug-source-function source) fun))
-                 (values (svref (sb!c::debug-source-name source) 0)
-                         nil
-                         name))
-                ((legal-fun-name-p name)
-                 (let ((exp (fun-name-inline-expansion name)))
-                   (values exp (not exp) name)))
-                (t
-                 (values nil t name))))
-        (values nil t name))))
+  (declare (type function fun))
+  (etypecase fun
+    #!+sb-eval
+    (sb!eval:interpreted-function
+     (let ((name (sb!eval:interpreted-function-name fun))
+           (lambda-list (sb!eval:interpreted-function-lambda-list fun))
+           (body (sb!eval:interpreted-function-body fun)))
+       (values `(lambda ,lambda-list ,@body)
+               t name)))
+    (function
+     (let* ((fun (%simple-fun-self fun))
+            (name (%fun-name fun))
+            (code (sb!di::fun-code-header fun))
+            (info (sb!kernel:%code-debug-info code)))
+       (if info
+           (let ((source (sb!c::debug-info-source info)))
+             (cond ((and (eq (sb!c::debug-source-from source) :lisp)
+                         (eq (sb!c::debug-source-function source) fun))
+                    (values (svref (sb!c::debug-source-name source) 0)
+                            nil
+                            name))
+                   ((legal-fun-name-p name)
+                    (let ((exp (fun-name-inline-expansion name)))
+                      (values exp (not exp) name)))
+                   (t
+                    (values nil t name))))
+           (values nil t name))))))
 
 (defun closurep (object)
   (= sb!vm:closure-header-widetag (widetag-of object)))
index c5b6fcb..9507b50 100644 (file)
@@ -754,3 +754,6 @@ SB-EXT:QUIT - the usual cleanup forms will be evaluated"
     (if (eql tl-val sb!vm::no-tls-value-marker-widetag)
         (sb!vm::symbol-global-value symbol)
         (sb!kernel:make-lisp-obj tl-val))))
+
+(defun sb!vm::locked-symbol-global-value-add (symbol-name delta)
+  (sb!vm::locked-symbol-global-value-add symbol-name delta))
index e211015..0fc9d1c 100644 (file)
         (old-run-utime old-run-stime old-page-faults old-bytes-consed)
       (time-get-sys-info))
     (setq old-real-time (get-internal-real-time))
-    (let ((start-gc-run-time *gc-run-time*))
+    (let ((start-gc-run-time *gc-run-time*)
+          #!+sb-eval (sb!eval:*eval-calls* 0))
+      (declare #!+sb-eval (special sb!eval:*eval-calls*))
       (multiple-value-prog1
           ;; Execute the form and return its values.
           (funcall fun)
                  ~S second~:P of real time~%  ~
                  ~S second~:P of user run time~%  ~
                  ~S second~:P of system run time~%  ~
-                ~@[[Run times include ~S second~:P GC run time.]~%  ~]~
+              ~@[[Run times include ~S second~:P GC run time.]~%  ~]~
+              ~@[~S call~:P to %EVAL~%  ~]~
                  ~S page fault~:P and~%  ~
                  ~:D bytes consed.~%"
                   (max (/ (- new-real-time old-real-time)
                   (unless (zerop gc-run-time)
                     (/ (float gc-run-time)
                        (float sb!xc:internal-time-units-per-second)))
+                  #!+sb-eval sb!eval:*eval-calls* #!-sb-eval nil
                   (max (- new-page-faults old-page-faults) 0)
                   (max (- new-bytes-consed old-bytes-consed) 0)))))))
+
index b5f20d7..b4b1adb 100644 (file)
 (define-source-transform long-float-p (x) `(double-float-p ,x))
 
 (define-source-transform compiled-function-p (x)
-  `(functionp ,x))
+  #!-sb-eval
+  `(functionp ,x)
+  #!+sb-eval
+  (once-only ((x x))
+    `(and (functionp ,x)
+          (not (sb!eval:interpreted-function-p ,x)))))
 
 (define-source-transform char-int (x)
   `(char-code ,x))
index 7b68ff8..bd6357e 100644 (file)
            (destructuring-bind (name . thing) var
              (declare (ignore name))
              (etypecase thing
-               (leaf nil)
+               ;; The evaluator will mark lexicals with :BOGUS when it
+               ;; translates an interpreter lexenv to a compiler
+               ;; lexenv.
+               ((or leaf #!+sb-eval (member :bogus)) nil)
                (cons (aver (eq (car thing) 'macro))
                      t)
                (heap-alien-info nil)))))
index f6e92de..08fdba6 100644 (file)
 
 (defun compile-in-lexenv (name definition lexenv)
   (multiple-value-bind (compiled-definition warnings-p failure-p)
-      (if (compiled-function-p definition)
-          (values definition nil nil)
-          (actually-compile name definition lexenv))
+      (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)))
+        ((compiled-function-p definition)
+         (values definition nil nil))
+        (t (actually-compile name definition lexenv)))
     (cond (name
            (if (and (symbolp name)
                     (macro-function name))
index 6db57f9..0c2e4eb 100644 (file)
   :slot-names (function-name class-name class initargs)
   :boa-constructor %make-ctor
   :superclass-name function
-  :metaclass-name random-pcl-classoid
-  :metaclass-constructor make-random-pcl-classoid
+  :metaclass-name static-classoid
+  :metaclass-constructor make-static-classoid
   :dd-type funcallable-structure
   :runtime-type-checks-p nil)
 
index 85d0fc3..831dcdd 100644 (file)
   (maplist (lambda (sublist)
              (let ((option-name (first (pop sublist))))
                (when (member option-name sublist :key #'first)
-                 (error "Multiple ~S options in DEFCLASS ~S."
-                        option-name class-name))))
+                 (error 'simple-program-error
+                        :format-control "Multiple ~S options in DEFCLASS ~S."
+                        :format-arguments (list option-name class-name)))))
            options)
   (let (metaclass
         default-initargs
           (:metaclass
            (let ((maybe-metaclass (second option)))
              (unless (and maybe-metaclass (legal-class-name-p maybe-metaclass))
-               (error "~@<The value of the :metaclass option (~S) ~
+               (error 'simple-program-error
+                      :format-control "~@<The value of the :metaclass option (~S) ~
                          is not a legal class name.~:@>"
-                      maybe-metaclass))
+                      :format-arguments (list maybe-metaclass)))
              (setf metaclass maybe-metaclass)))
           (:default-initargs
            (let (initargs arg-names)
index 08687ca..7dd07e4 100644 (file)
 ;;; FIXME: Lots of bare calls to INFO here could be handled
 ;;; more cleanly by calling the FDOCUMENTATION function instead.
 
+(defun fun-doc (x)
+  (etypecase x
+    (generic-function
+     (slot-value x '%documentation))
+    #+sb-eval
+    (sb-eval:interpreted-function
+     (sb-eval:interpreted-function-documentation x))
+    (function
+     (%fun-doc x))))
+
 ;;; functions, macros, and special forms
 (defmethod documentation ((x function) (doc-type (eql 't)))
-  (if (typep x 'generic-function)
-      (slot-value x '%documentation)
-      (%fun-doc x)))
+  (fun-doc x))
 
 (defmethod documentation ((x function) (doc-type (eql 'function)))
-  (if (typep x 'generic-function)
-      (slot-value x '%documentation)
-      (%fun-doc x)))
+  (fun-doc x))
 
 (defmethod documentation ((x list) (doc-type (eql 'function)))
   (and (legal-fun-name-p x)
 (defmethod documentation ((x symbol) (doc-type (eql 'setf)))
   (values (info :setf :documentation x)))
 
-(defmethod (setf documentation) (new-value (x function) (doc-type (eql 't)))
-  (if (typep x 'generic-function)
-      (setf (slot-value x '%documentation) new-value)
-      (let ((name (%fun-name x)))
-        (when (and name (typep name '(or symbol cons)))
-          (setf (info :function :documentation name) new-value))))
+(defun (setf fun-doc) (new-value x)
+  (etypecase x
+    (generic-function
+     (setf (slot-value x '%documentation) new-value))
+    #+sb-eval
+    (sb-eval:interpreted-function
+     (setf (sb-eval:interpreted-function-documentation x)
+           new-value))
+    (function
+     (let ((name (%fun-name x)))
+       (when (and name (typep name '(or symbol cons)))
+         (setf (info :function :documentation name) new-value)))))
   new-value)
 
-(defmethod (setf documentation)
-    (new-value (x function) (doc-type (eql 'function)))
-  (if (typep x 'generic-function)
-      (setf (slot-value x '%documentation) new-value)
-      (let ((name (%fun-name x)))
-        (when (and name (typep name '(or symbol cons)))
-          (setf (info :function :documentation name) new-value))))
-  new-value)
+
+(defmethod (setf documentation) (new-value (x function) (doc-type (eql 't)))
+  (setf (fun-doc x) new-value))
+
+(defmethod (setf documentation) (new-value
+                                 (x function)
+                                 (doc-type (eql 'function)))
+  (setf (fun-doc x) new-value))
 
 (defmethod (setf documentation) (new-value (x list) (doc-type (eql 'function)))
   (setf (info :function :documentation x) new-value))
index aa65f9e..1a1be86 100644 (file)
   (declare (special *boot-state* *the-class-standard-generic-function*))
   (when (valid-function-name-p fun)
     (setq fun (fdefinition fun)))
-  (when (funcallable-instance-p fun)
-    ;; HACK
-    (case (classoid-name (classoid-of fun))
-      (%method-function (setf (%method-function-name fun) new-name))
-      (t ;; KLUDGE: probably a generic function...
-       (if (if (eq *boot-state* 'complete)
-               (typep fun 'generic-function)
-               (eq (class-of fun) *the-class-standard-generic-function*))
-           (setf (%funcallable-instance-info fun 2) new-name)
-           (bug "unanticipated function type")))))
+  (typecase fun
+    (%method-function (setf (%method-function-name fun) new-name))
+    #+sb-eval
+    (sb-eval:interpreted-function
+     (setf (sb-eval:interpreted-function-name fun) new-name))
+    (funcallable-instance ;; KLUDGE: probably a generic function...
+     (cond ((if (eq *boot-state* 'complete)
+                (typep fun 'generic-function)
+                (eq (class-of fun) *the-class-standard-generic-function*))
+            (setf (%funcallable-instance-info fun 2) new-name))
+           (t
+            (bug "unanticipated function type")))))
   ;; Fixup name-to-function mappings in cases where the function
   ;; hasn't been defined by DEFUN.  (FIXME: is this right?  This logic
   ;; comes from CMUCL).  -- CSR, 2004-12-31
   :slot-names (fast-function name)
   :boa-constructor %make-method-function
   :superclass-name function
-  :metaclass-name random-pcl-classoid
-  :metaclass-constructor make-random-pcl-classoid
+  :metaclass-name static-classoid
+  :metaclass-constructor make-static-classoid
   :dd-type funcallable-structure)
 \f
 ;;; WITH-PCL-LOCK is used around some forms that were previously
index 915c545..1505a57 100644 (file)
@@ -21,9 +21,9 @@
              `(progn
                (assert (= (,op 4 2) ,res1))
                (assert (= (,op 2 4) ,res2))
-               (assert (= (funcall (compile nil (lambda (x y) (,op x y))) 4 2)
+               (assert (= (funcall (compile nil '(lambda (x y) (,op x y))) 4 2)
                         ,res1))
-               (assert (= (funcall (compile nil (lambda (x y) (,op x y))) 2 4)
+               (assert (= (funcall (compile nil '(lambda (x y) (,op x y))) 2 4)
                         ,res2)))))
   (test + 6 6)
   (test - 2 -2)
index e5cc140..a5b1085 100644 (file)
@@ -11,8 +11,6 @@
 ;;;; absolutely no warranty. See the COPYING and CREDITS files for
 ;;;; more information.
 
-(load "assertoid.lisp")
-
 (defpackage "CLOS-IMPURE"
   (:use "CL" "ASSERTOID" "TEST-UTIL"))
 (in-package "CLOS-IMPURE")
 (assert (= (bug222 t) 1))
 
 ;;; also, a test case to guard against bogus environment hacking:
+
 (eval-when (:compile-toplevel :load-toplevel :execute)
   (setq bug222-b 3))
 ;;; this should at the least compile:
 ;;; and it would be nice (though not specified by ANSI) if the answer
 ;;; were as follows:
 (let ((x (make-string-output-stream)))
-  ;; not specified by ANSI
-  (assert (= (bug222-b t x) 3))
+  (let ((value (bug222-b t x)))
+    ;; not specified by ANSI
+    #+#.(cl:if (cl:eq sb-ext:*evaluator-mode* :compile) '(and) '(or))
+    (assert (= value 3)))
   ;; specified.
   (assert (char= (char (get-output-stream-string x) 0) #\1)))
 \f
index 8bd3d9e..7d9a58f 100644 (file)
@@ -15,6 +15,9 @@
 ;;;; absolutely no warranty. See the COPYING and CREDITS files for
 ;;;; more information.
 
+(when (eq sb-ext:*evaluator-mode* :interpret)
+  (sb-ext:quit :unix-status 104))
+
 (load "test-util.lisp")
 (load "assertoid.lisp")
 (use-package "TEST-UTIL")
index c5e11ca..cfc11c1 100644 (file)
 
 (cl:in-package :cl-user)
 
+;; The tests in this file assume that EVAL will use the compiler
+(when (eq sb-ext:*evaluator-mode* :interpret)
+  (invoke-restart 'run-tests::skip-file))
+
 ;;; Exercise a compiler bug (by crashing the compiler).
 ;;;
 ;;; This test code is from Douglas Crosher's simplified TICKLE-BUG
index 45acaba..daa3fff 100644 (file)
@@ -161,6 +161,9 @@ fail_on_compiler_note $tmpfilename
 # test case from Rudi for some CLOS WARNINGness that shouldn't have
 # been there
 cat > $tmpfilename <<EOF
+    #+sb-eval (eval-when (:compile-toplevel)
+                (setf sb-ext:*evaluator-mode* :compile))
+
     (eval-when (:compile-toplevel :load-toplevel :execute)
       (defstruct buffer-state
         (output-index 0)))
index b9709f2..f01b9b3 100644 (file)
 ;;;; more information.
 
 (cl:in-package :cl-user)
+
+;;; The debugger doesn't have any native knowledge of the interpreter
+(when (eq sb-ext:*evaluator-mode* :interpret)
+  (sb-ext:quit :unix-status 104))
+
 \f
 ;;;; Check that we get debug arglists right.
 
     ;; happen to be the two case that I had my nose rubbed in when
     ;; debugging a GC problem caused by applying %SIMPLE-FUN-ARGLIST to
     ;; a closure. -- WHN 2001-06-05)
-    (t :unknown)))
+    (t
+     #+sb-eval
+     (if (typep fun 'sb-eval::interpreted-function)
+         (sb-eval::interpreted-function-lambda-list fun)
+         :unknown)
+     #-sb-eval
+     :unknown)))
 
 (defun zoop (zeep &key beep)
   blurp)
index 93cd7b9..83475d0 100644 (file)
 ;;; somewhat bogus, but the requirement is clear.)
 (defstruct person age (name 007 :type string)) ; not an error until 007 used
 (make-person :name "James") ; not an error, 007 not used
+
+#+#.(cl:if (cl:eq sb-ext:*evaluator-mode* :compile) '(and) '(or))
 (assert (raises-error? (make-person) type-error))
+#+#.(cl:if (cl:eq sb-ext:*evaluator-mode* :compile) '(and) '(or))
 (assert (raises-error? (setf (person-name (make-person :name "Q")) 1)
                        type-error))
 
@@ -43,6 +46,8 @@
   (assert (eql (boa-saux-c s) 5)))
                                         ; these two checks should be
                                         ; kept separated
+
+#+#.(cl:if (cl:eq sb-ext:*evaluator-mode* :compile) '(and) '(or))
 (let ((s (make-boa-saux)))
   (locally (declare (optimize (safety 0))
                     (inline boa-saux-a))
 (assert (not (vector-struct-p nil)))
 (assert (not (vector-struct-p #())))
 \f
+
 ;;; bug 3d: type safety with redefined type constraints on slots
+#+#.(cl:if (cl:eq sb-ext:*evaluator-mode* :compile) '(and) '(or))
 (macrolet
     ((test (type)
        (let* ((base-name (intern (format nil "bug3d-~A" type)))
index 8a26b58..ba18278 100644 (file)
@@ -11,6 +11,9 @@
 ;;;; absolutely no warranty. See the COPYING and CREDITS files for
 ;;;; more information.
 
+(when (eq sb-ext:*evaluator-mode* :interpret)
+  (sb-ext:quit :unix-status 104))
+
 (setq sb-c::*check-consistency* t)
 
 (defmacro defun-with-dx (name arglist &body body)
index d19df75..06fae1e 100644 (file)
@@ -16,15 +16,15 @@ ${SBCL:-sbcl} <<EOF > /dev/null &
   (declare (ignore _))
   nil)
 
-(let ((junk (mapcar (lambda (_)
-                      (declare (ignore _))
-                      (let ((x (gensym)))
-                          (finalize x (lambda ()
-                                        ;; cons in finalizer
-                                        (setf *tmp* (make-list 10000))
-                                        (incf *count*)))
-                          x))
-                     (make-list 10000))))
+(let ((junk (mapcar (compile nil '(lambda (_)
+                                   (declare (ignore _))
+                                   (let ((x (gensym)))
+                                     (finalize x (lambda ()
+                                                   ;; cons in finalizer
+                                                   (setf *tmp* (make-list 10000))
+                                                   (incf *count*)))
+                                     x)))
+                    (make-list 10000))))
     (setf junk (foo junk))
     (foo junk))
 
diff --git a/tests/full-eval.impure.lisp b/tests/full-eval.impure.lisp
new file mode 100644 (file)
index 0000000..a35528c
--- /dev/null
@@ -0,0 +1,33 @@
+;;;; various tests of the interpreter
+
+;;;; This software is part of the SBCL system. See the README file for
+;;;; more information.
+;;;;
+;;;; While most of SBCL is derived from the CMU CL system, the test
+;;;; files (like this one) were written from scratch after the fork
+;;;; from CMU CL.
+;;;;
+;;;; This software is in the public domain and is provided with
+;;;; absolutely no warranty. See the COPYING and CREDITS files for
+;;;; more information.
+
+#-sb-eval
+(sb-ext:quit :unix-status 104)
+
+(setf sb-ext:*evaluator-mode* :interpret)
+
+(assert (not (typep (lambda ()) 'compiled-function)))
+
+(assert (not (compiled-function-p (lambda ()))))
+
+(let ((seen-forms (make-hash-table :test 'equal)))
+  (let ((*macroexpand-hook* (compile nil
+                                     `(lambda (fun form env)
+                                        (setf (gethash form ,seen-forms) t)
+                                        (funcall fun form env)))))
+    (let ((fun (lambda ()
+                 (when t nil))))
+      (assert (not (gethash '(when t nil) seen-forms)))
+      (funcall fun)
+      (assert (gethash '(when t nil) seen-forms)))))
+
index 3ccb547..3dd39db 100644 (file)
 ;;;; absolutely no warranty. See the COPYING and CREDITS files for
 ;;;; more information.
 
-(let ((*macroexpand-hook* (lambda (fun form env)
-                            (handler-bind ((error (lambda (c)
-                                                    (when (eq 'destructuring-bind (car form))
-                                                      (throw 'd-b-error c)))))
-                              (funcall fun form env)))))
-  (macrolet ((error-p (ll)
+(let ((*macroexpand-hook*
+       (compile nil
+                (lambda (fun form env)
+                  (handler-bind ((error (lambda (c)
+                                          (when (eq 'destructuring-bind (car form))
+                                            (throw 'd-b-error c)))))
+                    (funcall fun form env))))))
+  (macrolet ((maybe-funcall (&rest args)
+               ;; The evaluator will delay lambda-list checks until
+               ;; the lambda is actually called.
+               (if (eq sb-ext:*evaluator-mode* :interpret)
+                   `(funcall ,@args)
+                   `(progn ,@args)))
+             (error-p (ll)
                `(progn
-                  (multiple-value-bind (result error) (ignore-errors (eval `(lambda ,',ll 'ok)))
+                  (multiple-value-bind (result error)
+                      (ignore-errors (maybe-funcall (eval `(lambda ,',ll 'ok))))
                     (unless (and (not result) error)
                       (error "No error from lambda ~S." ',ll)))
                   (catch 'd-b-error
-                    (eval `(lambda (x) (destructuring-bind ,',ll x 'ok)))
+                    (maybe-funcall
+                     (eval `(lambda (x) (destructuring-bind ,',ll x 'ok)))
+                     nil)
                     (error "No error from d-b ~S." ',ll)))))
     (error-p (&aux (foo 1) &aux (bar 2)))
     (error-p (&aux (foo 1) &key bar))
index 917731a..45d190a 100644 (file)
@@ -33,7 +33,8 @@
 ;;; a bug reported and fixed by Alexey Dejneka sbcl-devel 2001-10-05:
 ;;; The type declarations should apply, hence under Python's
 ;;; declarations-are-assertions rule, the code should signal a type
-;;; error.
+;;; error. (Except when running interpreted code)
+#+#.(cl:if (cl:eq sb-ext:*evaluator-mode* :compile) '(and) '(or))
 (assert (typep (nth-value 1
                           (ignore-errors
                             (funcall (lambda ()
   (setf (gethash 7 ht) 15)
   (assert (= (loop for v fixnum being each hash-key in ht sum v) 8))
   (assert (= (loop for v fixnum being each hash-value in ht sum v) 18))
+  #+#.(cl:if (cl:eq sb-ext:*evaluator-mode* :compile) '(and) '(or))
   (assert (raises-error? (loop for v float being each hash-value in ht sum v)
                          type-error)))
 
index 6781308..e0f50fb 100644 (file)
                        (setf (test:function) 1)))
 
     ;; ftype
+    ;;
+    ;; The interpreter doesn't do anything with ftype declarations
+    #+#.(cl:if (cl:eq sb-ext:*evaluator-mode* :compile) '(and) '(or))
     (test:function . (locally
                          (declare (ftype function test:function))
                        (cons t t)))
 
     ;; type
+    ;;
+    ;; Nor with type declarations
+    #+#.(cl:if (cl:eq sb-ext:*evaluator-mode* :compile) '(and) '(or))
     (test:num . (locally
                     (declare (type fixnum test:num))
                   (cons t t)))
                         (cons t t)))
 
     ;; declare ftype
+    #+#.(cl:if (cl:eq sb-ext:*evaluator-mode* :compile) '(and) '(or))
     (test:numfun . (locally
                        (declare (ftype (function (fixnum) fixnum) test:numfun))
                      (cons t t)))))
 (defmethod pcl-type-declaration-method-bug ((test:*special* stream))
   test:*special*)
 (assert (eq *terminal-io* (pcl-type-declaration-method-bug *terminal-io*)))
+
+#+#.(cl:if (cl:eq sb-ext:*evaluator-mode* :compile) '(and) '(or))
 (assert (raises-error?
          (eval
           '(defmethod pcl-type-declaration-method-bug ((test:*special* stream))
index 9684221..0d3269b 100644 (file)
                      bashed-dst)
              (return-from test-copy-bashing nil))))))))
 
+;; Too slow for the interpreter
+#+#.(cl:if (cl:eq sb-ext:*evaluator-mode* :compile) '(and) '(or))
 (loop for i = 1 then (* i 2) do
      ;; the bare '32' here is fairly arbitrary; '8' provides a good
      ;; range of lengths over which to fill and copy, which should tease
index af467d7..bac6d51 100644 (file)
 ;;; Test derivation of LOG{AND,IOR,XOR} bounds for unsigned arguments.
 ;;;
 ;;; Fear the Loop of Doom!
+;;;
+;;; (In fact, this is such a fearsome loop that executing it with the
+;;; evaluator would take ages... Disable it under those circumstances.)
+#+#.(cl:if (cl:eq sb-ext:*evaluator-mode* :compile) '(and) '(or))
 (let* ((bits 5)
        (size (ash 1 bits)))
   (flet ((brute-force (a b c d op minimize)
index 041ec82..03a45c0 100644 (file)
@@ -17,4 +17,4 @@
 ;;; checkins which aren't released. (And occasionally for internal
 ;;; versions, especially for internal versions off the main CVS
 ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
-"0.9.16.26"
+"0.9.16.27"