Fixed bug in line info. when using 'not' operator

When creating code for a jump on a 'not' condition, the code generator
was removing an instruction (the OP_NOT) without adjusting its
corresponding line information.

This fix also added tests for this case and extra functionality in
the test library to debug line info. structures.
diff --git a/lcode.c b/lcode.c
index ab91561..d00038d 100644
--- a/lcode.c
+++ b/lcode.c
@@ -1,5 +1,5 @@
 /*
-** $Id: lcode.c,v 2.160 2018/03/16 14:22:09 roberto Exp roberto $
+** $Id: lcode.c $
 ** Code generator for Lua
 ** See Copyright Notice in lua.h
 */
@@ -309,10 +309,19 @@
 }
 
 
+/*
+** MAXimum number of successive Instructions WiTHout ABSolute line
+** information.
+*/
 #if !defined(MAXIWTHABS)
 #define MAXIWTHABS	120
 #endif
 
+
+/* limit for difference between lines in relative line info. */
+#define LIMLINEDIFF	0x80
+
+
 /*
 ** Save line info for a new instruction. If difference from last line
 ** does not fit in a byte, of after that many instructions, save a new
@@ -320,14 +329,15 @@
 ** in 'lineinfo' signals the existence of this absolute information.)
 ** Otherwise, store the difference from last line in 'lineinfo'.
 */
-static void savelineinfo (FuncState *fs, Proto *f, int pc, int line) {
+static void savelineinfo (FuncState *fs, Proto *f, int line) {
   int linedif = line - fs->previousline;
-  if (abs(linedif) >= 0x80 || fs->iwthabs++ > MAXIWTHABS) {
+  int pc = fs->pc - 1;  /* last instruction coded */
+  if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ > MAXIWTHABS) {
     luaM_growvector(fs->ls->L, f->abslineinfo, fs->nabslineinfo,
                     f->sizeabslineinfo, AbsLineInfo, MAX_INT, "lines");
     f->abslineinfo[fs->nabslineinfo].pc = pc;
     f->abslineinfo[fs->nabslineinfo++].line = line;
-    linedif = ABSLINEINFO;  /* signal there is absolute information */
+    linedif = ABSLINEINFO;  /* signal that there is absolute information */
     fs->iwthabs = 0;  /* restart counter */
   }
   luaM_growvector(fs->ls->L, f->lineinfo, pc, f->sizelineinfo, ls_byte,
@@ -338,6 +348,37 @@
 
 
 /*
+** Remove line information from the last instruction.
+** If line information for that instruction is absolute, set 'iwthabs'
+** above its max to force the new (replacing) instruction to have
+** absolute line info, too.
+*/
+static void removelastlineinfo (FuncState *fs) {
+  Proto *f = fs->f;
+  int pc = fs->pc - 1;  /* last instruction coded */
+  if (f->lineinfo[pc] != ABSLINEINFO) {  /* relative line info? */
+    fs->previousline -= f->lineinfo[pc];  /* last line saved */
+    fs->iwthabs--;
+  }
+  else {  /* absolute line information */
+    fs->nabslineinfo--;  /* remove it */
+    lua_assert(f->abslineinfo[fs->nabslineinfo].pc = pc);
+    fs->iwthabs = MAXIWTHABS + 1;  /* force next line info to be absolute */
+  }
+}
+
+
+/*
+** Remove the last instruction created, correcting line information
+** accordingly.
+*/
+static void removelastinstruction (FuncState *fs) {
+  removelastlineinfo(fs);
+  fs->pc--;
+}
+
+
+/*
 ** Emit instruction 'i', checking for array sizes and saving also its
 ** line information. Return 'i' position.
 */
@@ -346,9 +387,9 @@
   /* put new instruction in code array */
   luaM_growvector(fs->ls->L, f->code, fs->pc, f->sizecode, Instruction,
                   MAX_INT, "opcodes");
-  f->code[fs->pc] = i;
-  savelineinfo(fs, f, fs->pc, fs->ls->lastline);
-  return fs->pc++;
+  f->code[fs->pc++] = i;
+  savelineinfo(fs, f, fs->ls->lastline);
+  return fs->pc - 1;  /* index of new instruction */
 }
 
 
@@ -986,7 +1027,7 @@
   if (e->k == VRELOC) {
     Instruction ie = getinstruction(fs, e);
     if (GET_OPCODE(ie) == OP_NOT) {
-      fs->pc--;  /* remove previous OP_NOT */
+      removelastinstruction(fs);  /* remove previous OP_NOT */
       return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond);
     }
     /* else go through */
@@ -1572,23 +1613,12 @@
 
 
 /*
-** Change line information associated with current position. If that
-** information is absolute, just change it and correct 'previousline'.
-** Otherwise, restore 'previousline' to its value before saving the
-** current position and than saves the line information again, with the
-** new line.
+** Change line information associated with current position, by removing
+** previous info and adding it again with new line.
 */
 void luaK_fixline (FuncState *fs, int line) {
-  Proto *f = fs->f;
-  if (f->lineinfo[fs->pc - 1] == ABSLINEINFO) {
-    lua_assert(f->abslineinfo[fs->nabslineinfo - 1].pc == fs->pc - 1);
-    f->abslineinfo[fs->nabslineinfo - 1].line = line;
-    fs->previousline = line;
-  }
-  else {
-    fs->previousline -= f->lineinfo[fs->pc - 1];  /* undo previous info. */
-    savelineinfo(fs, f, fs->pc - 1, line);  /* redo it */
-  }
+  removelastlineinfo(fs);
+  savelineinfo(fs, fs->f, line);
 }
 
 
diff --git a/ltests.c b/ltests.c
index ac548a9..13717da 100644
--- a/ltests.c
+++ b/ltests.c
@@ -526,7 +526,8 @@
   OpCode o = GET_OPCODE(i);
   const char *name = opnames[o];
   int line = luaG_getfuncline(p, pc);
-  sprintf(buff, "(%4d) %4d - ", line, pc);
+  int lineinfo = (p->lineinfo != NULL) ? p->lineinfo[pc] : 0;
+  sprintf(buff, "(%2d - %4d) %4d - ", lineinfo, line, pc);
   switch (getOpMode(o)) {
     case iABC:
       sprintf(buff+strlen(buff), "%-12s%4d %4d %4d%s", name,
@@ -621,6 +622,24 @@
 }
 
 
+static int listabslineinfo (lua_State *L) {
+  Proto *p;
+  int i;
+  luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1),
+                 1, "Lua function expected");
+  p = getproto(obj_at(L, 1));
+  luaL_argcheck(L, p->abslineinfo != NULL, 1, "function has no debug info");
+  lua_createtable(L, 2 * p->sizeabslineinfo, 0);
+  for (i=0; i < p->sizeabslineinfo; i++) {
+    lua_pushinteger(L, p->abslineinfo[i].pc);
+    lua_rawseti(L, -2, 2 * i + 1);
+    lua_pushinteger(L, p->abslineinfo[i].line);
+    lua_rawseti(L, -2, 2 * i + 2);
+  }
+  return 1;
+}
+
+
 static int listlocals (lua_State *L) {
   Proto *p;
   int pc = cast_int(luaL_checkinteger(L, 2)) - 1;
@@ -1682,6 +1701,7 @@
   {"listcode", listcode},
   {"printcode", printcode},
   {"listk", listk},
+  {"listabslineinfo", listabslineinfo},
   {"listlocals", listlocals},
   {"loadlib", loadlib},
   {"checkpanic", checkpanic},
diff --git a/testes/errors.lua b/testes/errors.lua
index 63a7b74..19a7b6f 100644
--- a/testes/errors.lua
+++ b/testes/errors.lua
@@ -1,4 +1,4 @@
--- $Id: errors.lua,v 1.97 2017/11/28 15:31:56 roberto Exp $
+-- $Id: errors.lua $
 -- See Copyright Notice in file all.lua
 
 print("testing errors")
@@ -299,7 +299,7 @@
 local function lineerror (s, l)
   local err,msg = pcall(load(s))
   local line = string.match(msg, ":(%d+):")
-  assert((line and line+0) == l)
+  assert(tonumber(line) == l)
 end
 
 lineerror("local a\n for i=1,'a' do \n print(i) \n end", 2)
@@ -350,6 +350,23 @@
 X=2;lineerror((p), 1)
 
 
+lineerror([[
+local b = false
+if not b then
+  error 'test'
+end]], 3)
+
+lineerror([[
+local b = false
+if not b then
+  if not b then
+    if not b then
+      error 'test'
+    end
+  end
+end]], 5)
+
+
 if not _soft then
   -- several tests that exaust the Lua stack
   collectgarbage()