Ilya Zakharevich on Wed, 12 Mar 2003 10:10:21 -0800


[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

Re: trap()ing user errors


On Wed, Mar 12, 2003 at 04:35:28PM +0100, Bill Allombert wrote:
> Hello PARI-Dev,
> 
> It seems that currently trap() cannot catch user error raised with
> error(). It could be useful to add that. Also it would not
> cost much to support all the errors reported by PARI or at least
> a 'none of the above' error code. 
> 
> Beside I do not like the syntax/semantic of trap:
> Trap currently has two very different use:
> 1) Install an error handler. 
> 2) Execute an instruction in error recovery mode.
> 
> I would prefer that the second usage be covered by a 
> iferr(SEQ,REC{,error})
> so that trap() will not need a third parameter anymore.

I think I sent a patch for this during the last round (at least in C
mode).  But all this is cosmetic problems.

Here comes the *real* problem with the current modus operandi: one
can't rethrow().  Given a possibility of rethrow(), one could forget
about the current (IMO, extremely lousy) scheme, and always trap all
errors, then rethrow() if needed.

Below are the problems with implementing rethrow():  One could easily
modify the pari_err() logic to save its parameters in a safe place (or
better a stack of safe places), and then support

  err(rethrow)

[which would pop this stack of safe places].  However, there are
several places where the error string is assembled on the stack.
[Examples below.]  Thus actually err() will need to *copy* all the
char* parameters.  This would significantly decrease the performance,
thus may make the whole err/trap scheme infeasible.

> As a general note, I think it better if functions return errors
> as part of their output whenever is convenient, instead of raising
> an error.

Having a possibility of non-local return is very important.

> This ask for more work of the caller, but this avoid 
> relying on trap when error catching is needed.

Given the current scheme (when reliance on trap() is very shaky), one
*should* rely on non-local returns if the algorithm asks for it.

> For example, sqrtn(Mod(a,p),3) raise an error id a is not a cube mod p.
> Unfortunately testing if it is take almost the same time that doing
> the computation so it is a waste to have to write 
> if(Mod(a,p)^((p-1)/3)!=1,next); sqrtn(Mod(a,p),3)
> if we want to avoid using trap.

Design a better trap().  Then do not hesitate to use it.

Hope this helps,
Ilya

P.S.  Here is my list of suspicious err()s which have arguments on stack.

   buch2.c:
      char str[64]; sprintf(str,"buchall (%s)",precpb);
      err(warnprec,str,PRECREG);

   gp.c anal.c: a lot

[These found by pfind -alllines src /\.c$/ "=~ /\berr\s*\(\s*\w+\s*,\s*[^\"\s]/"
+ manual filtering.]

Now

  #!/bin/sh
  pfind.pl -alllines src '/\.c$/' '=~ /\berr\s*\(\s*\w+\s*,\s*\"[^\"]*\"\s*,\s*[^\"\s]/' > o
  grep "%s" o >oo-s
  grep -v "%s" o |
    grep -v "\<oper[if]\|warnprec\>" |
    grep -v '%.*" *, *[a-zA-Z][a-zA-Z.]* *)' |
    grep -v '%.*%.*" *, *[a-zA-Z][a-zA-Z.]* *, *[a-zA-Z][a-zA-Z.]* *)' |
    grep -v '(member *, *".*" *, *mark\.member *, *mark\.start *)' |
    grep -v '(talker2 *, *".*" *, *mark\.[a-z]* *, *mark\.[a-z]* *)' >oo-no-s

    + manual filtering

Finds:

      src/gp/gp.c :   if (*p) err(talker2,"I was expecting an integer here", s, s);
      src/gp/gp.c :   if (n < 0) err(talker2,"integer too large in get_int",s,s);
      src/gp/gp.c :   if (*p == '-') err(talker2,"arguments must be positive integers",s,s);
      src/gp/gp.c :       err(talker2,"default: inexistent format",v,v);
      src/gp/gp.c :       if (*s != ']') err(talker2,"expected character: ']'",s, *st);
      src/gp/gp.c :       err(talker2,"no such section in help: ?",s,s);
      src/language/anal.c :       if (*s != ',') err(talker2, "missing comma", old, code);
      src/language/anal.c :     case 'v': err(talker2, "this code has to come first", s-1, code);
      src/language/anal.c :     default: err(talker2, "unknown parser code", s-1, code);
      src/language/anal.c :     if (*s) err(talker2,"not a valid identifier", s, name);
      src/language/anal.c :     if (!GP_DATA) err(talker2,"history not available", old, mark.start);
      src/language/anal.c :       err(talker2,"not a suitable VECSMALL component",old,mark.start);
      src/language/anal.c :         err(talker2,"global variable: ",old , mark.start);
      src/language/anal.c :             default: err(talker2,"symbol already in use",ch1,mark.start);
      src/language/anal.c :       if (nb > 8) err(talker2,"exponent too large",old,mark.start);
      src/language/anal.c :     if (strict) err(talker2,"unused characters", analyseur, c);
      src/language/anal.c : 	  err(talker2,"; or ] expected",old,mark.start);
      src/language/anal.c :       err(talker2,"global variable not allowed", old,mark.start);
      src/language/es.c :     if (fclose(f->file)) err(warnfile, "close", f->name);
      src/language/es.c :       if (fclose(f->file)) err(warnfile, "close", f->name);
      src/language/es.c :       if (unlink(f->name)) err(warnfile, "delete", f->name);
      src/language/es.c :       if (pclose(f->file) < 0) err(warnfile, "close pipe", f->name);
      src/language/es.c :   if (!p) err(talker2,"unknown user ",s,s-1);
      src/language/es.c :   err(openfiler,"input",name0);
      src/language/es.c :     if (!f) err(openfiler,"output",name);
      src/language/es.c :   if (!f) err(openfiler,"binary output",name);
      src/language/es.c :     err(talker2, "I can't see into the future", old, entry);
      src/language/es.c :     err(talker2, "I can't remember before the big bang", old, entry);

      src/basemath/base1.c :     err(talker,"missing units in %s", f);
      src/basemath/base1.c :     if (typ(x[k])!=t_INT) err(talker,"polynomial not in Z[X] in %s",s);
      src/basemath/base2.c :     err(talker,"not a pseudo-matrix in %s", s);
      src/basemath/buch2.c :     err(warner,"%s, not given",s);
      src/basemath/buch3.c :     if (!hnfdivide(H, D)) err(talker,"incorrect subgroup in %s", s);
      src/gp/gp.c : { err(talker,"[secure mode]: can't modify '%s' default (to %s)",d,v); }
      src/gp/gp.c :           err(warner,"broken prettyprinter: '%s'",v);
      src/gp/gp.c :   err(talker,"unknown default: %s",s);
      src/gp/gp.c :   if ((flag & h_RL) == 0) err(talker, "%s: %s", s1, s2);
      src/gp/gp.c :         err(talker,"Texmacs_stdin command %s not implemented", c.cmd);
      src/gp/gp.c :     err(talker, "[secure mode]: system commands not allowed\nTried to run '%s'",s);
      src/gp/highlvl.c :     if (lib) err(talker,"couldn't open dynamic library '%s'",lib);
      src/gp/highlvl.c :     if (lib) err(talker,"can't find symbol '%s' in library '%s'",name,lib);
      src/gp/highlvl.c :     err(talker,"can't find symbol '%s' in dynamic symbol table of process",name);
      src/gp/highlvl.c :     if (lib) err(talker,"couldn't open dynamic library '%s'",lib);
      src/gp/highlvl.c :     if (lib) err(talker,"can't find symbol '%s' in library '%s'",name,lib);
      src/gp/highlvl.c :     err(talker,"can't find symbol '%s' in dynamic symbol table of process",name);
      src/gp/highlvl.c :     if (*s) err(talker,"Unknown type: %s",s);
      src/gp/highlvl.c :   err(talker,"Unknown type: t_%s",st);
      src/language/anal.c :       err(talker,"[install] identifier '%s' already in use", name);
      src/language/anal.c :     err(warner, "[install] updating '%s' prototype; module not reloaded", name);
      src/language/anal.c :     if (doerr) err(talker,"identifier already in use: %s", s);
      src/language/anal.c :     err(talker, "%s already exists with incompatible valence", s);
      src/language/anal.c :     err(warner, "unused characters: %s", s);
      src/language/anal.c :       err(warner,"using obsolete function %s",ep->name);
      src/language/es.c :   err(warner,"broken prettyprinter: '%s'",pp->cmd);
      src/language/es.c :   if (!f) err(talker, "could not open requested file %s", s);
      src/language/es.c :   if (fd == -1) err(talker,"tempfile %s already exists",s);
      src/language/es.c :   if (unlink(s)) err(warner, "I/O: can\'t remove file %s", s);
      src/language/es.c :   if (!file) err(talker,"[pipe:] '%s' failed",cmd);
      src/language/es.c :       err(warner,"undefined environment variable: %s",env);
      src/language/es.c :     err(warner,"skipping directory %s",name);
      src/language/es.c :         err(talker,"%s is a GP binary file. Please use writebin", name);
      src/language/es.c :     err(talker, "%s is not a GP binary file",name);
      src/language/es.c :     err(talker, "unexpected endianness in %s",name);
      src/language/es.c :     err(talker, "%s written by an incompatible version of GP",name);
      src/language/es.c :     err(warner,"%s is set (%s), but is not writeable", s,t);
      src/language/es.c :     err(warner,"%s is set (%s), but is not a directory", s,t);
      src/language/es.c :       err(talker,"couldn't find a suitable name for a tempfile (%s)",s);
      src/modules/mpqs.c : #define bummer(fn) err(talker, "error whilst writing to file %s", fn)
      src/modules/mpqs.c :       err(talker, "error whilst appending to file %s", f->name);
      src/modules/mpqs.c :     err(warner, "error whilst flushing file %s", f->name);
      src/modules/mpqs.c :     err(talker, "can\'t rename file %s to %s", TMP_str, REL_str);