;;; general case of EVAL (except in that it can't handle toplevel
;;; EVAL-WHEN magic properly): Delegate to #'COMPILE.
(defun %eval (expr lexenv)
- (funcall (sb!c:compile-in-lexenv
- (gensym "EVAL-TMPFUN-")
- `(lambda ()
- ,expr)
- lexenv)))
+ ;; FIXME: It might be nice to quieten the toplevel by muffling
+ ;; warnings generated by this compilation (since we're about to
+ ;; execute the results irrespective of the warnings). We might want
+ ;; to be careful about not muffling warnings arising from inner
+ ;; evaluations/compilations, though [e.g. the ignored variable in
+ ;; (DEFUN FOO (X) 1)]. -- CSR, 2003-05-13
+ (let ((fun (sb!c:compile-in-lexenv (gensym "EVAL-TMPFUN-")
+ `(lambda ()
+ ,expr)
+ lexenv)))
+ (funcall fun)))
;;; Handle PROGN and implicit PROGN.
(defun eval-progn-body (progn-body lexenv)
(defun eval-in-lexenv (original-exp lexenv)
(declare (optimize (safety 1)))
;; (aver (lexenv-simple-p lexenv))
- (let ((exp (macroexpand original-exp lexenv)))
- (typecase exp
- (symbol
- (ecase (info :variable :kind exp)
- (:constant
- (values (info :variable :constant-value exp)))
- ((:special :global)
- (symbol-value exp))
- ;; FIXME: This special case here is a symptom of non-ANSI
- ;; weirdness in SBCL's ALIEN implementation, which could
- ;; cause problems for e.g. code walkers. It'd probably be
- ;; good to ANSIfy it by making alien variable accessors into
- ;; ordinary forms, e.g. (SB-UNIX:ENV) and (SETF SB-UNIX:ENV),
- ;; instead of magical symbols, e.g. plain SB-UNIX:ENV. Then
- ;; if the old magical-symbol syntax is to be retained for
- ;; compatibility, it can be implemented with
- ;; DEFINE-SYMBOL-MACRO, keeping the code walkers happy.
- (:alien
- (%eval original-exp lexenv))))
- (list
- (let ((name (first exp))
- (n-args (1- (length exp))))
- (case name
- ((function)
- (unless (= n-args 1)
- (error "wrong number of args to FUNCTION:~% ~S" exp))
- (let ((name (second exp)))
- (if (and (legal-fun-name-p name)
- (not (consp (let ((sb!c:*lexenv* lexenv))
- (sb!c:lexenv-find name funs)))))
- (fdefinition name)
- (%eval original-exp lexenv))))
- ((quote)
- (unless (= n-args 1)
- (error "wrong number of args to QUOTE:~% ~S" exp))
- (second exp))
- (setq
- (unless (evenp n-args)
- (error "odd number of args to SETQ:~% ~S" exp))
- (unless (zerop n-args)
- (do ((name (cdr exp) (cddr name)))
- ((null name)
- (do ((args (cdr exp) (cddr args)))
- ((null (cddr args))
- ;; We duplicate the call to SET so that the
- ;; correct value gets returned.
- (set (first args) (eval (second args))))
- (set (first args) (eval (second args)))))
- (let ((symbol (first name)))
- (case (info :variable :kind symbol)
- ;; FIXME: I took out the *TOPLEVEL-AUTO-DECLARE*
- ;; test here, and removed the *TOPLEVEL-AUTO-DECLARE*
- ;; variable; the code should now act as though that
- ;; variable is NIL. This should be tested..
- (:special)
- (t (return (%eval original-exp lexenv))))))))
- ((progn)
- (eval-progn-body (rest exp) lexenv))
- ((eval-when)
- ;; FIXME: DESTRUCTURING-BIND returns ARG-COUNT-ERROR
- ;; instead of PROGRAM-ERROR when there's something wrong
- ;; with the syntax here (e.g. missing SITUATIONS). This
- ;; could be fixed by hand-crafting clauses to catch and
- ;; report each possibility, but it would probably be
- ;; cleaner to write a new macro
- ;; DESTRUCTURING-BIND-PROGRAM-SYNTAX which does
- ;; DESTRUCTURING-BIND and promotes any mismatch to
- ;; PROGRAM-ERROR, then to use it here and in (probably
- ;; dozens of) other places where the same problem arises.
- (destructuring-bind (eval-when situations &rest body) exp
- (declare (ignore eval-when))
- (multiple-value-bind (ct lt e)
- (sb!c:parse-eval-when-situations situations)
- ;; CLHS 3.8 - Special Operator EVAL-WHEN: The use of
- ;; the situation :EXECUTE (or EVAL) controls whether
- ;; evaluation occurs for other EVAL-WHEN forms; that
- ;; is, those that are not top level forms, or those in
- ;; code processed by EVAL or COMPILE. If the :EXECUTE
- ;; situation is specified in such a form, then the
- ;; body forms are processed as an implicit PROGN;
- ;; otherwise, the EVAL-WHEN form returns NIL.
- (declare (ignore ct lt))
- (when e
- (eval-progn-body body lexenv)))))
- ((locally)
- (multiple-value-bind (body decls) (parse-body (rest exp) nil)
- (let ((lexenv
- ;; KLUDGE: Uh, yeah. I'm not anticipating
- ;; winning any prizes for this code, which was
- ;; written on a "let's get it to work" basis.
- ;; These seem to be the variables that need
- ;; bindings for PROCESS-DECLS to work
- ;; (*FREE-FUNS* and *FREE-VARS* so that
- ;; references to free functions and variables in
- ;; the declarations can be noted;
- ;; *UNDEFINED-WARNINGS* so that warnings about
- ;; undefined things can be accumulated [and then
- ;; thrown away, as it happens]). -- CSR, 2002-10-24
- (let ((sb!c:*lexenv* lexenv)
- (sb!c::*free-funs* (make-hash-table :test 'equal))
- (sb!c::*free-vars* (make-hash-table :test 'eq))
- (sb!c::*undefined-warnings* nil))
- (sb!c::process-decls decls
- nil nil
- (sb!c::make-continuation)
- lexenv))))
- (eval-progn-body body lexenv))))
- ((macrolet)
- (destructuring-bind (definitions &rest body)
- (rest exp)
- ;; FIXME: shared code with FUNCALL-IN-FOOMACROLET-LEXENV
- (declare (type list definitions))
- (unless (= (length definitions)
- (length (remove-duplicates definitions :key #'first)))
- (style-warn "duplicate definitions in ~S" definitions))
- (let ((lexenv
- (sb!c::make-lexenv
- :default lexenv
- :funs (mapcar
- (sb!c::macrolet-definitionize-fun
- :eval
- ;; I'm not sure that this is the correct
- ;; LEXENV to be compiling local macros
- ;; in...
- lexenv)
- definitions))))
- (eval-in-lexenv `(locally ,@body) lexenv))))
- ((symbol-macrolet)
- (destructuring-bind (definitions &rest body)
- (rest exp)
- ;; FIXME: shared code with FUNCALL-IN-FOOMACROLET-LEXENV
- (declare (type list definitions))
- (unless (= (length definitions)
- (length (remove-duplicates definitions :key #'first)))
- (style-warn "duplicate definitions in ~S" definitions))
- (let ((lexenv
- (sb!c::make-lexenv
- :default lexenv
- :vars (mapcar
- (sb!c::symbol-macrolet-definitionize-fun
- :eval)
- definitions))))
- (eval-in-lexenv `(locally ,@body) lexenv))))
- (t
- (if (and (symbolp name)
- (eq (info :function :kind name) :function))
- (collect ((args))
- (dolist (arg (rest exp))
- (args (eval-in-lexenv arg lexenv)))
- (apply (symbol-function name) (args)))
- (%eval exp lexenv))))))
- (t
- exp))))
+ (handler-bind
+ ((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 a
+ ;; program error straight away.
+ (invoke-restart 'sb!c::signal-program-error)))))
+ (let ((exp (macroexpand original-exp lexenv)))
+ (typecase exp
+ (symbol
+ (ecase (info :variable :kind exp)
+ (:constant
+ (values (info :variable :constant-value exp)))
+ ((:special :global)
+ (symbol-value exp))
+ ;; FIXME: This special case here is a symptom of non-ANSI
+ ;; weirdness in SBCL's ALIEN implementation, which could
+ ;; cause problems for e.g. code walkers. It'd probably be
+ ;; good to ANSIfy it by making alien variable accessors
+ ;; into ordinary forms, e.g. (SB-UNIX:ENV) and (SETF
+ ;; SB-UNIX:ENV), instead of magical symbols, e.g. plain
+ ;; SB-UNIX:ENV. Then if the old magical-symbol syntax is to
+ ;; be retained for compatibility, it can be implemented
+ ;; with DEFINE-SYMBOL-MACRO, keeping the code walkers
+ ;; happy.
+ (:alien
+ (%eval original-exp lexenv))))
+ (list
+ (let ((name (first exp))
+ (n-args (1- (length exp))))
+ (case name
+ ((function)
+ (unless (= n-args 1)
+ (error "wrong number of args to FUNCTION:~% ~S" exp))
+ (let ((name (second exp)))
+ (if (and (legal-fun-name-p name)
+ (not (consp (let ((sb!c:*lexenv* lexenv))
+ (sb!c:lexenv-find name funs)))))
+ (fdefinition name)
+ (%eval original-exp lexenv))))
+ ((quote)
+ (unless (= n-args 1)
+ (error "wrong number of args to QUOTE:~% ~S" exp))
+ (second exp))
+ (setq
+ (unless (evenp n-args)
+ (error "odd number of args to SETQ:~% ~S" exp))
+ (unless (zerop n-args)
+ (do ((name (cdr exp) (cddr name)))
+ ((null name)
+ (do ((args (cdr exp) (cddr args)))
+ ((null (cddr args))
+ ;; We duplicate the call to SET so that the
+ ;; correct value gets returned.
+ (set (first args) (eval (second args))))
+ (set (first args) (eval (second args)))))
+ (let ((symbol (first name)))
+ (case (info :variable :kind symbol)
+ ;; FIXME: I took out the *TOPLEVEL-AUTO-DECLARE*
+ ;; test here, and removed the
+ ;; *TOPLEVEL-AUTO-DECLARE* variable; the code
+ ;; should now act as though that variable is
+ ;; NIL. This should be tested..
+ (:special)
+ (t (return (%eval original-exp lexenv))))))))
+ ((progn)
+ (eval-progn-body (rest exp) lexenv))
+ ((eval-when)
+ ;; FIXME: DESTRUCTURING-BIND returns ARG-COUNT-ERROR
+ ;; instead of PROGRAM-ERROR when there's something wrong
+ ;; with the syntax here (e.g. missing SITUATIONS). This
+ ;; could be fixed by hand-crafting clauses to catch and
+ ;; report each possibility, but it would probably be
+ ;; cleaner to write a new macro
+ ;; DESTRUCTURING-BIND-PROGRAM-SYNTAX which does
+ ;; DESTRUCTURING-BIND and promotes any mismatch to
+ ;; PROGRAM-ERROR, then to use it here and in (probably
+ ;; dozens of) other places where the same problem
+ ;; arises.
+ (destructuring-bind (eval-when situations &rest body) exp
+ (declare (ignore eval-when))
+ (multiple-value-bind (ct lt e)
+ (sb!c:parse-eval-when-situations situations)
+ ;; CLHS 3.8 - Special Operator EVAL-WHEN: The use of
+ ;; the situation :EXECUTE (or EVAL) controls whether
+ ;; evaluation occurs for other EVAL-WHEN forms; that
+ ;; is, those that are not top level forms, or those
+ ;; in code processed by EVAL or COMPILE. If the
+ ;; :EXECUTE situation is specified in such a form,
+ ;; then the body forms are processed as an implicit
+ ;; PROGN; otherwise, the EVAL-WHEN form returns NIL.
+ (declare (ignore ct lt))
+ (when e
+ (eval-progn-body body lexenv)))))
+ ((locally)
+ (multiple-value-bind (body decls) (parse-body (rest exp) nil)
+ (let ((lexenv
+ ;; KLUDGE: Uh, yeah. I'm not anticipating
+ ;; winning any prizes for this code, which was
+ ;; written on a "let's get it to work" basis.
+ ;; These seem to be the variables that need
+ ;; bindings for PROCESS-DECLS to work
+ ;; (*FREE-FUNS* and *FREE-VARS* so that
+ ;; references to free functions and variables
+ ;; in the declarations can be noted;
+ ;; *UNDEFINED-WARNINGS* so that warnings about
+ ;; undefined things can be accumulated [and
+ ;; then thrown away, as it happens]). -- CSR,
+ ;; 2002-10-24
+ (let ((sb!c:*lexenv* lexenv)
+ (sb!c::*free-funs* (make-hash-table :test 'equal))
+ (sb!c::*free-vars* (make-hash-table :test 'eq))
+ (sb!c::*undefined-warnings* nil))
+ (sb!c::process-decls decls
+ nil nil
+ (sb!c::make-continuation)
+ lexenv))))
+ (eval-progn-body body lexenv))))
+ ((macrolet)
+ (destructuring-bind (definitions &rest body)
+ (rest exp)
+ ;; FIXME: shared code with
+ ;; FUNCALL-IN-FOOMACROLET-LEXENV
+ (declare (type list definitions))
+ (unless (= (length definitions)
+ (length (remove-duplicates definitions
+ :key #'first)))
+ (style-warn "duplicate definitions in ~S" definitions))
+ (let ((lexenv
+ (sb!c::make-lexenv
+ :default lexenv
+ :funs (mapcar
+ (sb!c::macrolet-definitionize-fun
+ :eval
+ ;; I'm not sure that this is the
+ ;; correct LEXENV to be compiling
+ ;; local macros in...
+ lexenv)
+ definitions))))
+ (eval-in-lexenv `(locally ,@body) lexenv))))
+ ((symbol-macrolet)
+ (destructuring-bind (definitions &rest body)
+ (rest exp)
+ ;; FIXME: shared code with
+ ;; FUNCALL-IN-FOOMACROLET-LEXENV
+ (declare (type list definitions))
+ (unless (= (length definitions)
+ (length (remove-duplicates definitions
+ :key #'first)))
+ (style-warn "duplicate definitions in ~S" definitions))
+ (let ((lexenv
+ (sb!c::make-lexenv
+ :default lexenv
+ :vars (mapcar
+ (sb!c::symbol-macrolet-definitionize-fun
+ :eval)
+ definitions))))
+ (eval-in-lexenv `(locally ,@body) lexenv))))
+ (t
+ (if (and (symbolp name)
+ (eq (info :function :kind name) :function))
+ (collect ((args))
+ (dolist (arg (rest exp))
+ (args (eval-in-lexenv arg lexenv)))
+ (apply (symbol-function name) (args)))
+ (%eval exp lexenv))))))
+ (t
+ exp)))))
\f
;;; miscellaneous full function definitions of things which are
;;; ordinarily handled magically by the compiler
;;; a function that is called to unwind out of COMPILER-ERROR
(declaim (type (function () nil) *compiler-error-bailout*))
-(defvar *compiler-error-bailout*
- (lambda () (error "COMPILER-ERROR with no bailout")))
+(defvar *compiler-error-bailout*)
;;; an application programmer's error caught by the compiler
;;;
;;; and turned into diagnostic output and a FAILURE-P return value
;;; from COMPILE or COMPILE-FILE. Bugs in SBCL itself throw us into
;;; the debugger.
-(define-condition compiler-error (simple-error) ())
+;;;
+;;; A further word or two of explanation might be warranted here,
+;;; since I (CSR) have spent the last day or so wandering in a
+;;; confused daze trying to get this to behave nicely before finally
+;;; hitting on the right solution.
+;;;
+;;; These objects obey a slightly involved protocol in order to
+;;; achieve the right dynamic behaviour. If we signal a
+;;; COMPILER-ERROR from within the compiler, we want that the
+;;; outermost call to COMPILE/COMPILE-FILE cease attempting to compile
+;;; the code in question and instead compile a call to signal a
+;;; PROGRAM-ERROR. This is achieved by resignalling the condition
+;;; from within the handler, so that the condition travels up the
+;;; handler stack until it finds the outermost handler. Why the
+;;; outermost? Well, COMPILE-FILE could call EVAL from an EVAL-WHEN,
+;;; which could recursively call COMPILE, which could then signal an
+;;; error; we want the inner EVAL not to fail so that we can go on
+;;; compiling, so it's the outer COMPILE-FILE that needs to replace
+;;; the erroneous call with a call to ERROR.
+;;;
+;;; This resignalling up the stack means that COMPILER-ERROR should
+;;; not be a generalized instance of ERROR, as otherwise code such as
+;;; (IGNORE-ERRORS (DEFGENERIC IF (X))) will catch and claim to handle
+;;; the COMPILER-ERROR. So we make COMPILER-ERROR inherit from
+;;; SIMPLE-CONDITION and SERIOUS-CONDITION instead, as of
+;;; sbcl-0.8alpha.0.2x, so that unless the user claims to be able to
+;;; handle SERIOUS-CONDITION (and if he does, he deserves what's going
+;;; to happen :-)
+;;;
+;;; So, what if we're not inside the compiler, then? Well, in that
+;;; case we're in the evaluator, so we want to convert the
+;;; COMPILER-ERROR into a PROGRAM-ERROR and signal it immediately. We
+;;; have to signal the PROGRAM-ERROR from the dynamic environment of
+;;; attempting to evaluate the erroneous code, and not from any
+;;; exterior handler, so that user handlers for PROGRAM-ERROR and
+;;; ERROR stand a chance of running, in e.g. (IGNORE-ERRORS
+;;; (DEFGENERIC IF (X))). So this is where the SIGNAL-PROGRAM-ERROR
+;;; restart comes in; the handler in EVAL-IN-LEXENV chooses this
+;;; restart if it believes that the compiler is not present (which it
+;;; tests using the BOUNDPness of *COMPILER-ERROR-BAILOUT*). The
+;;; restart executes in the dynamic environment of the original
+;;; COMPILER-ERROR call, and all is well.
+;;;
+;;; CSR, 2003-05-13
+(define-condition compiler-error (simple-condition serious-condition) ())
;;; Signal the appropriate condition. COMPILER-ERROR calls the bailout
;;; function so that it never returns (but compilation continues).
:format-control format-string
:format-arguments format-args))
(defun compiler-error (format-string &rest format-args)
- (cerror "Replace form with call to ERROR."
- 'compiler-error
- :format-control format-string
- :format-arguments format-args)
- (funcall *compiler-error-bailout*)
- (bug "Control returned from *COMPILER-ERROR-BAILOUT*."))
+ (restart-case
+ (progn
+ (cerror "Replace form with call to ERROR."
+ 'compiler-error
+ :format-control format-string
+ :format-arguments format-args)
+ (funcall *compiler-error-bailout*)
+ (bug "Control returned from *COMPILER-ERROR-BAILOUT*."))
+ (signal-program-error ()
+ (error 'simple-program-error
+ :format-control format-string
+ :format-arguments format-args))))
(defun compiler-warn (format-string &rest format-args)
(apply #'warn format-string format-args)
(values))