git.fiddlerwoaroof.com
Raw Blame History
;;; This file contains general error handling routines.

;;; This is the general error handler.  It has three arguments: an
;;; id, error type, and an error message.  The message is a list of
;;; format, arglist combinations.

;;; The error types are:
;;;   warning       -> control returns and compilation proceeds
;;;                    The message may be suppressed
;;;   recoverable   -> control returns and compilation proceeds
;;;   phase         -> control returns but compilation is aborted
;;;                         after the phase in *abort-point*.
;;;   fatal         -> control goes back to the top level
;;;   internal      -> enters the break loop or does a fatal error

;;; Two globals control error behavior:
;;;   *break-on-error?* enter the break loop on any error
;;;   *never-break?* never enter the break loop, even for internal errors.

;;; The global *error-output-port* controls where errors are printer.

;;; The strategy here is to first write a banner message based on the id and
;;; type, write out the messages, and then take action depending on the type.

(define *in-error-handler?* '#f)

(define (haskell-error id type messages)
  (format *error-output-port* "~&[~A] ~A in phase ~A:~%"
	  id (err-type->banner type) (dynamic *phase*))
  (dolist (m messages)
    (apply (function format) *error-output-port* m)
    (fresh-line *error-output-port*))
  (maybe-show-context (dynamic *context*))
  (if (dynamic *in-error-handler?*)
      (error "Recursive error in haskell-error.")
      (begin
        (dynamic-let ((*in-error-handler?*  '#t))
	  (cond (*break-on-error?*
		 (haskell-breakpoint))
		((eq? type 'internal)
		 (if *never-break?*
		     (abort-compilation)
		     (haskell-breakpoint)))
		((eq? type 'fatal)
		 (abort-compilation))
		((eq? type 'phase)
		 (halt-compilation))))
	(when (and (memq type '(recoverable phase))
		   (dynamic *recoverable-error-handler*))
	  (funcall (dynamic *recoverable-error-handler*)))
	'ok)))

(define (err-type->banner err-type)
  (cond ((eq? err-type 'warning)
	 "Warning")
	((eq? err-type 'recoverable)
	 "Recoverable error")
	((eq? err-type 'phase)
	 "Phase error")
	((eq? err-type 'fatal)
	 "Fatal error")	
	((eq? err-type 'internal)
	 "Internal-error")
	(else "???")))

(define (maybe-show-context context)
  (when context
    (with-slots source-pointer (line file) (ast-node-line-number context)
      (fresh-line *error-output-port*)
      (format *error-output-port* "Error occurred at line ~A in file ~A.~%"
         line (filename-name file)))))

;;; A few entry points into the error system.
;;; As a matter of convention, there should be a signaling function defined
;;; for each specific error condition that calls one of these functions.
;;; Error messages should be complete sentences with proper punctuation
;;; and capitalization.  The signaling function should use the message
;;; to report the error and not do any printing of its own.

(define (fatal-error id . msg)
 (haskell-error id 'fatal (list msg)))

(define (haskell-warning id . msg)
 (haskell-error id 'warning (list msg)))

(define (recoverable-error id . msg)
 (haskell-error id 'recoverable (list msg)))

(define (compiler-error id . msg)
 (haskell-error id 'internal (list msg)))

(define (phase-error id . msg)
 (haskell-error id 'phase (list msg)))

;;; This function puts the compiler into the lisp breakloop.  this may
;;; want to fiddle the programming envoronment someday.

(define (haskell-breakpoint)
 (error "Haskell breakpoint."))


;;; This deals with error at runtime

(define (haskell-runtime-error msg)
  (format '#t "~&Haskell runtime abort.~%~A~%" msg)
  (funcall (dynamic *runtime-abort*)))

;; Some common error handlers

(define (signal-unknown-file-type filename)
  (fatal-error 'unknown-file-type
    "The filename ~a has an unknown file type."
    filename))

(define (signal-file-not-found filename)
  (fatal-error 'file-not-found
    "The file ~a doesn't exist."
    filename))