caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: "Krishnaswami, Neel" <neelk@cswcasa.com>
To: caml-list@inria.fr
Subject: Re: [Caml-list] Error Reporting
Date: Thu, 27 Sep 2001 09:32:26 -0400	[thread overview]
Message-ID: <B1E4D3274D57D411BE8400D0B783FF322E872A@exchange1.cswv.com> (raw)

David McClain [mailto:barabh@qwest.net] wrote:
> 
> I am looking for some ideas on handling program error reporting for
> a compiler that is built largely on CPS. Since there are no stack
> frames to crawl for traceback, I have a first cut based on keeping a
> finite length queue of last visited closures. But this becomes
> problematic when there are circularities in the code, i.e.,
> recursion. Also, I think I have way too much error tracking going on
> at each incremental execution step and so runtime performance is cut
> back compared to what it could be.

Since you're using CPS, it seems like you have most of the infrastructure 
you need already. If you track source locations from parsing down to code
generation, you can place a source location into each frame as you allocate
it. Then, when you have an error, you can simply walk back down the
contination, emitting the source location of each frame. Since the
continuation represents the rest of the computation, you automatically get 
the exact execution context of the erroneous statement. 

The overhead for this is one word per frame, plus initializing it 
at each continuation allocation. This is probably acceptable unless
you are writing a really high-performance compiler. Also, tail-call
optimization will make the traceback only approximate, but this is
IME not a big deal in practice. 

Below is a small CPS-style lambda calculus interpreter that uses this 
idea to generate tracebacks of its evaluation. It's a treecode 
interpreter, but the ideas ought to carry over directly to bytecode too. 

-*-*-*-

type var = string
type pos = int

(* Expressions carry the source location with them *)

type expr =
  | Lit of pos * int
  | Var of pos * var
  | Fun of pos * var * expr
  | App of pos * expr * expr

(*
   Continuation frames have the position as an extra argument, so that
   the continuation as a whole contains the backtrace info. Primitive
   functions take the continuation as an extra argument so that they
   can generate a backtrace on an error.
*)

type denot =
  | Int of int
  | Closure of env * var * expr
  | Primop of (denot -> cont -> denot)
and frame =
  | KFun of pos * env * expr
  | KArg of pos * env * denot
and env = (var * denot) list
and cont = frame list

(*
   Here are the guts of the machinery to generate a backtrace. We
   need to extract a position from a frame and then just map over
   the whole continuation to get the backtrace.
*)

exception Eval_error of string * pos list

let framepos frame =
  match frame with
  | KFun(pos, _, _) -> pos
  | KArg(pos, _, _) -> pos

let backtrace k = List.map framepos k 

(* 
  We augment variable lookup so that failed lookups generate backtraces.
  If you check statically for unbound variables this is unneccesary.
*)

let bind v d r = (v,d) :: r

let lookup v r k =
  try
    List.assoc v r 
  with
    Not_found -> raise (Eval_error("Unbound variable " ^ v, backtrace k))


(*
   In the interpreter proper, the only place where a backtrace is 
   generated is when an argument is applied to a non-function. If an 
   if-statement is added, then that should be too. Likewise, if 
   exceptions are added then an uncaught exception should generate
   one also.
*)

let rec eval e r k =
  match e with
  | Lit(pos, n) ->
      next (Int n) k
  | Var(pos, v) ->
      next (lookup v r k) k 
  | Fun(pos, formal, body) ->
      next (Closure(r, formal, body)) k
  | App(pos, f, arg) ->
      eval f r (KFun(pos, r, arg) :: k)
and next d k =
  match k with
  | [] -> d
  | KFun(pos, r, arg) :: k' ->
      eval arg r k'
  | KArg(pos, r, func) :: k' ->
      (match func with
      | Int _ ->
          raise (Eval_error("Not a function!", backtrace k))
      | Closure(r, formal, body) ->
          eval body (bind formal d r) k'
      | Primop f ->
          next (f d k) k')
--
Neel Krishnaswami
neelk@cswcasa.com
-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr


             reply	other threads:[~2001-09-27 13:28 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2001-09-27 13:32 Krishnaswami, Neel [this message]
2001-09-27 20:36 ` David McClain
2001-09-28 15:24   ` Frank Atanassow
2001-09-28 16:41     ` David McClain
2001-09-27 22:34 ` David McClain
  -- strict thread matches above, loose matches on Subject: below --
2001-09-28 16:49 David McClain
2001-10-01  0:52 ` John Prevost
2001-10-01 14:17 ` Frank Atanassow
2001-10-01 15:44   ` David McClain
2001-09-27 13:00 Damien Doligez
2001-09-26 22:55 David McClain

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=B1E4D3274D57D411BE8400D0B783FF322E872A@exchange1.cswv.com \
    --to=neelk@cswcasa.com \
    --cc=caml-list@inria.fr \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).