First implementation of constant propagation

Local constant variables initialized with compile-time constants
are optimized away from the code.
diff --git a/lcode.c b/lcode.c
index 837253f..74ff47d 100644
--- a/lcode.c
+++ b/lcode.c
@@ -68,6 +68,30 @@
 
 
 /*
+** If expression is a constant, fills 'v' with its value
+** and returns 1. Otherwise, returns 0.
+*/
+int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v) {
+  if (hasjumps(e))
+    return 0;  /* not a constant */
+  switch (e->k) {
+    case VFALSE: case VTRUE:
+      setbvalue(v, e->k == VTRUE);
+      return 1;
+    case VNIL:
+      setnilvalue(v);
+      return 1;
+    case VK: {
+      TValue *k = &fs->f->k[e->u.info];
+      setobj(fs->ls->L, v, k);
+      return 1;
+    }
+    default: return tonumeral(e, v);
+  }
+}
+
+
+/*
 ** Return the previous instruction of the current code. If there
 ** may be a jump target between the current instruction and the
 ** previous one, return an invalid instruction (to avoid wrong
@@ -630,6 +654,31 @@
 
 
 /*
+** Convert a constant in 'v' into an expression description 'e'
+*/
+static void const2exp (FuncState *fs, TValue *v, expdesc *e) {
+  switch (ttypetag(v)) {
+    case LUA_TNUMINT:
+      e->k = VKINT; e->u.ival = ivalue(v);
+      break;
+    case LUA_TNUMFLT:
+      e->k = VKFLT; e->u.nval = fltvalue(v);
+      break;
+    case LUA_TBOOLEAN:
+      e->k = bvalue(v) ? VTRUE : VFALSE;
+      break;
+    case LUA_TNIL:
+      e->k = VNIL;
+      break;
+    case LUA_TSHRSTR:  case LUA_TLNGSTR:
+      e->k = VK; e->u.info = luaK_stringK(fs, tsvalue(v));
+      break;
+    default: lua_assert(0);
+  }
+}
+
+
+/*
 ** Fix an expression to return the number of results 'nresults'.
 ** Either 'e' is a multi-ret expression (function call or vararg)
 ** or 'nresults' is LUA_MULTRET (as any expression can satisfy that).
@@ -677,6 +726,11 @@
 */
 void luaK_dischargevars (FuncState *fs, expdesc *e) {
   switch (e->k) {
+    case VCONST: {
+      TValue *val = &fs->ls->dyd->actvar.arr[e->u.info].k;
+      const2exp(fs, val, e);
+      break;
+    }
     case VLOCAL: {  /* already in a register */
       e->u.info = e->u.var.sidx;
       e->k = VNONRELOC;  /* becomes a non-relocatable value */
@@ -1074,7 +1128,6 @@
 ** Code 'not e', doing constant folding.
 */
 static void codenot (FuncState *fs, expdesc *e) {
-  luaK_dischargevars(fs, e);
   switch (e->k) {
     case VNIL: case VFALSE: {
       e->k = VTRUE;  /* true == not nil == not false */
@@ -1447,6 +1500,7 @@
 */
 void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) {
   static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP};
+  luaK_dischargevars(fs, e);
   switch (op) {
     case OPR_MINUS: case OPR_BNOT:  /* use 'ef' as fake 2nd operand */
       if (constfolding(fs, op + LUA_OPUNM, e, &ef))
diff --git a/lcode.h b/lcode.h
index 0758f88..a15b687 100644
--- a/lcode.h
+++ b/lcode.h
@@ -56,6 +56,7 @@
 LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A,
                                             int B, int C, int k);
 LUAI_FUNC int luaK_isKint (expdesc *e);
+LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v);
 LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
 LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
 LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);
diff --git a/ldump.c b/ldump.c
index 3d5b7b3..9b50172 100644
--- a/ldump.c
+++ b/ldump.c
@@ -149,7 +149,7 @@
   for (i = 0; i < n; i++) {
     DumpByte(f->upvalues[i].instack, D);
     DumpByte(f->upvalues[i].idx, D);
-    DumpByte(f->upvalues[i].ro, D);
+    DumpByte(f->upvalues[i].kind, D);
   }
 }
 
diff --git a/lobject.h b/lobject.h
index 64366a9..2f95bcb 100644
--- a/lobject.h
+++ b/lobject.h
@@ -460,7 +460,7 @@
   TString *name;  /* upvalue name (for debug information) */
   lu_byte instack;  /* whether it is in stack (register) */
   lu_byte idx;  /* index of upvalue (in stack or in outer function's list) */
-  lu_byte ro;  /* true if upvalue is read-only (const) */
+  lu_byte kind;  /* kind of corresponding variable */
 } Upvaldesc;
 
 
diff --git a/lparser.c b/lparser.c
index 7f282bf..79df021 100644
--- a/lparser.c
+++ b/lparser.c
@@ -170,15 +170,16 @@
 ** Register a new local variable in the active 'Proto' (for debug
 ** information).
 */
-static int registerlocalvar (lua_State *L, FuncState *fs, TString *varname) {
+static int registerlocalvar (LexState *ls, FuncState *fs, TString *varname) {
   Proto *f = fs->f;
   int oldsize = f->sizelocvars;
-  luaM_growvector(L, f->locvars, fs->ndebugvars, f->sizelocvars,
+  luaM_growvector(ls->L, f->locvars, fs->ndebugvars, f->sizelocvars,
                   LocVar, SHRT_MAX, "local variables");
   while (oldsize < f->sizelocvars)
     f->locvars[oldsize++].varname = NULL;
   f->locvars[fs->ndebugvars].varname = varname;
-  luaC_objbarrier(L, f, varname);
+  f->locvars[fs->ndebugvars].startpc = fs->pc;
+  luaC_objbarrier(ls->L, f, varname);
   return fs->ndebugvars++;
 }
 
@@ -191,16 +192,13 @@
   FuncState *fs = ls->fs;
   Dyndata *dyd = ls->dyd;
   Vardesc *var;
-  int reg = registerlocalvar(L, fs, name);
   checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal,
                  MAXVARS, "local variables");
   luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
                   dyd->actvar.size, Vardesc, USHRT_MAX, "local variables");
   var = &dyd->actvar.arr[dyd->actvar.n++];
-  var->pidx = cast(short, reg);
-  var->ro = 0;
-  var->name = name;
-  setnilvalue(var);
+  var->vd.kind = VDKREG;  /* default is a regular variable */
+  var->vd.name = name;
   return var;
 }
 
@@ -225,8 +223,8 @@
 static int stacklevel (FuncState *fs, int nvar) {
   while (nvar > 0) {
     Vardesc *vd = getlocalvardesc(fs, nvar - 1);
-    if (vdinstack(vd))  /* is in the stack? */
-      return vd->sidx + 1;
+    if (vd->vd.kind != RDKCTC)  /* is in the stack? */
+      return vd->vd.sidx + 1;
     else
       nvar--;  /* try previous variable */
   }
@@ -247,10 +245,10 @@
 */
 static LocVar *localdebuginfo (FuncState *fs, int i) {
   Vardesc *vd = getlocalvardesc(fs, i);
-  if (!vdinstack(vd))
+  if (vd->vd.kind == RDKCTC)
     return NULL;  /* no debug info. for constants */
   else {
-    int idx = vd->pidx;
+    int idx = vd->vd.pidx;
     lua_assert(idx < fs->ndebugvars);
     return &fs->f->locvars[idx];
   }
@@ -261,7 +259,7 @@
   e->f = e->t = NO_JUMP;
   e->k = VLOCAL;
   e->u.var.vidx = i;
-  e->u.var.sidx = getlocalvardesc(fs, i)->sidx;
+  e->u.var.sidx = getlocalvardesc(fs, i)->vd.sidx;
 }
 
 
@@ -269,15 +267,19 @@
   FuncState *fs = ls->fs;
   TString *varname = NULL;  /* to be set if variable is const */
   switch (e->k) {
+    case VCONST: {
+      varname = ls->dyd->actvar.arr[e->u.info].vd.name;
+      break;
+    }
     case VLOCAL: {
       Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx);
-      if (vardesc->ro)
-        varname = vardesc->name;
+      if (vardesc->vd.kind != VDKREG)  /* not a regular variable? */
+        varname = vardesc->vd.name;
       break;
     }
     case VUPVAL: {
       Upvaldesc *up = &fs->f->upvalues[e->u.info];
-      if (up->ro)
+      if (up->kind != VDKREG)
         varname = up->name;
       break;
     }
@@ -302,8 +304,8 @@
   for (i = 0; i < nvars; i++) {
     int varidx = fs->nactvar++;
     Vardesc *var = getlocalvardesc(fs, varidx);
-    var->sidx = stklevel++;
-    fs->f->locvars[var->pidx].startpc = fs->pc;
+    var->vd.sidx = stklevel++;
+    var->vd.pidx = registerlocalvar(ls, fs, var->vd.name);
   }
 }
 
@@ -354,13 +356,13 @@
   if (v->k == VLOCAL) {
     up->instack = 1;
     up->idx = v->u.var.sidx;
-    up->ro = getlocalvardesc(prev, v->u.var.vidx)->ro;
-    lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->name));
+    up->kind = getlocalvardesc(prev, v->u.var.vidx)->vd.kind;
+    lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name));
   }
   else {
     up->instack = 0;
     up->idx = cast_byte(v->u.info);
-    up->ro = prev->f->upvalues[v->u.info].ro;
+    up->kind = prev->f->upvalues[v->u.info].kind;
     lua_assert(eqstr(name, prev->f->upvalues[v->u.info].name));
   }
   up->name = name;
@@ -373,11 +375,17 @@
 ** Look for an active local variable with the name 'n' in the
 ** function 'fs'.
 */
-static int searchvar (FuncState *fs, TString *n) {
+static int searchvar (FuncState *fs, TString *n, expdesc *var) {
   int i;
   for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) {
-    if (eqstr(n, getlocalvardesc(fs, i)->name))
-      return i;
+    Vardesc *vd = getlocalvardesc(fs, i);
+    if (eqstr(n, vd->vd.name)) {  /* found? */
+      if (vd->vd.kind == RDKCTC)  /* compile-time constant? */
+        init_exp(var, VCONST, fs->firstlocal + i);
+      else  /* real variable */
+        init_var(fs, var, i);
+      return var->k;
+    }
   }
   return -1;  /* not found */
 }
@@ -405,20 +413,19 @@
   if (fs == NULL)  /* no more levels? */
     init_exp(var, VVOID, 0);  /* default is global */
   else {
-    int v = searchvar(fs, n);  /* look up locals at current level */
+    int v = searchvar(fs, n, var);  /* look up locals at current level */
     if (v >= 0) {  /* found? */
-      init_var(fs, var, v);  /* variable is local */
-      if (!base)
+      if (v == VLOCAL && !base)
         markupval(fs, var->u.var.vidx);  /* local will be used as an upval */
     }
     else {  /* not found as local at current level; try upvalues */
       int idx = searchupvalue(fs, n);  /* try existing upvalues */
       if (idx < 0) {  /* not found? */
         singlevaraux(fs->prev, n, var, 0);  /* try upper levels */
-        if (var->k == VVOID)  /* not found? */
-          return;  /* it is a global */
-        /* else was LOCAL or UPVAL */
-        idx  = newupvalue(fs, n, var);  /* will be a new upvalue */
+        if (var->k == VLOCAL || var->k == VUPVAL)  /* local or upvalue? */
+          idx  = newupvalue(fs, n, var);  /* will be a new upvalue */
+        else  /* it is a global or a constant */
+          return;  /* don't need to do anything at this level */
       }
       init_exp(var, VUPVAL, idx);  /* new or old upvalue */
     }
@@ -483,7 +490,7 @@
 ** local variable.
 */
 static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) {
-  const char *varname = getstr(getlocalvardesc(ls->fs, gt->nactvar)->name);
+  const char *varname = getstr(getlocalvardesc(ls->fs, gt->nactvar)->vd.name);
   const char *msg = "<goto %s> at line %d jumps into the scope of local '%s'";
   msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname);
   luaK_semerror(ls, msg);  /* raise the error */
@@ -1710,21 +1717,20 @@
     const char *attr = getstr(str_checkname(ls));
     checknext(ls, '>');
     if (strcmp(attr, "const") == 0)
-      return 1;  /* read-only variable */
+      return RDKCONST;  /* read-only variable */
     else if (strcmp(attr, "toclose") == 0)
-      return 2;  /* to-be-closed variable */
+      return RDKTOCLOSE;  /* to-be-closed variable */
     else
       luaK_semerror(ls,
         luaO_pushfstring(ls->L, "unknown attribute '%s'", attr));
   }
-  return 0;
+  return VDKREG;
 }
 
 
-static void checktoclose (LexState *ls, int toclose) {
-  if (toclose != -1) {  /* is there a to-be-closed variable? */
+static void checktoclose (LexState *ls, int level) {
+  if (level != -1) {  /* is there a to-be-closed variable? */
     FuncState *fs = ls->fs;
-    int level = luaY_nvarstack(fs) + toclose;
     markupval(fs, level + 1);
     fs->bl->insidetbc = 1;  /* in the scope of a to-be-closed variable */
     luaK_codeABC(fs, OP_TBC, level, 0, 0);
@@ -1734,20 +1740,20 @@
 
 static void localstat (LexState *ls) {
   /* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */
+  FuncState *fs = ls->fs;
   int toclose = -1;  /* index of to-be-closed variable (if any) */
+  Vardesc *var;  /* last variable */
   int nvars = 0;
   int nexps;
   expdesc e;
   do {
     int kind = getlocalattribute(ls);
-    Vardesc *var = new_localvar(ls, str_checkname(ls));
-    if (kind != 0) {  /* is there an attribute? */
-      var->ro = 1;  /* all attributes make variable read-only */
-      if (kind == 2) {  /* to-be-closed? */
-        if (toclose != -1)  /* one already present? */
-          luaK_semerror(ls, "multiple to-be-closed variables in local list");
-        toclose = nvars;
-      }
+    var = new_localvar(ls, str_checkname(ls));
+    var->vd.kind = kind;
+    if (kind == RDKTOCLOSE) {  /* to-be-closed? */
+      if (toclose != -1)  /* one already present? */
+        luaK_semerror(ls, "multiple to-be-closed variables in local list");
+      toclose = luaY_nvarstack(fs) + nvars;
     }
     nvars++;
   } while (testnext(ls, ','));
@@ -1757,9 +1763,18 @@
     e.k = VVOID;
     nexps = 0;
   }
-  adjust_assign(ls, nvars, nexps, &e);
+  if (nvars == nexps &&  /* no adjustments? */
+      var->vd.kind == RDKCONST &&  /* last variable is const? */
+      luaK_exp2const(fs, &e, &var->k)) {  /* compile-time constant? */
+    var->vd.kind = RDKCTC;  /* variable is a compile-time constant */
+    adjustlocalvars(ls, nvars - 1);  /* exclude last variable */
+    fs->nactvar++;  /* but count it */
+  }
+  else {
+    adjust_assign(ls, nvars, nexps, &e);
+    adjustlocalvars(ls, nvars);
+  }
   checktoclose(ls, toclose);
-  adjustlocalvars(ls, nvars);
 }
 
 
@@ -1925,7 +1940,7 @@
   env = allocupvalue(fs);  /* ...set environment upvalue */
   env->instack = 1;
   env->idx = 0;
-  env->ro = 0;
+  env->kind = VDKREG;
   env->name = ls->envn;
   luaX_next(ls);  /* read first token */
   statlist(ls);  /* parse main body */
diff --git a/lparser.h b/lparser.h
index 7d43a81..d9b734b 100644
--- a/lparser.h
+++ b/lparser.h
@@ -34,8 +34,9 @@
   VNONRELOC,  /* expression has its value in a fixed register;
                  info = result register */
   VLOCAL,  /* local variable; var.ridx = local register;
-              var.vidx = index in 'actvar.arr'  */
+              var.vidx = relative index in 'actvar.arr'  */
   VUPVAL,  /* upvalue variable; info = index of upvalue in 'upvalues' */
+  VCONST,  /* compile-time constant; info = absolute index in 'actvar.arr'  */
   VINDEXED,  /* indexed variable;
                 ind.t = table register;
                 ind.idx = key's R index */
@@ -81,19 +82,25 @@
 } expdesc;
 
 
+/* kinds of variables */
+#define VDKREG		0   /* regular */
+#define RDKCONST	1   /* constant */
+#define RDKTOCLOSE	2   /* to-be-closed */
+#define RDKCTC		3   /* compile-time constant */
+
 /* description of an active local variable */
-typedef struct Vardesc {
-  TValuefields;  /* constant value (if it is a compile-time constant) */
-  lu_byte ro;  /* true if variable is 'const' */
-  lu_byte sidx;  /* index of the variable in the stack */
-  short pidx;  /* index of the variable in the Proto's 'locvars' array */
-  TString *name;  /* variable name */
+typedef union Vardesc {
+  struct {
+    TValuefields;  /* constant value (if it is a compile-time constant) */
+    lu_byte kind;
+    lu_byte sidx;  /* index of the variable in the stack */
+    short pidx;  /* index of the variable in the Proto's 'locvars' array */
+    TString *name;  /* variable name */
+  } vd;
+  TValue k;  /* constant value (if any) */
 } Vardesc;
 
 
-/* check whether Vardesc is in the stack (not a compile-time constant) */
-#define vdinstack(vd)	(ttisnil(vd))
-
 
 /* description of pending goto statements and label statements */
 typedef struct Labeldesc {
diff --git a/lundump.c b/lundump.c
index 5c0e94d..8f2a490 100644
--- a/lundump.c
+++ b/lundump.c
@@ -198,12 +198,11 @@
   n = LoadInt(S);
   f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc);
   f->sizeupvalues = n;
-  for (i = 0; i < n; i++)
-    f->upvalues[i].name = NULL;
   for (i = 0; i < n; i++) {
+    f->upvalues[i].name = NULL;
     f->upvalues[i].instack = LoadByte(S);
     f->upvalues[i].idx = LoadByte(S);
-    f->upvalues[i].ro = LoadByte(S);
+    f->upvalues[i].kind = LoadByte(S);
   }
 }
 
diff --git a/manual/manual.of b/manual/manual.of
index 136e902..61fcdaa 100644
--- a/manual/manual.of
+++ b/manual/manual.of
@@ -223,7 +223,7 @@
 so changing its value will affect only your own code.)
 
 When Lua loads a chunk,
-the default value for its @id{_ENV} upvalue
+the default value for its @id{_ENV} variable
 is the global environment @seeF{load}.
 Therefore, by default,
 free names in Lua code refer to entries in the global environment
@@ -233,7 +233,7 @@
 You can use @Lid{load} (or @Lid{loadfile})
 to load a chunk with a different environment.
 (In C, you have to load the chunk and then change the value
-of its first upvalue.)
+of its first upvalue; see @See{lua_setupvalue}.)
 
 }
 
@@ -1224,7 +1224,7 @@
 receive arguments, and return values.
 Moreover, such anonymous function is compiled as in the
 scope of an external local variable called @id{_ENV} @see{globalenv}.
-The resulting function always has @id{_ENV} as its only upvalue,
+The resulting function always has @id{_ENV} as its only external variable,
 even if it does not use that variable.
 
 A chunk can be stored in a file or in a string inside the host program.
@@ -2241,8 +2241,8 @@
 Because of the @x{lexical scoping} rules,
 local variables can be freely accessed by functions
 defined inside their scope.
-A local variable used by an inner function is called
-an @def{upvalue}, or @emphx{external local variable},
+A local variable used by an inner function is called an @def{upvalue}
+(or @emphx{external local variable}, or simply @emphx{external variable})
 inside the inner function.
 
 Notice that each execution of a @Rw{local} statement
@@ -4765,11 +4765,7 @@
 Returns @id{NULL} (and pushes nothing)
 when the index @id{n} is greater than the number of upvalues.
 
-For @N{C functions}, this function uses the empty string @T{""}
-as a name for all upvalues.
-(For Lua functions,
-upvalues are the external local variables that the function uses,
-and that are consequently included in its closure.)
+See @Lid{debug.getupvalue} for more information about upvalues.
 
 }
 
@@ -8485,6 +8481,8 @@
 following the order that they are declared in the code,
 counting only the variables that are active
 in the current scope of the function.
+Compile-time constants may not appear in this listing,
+if they were optimized away by the compiler.
 Negative indices refer to vararg arguments;
 @num{-1} is the first vararg argument.
 The function returns @nil if there is no variable with the given index,
@@ -8520,8 +8518,15 @@
 with index @id{up} of the function @id{f}.
 The function returns @nil if there is no upvalue with the given index.
 
-Variable names starting with @Char{(} (open parenthesis) @C{)}
-represent variables with no known names
+(For Lua functions,
+upvalues are the external local variables that the function uses,
+and that are consequently included in its closure.)
+
+For @N{C functions}, this function uses the empty string @T{""}
+as a name for all upvalues.
+
+Variable name @Char{?} (interrogation mark)
+represents variables with no known names
 (variables from chunks saved without debug information).
 
 }
@@ -8626,6 +8631,8 @@
 with the given index.
 Otherwise, it returns the name of the upvalue.
 
+See @Lid{debug.getupvalue} for more information about upvalues.
+
 }
 
 @LibEntry{debug.setuservalue (udata, value, n)|
diff --git a/testes/code.lua b/testes/code.lua
index 128ca2c..49d682f 100644
--- a/testes/code.lua
+++ b/testes/code.lua
@@ -7,6 +7,22 @@
 end
 print "testing code generation and optimizations"
 
+-- to test constant propagation
+local <const> k0 = 0
+local <const> k1 = 1
+local <const> k3 = 3
+local <const> k6 = k3 + (k3 << k0)
+local <const> kFF0 = 0xFF0
+local <const> k3_78 = 3.78
+local <const> x, <const> k3_78_4 = 10, k3_78 / 4
+assert(x == 10)
+
+local <const> kx = "x"
+
+local <const> kTrue = true
+local <const> kFalse = false
+
+local <const> kNil = nil
 
 -- this code gave an error for the code checker
 do
@@ -27,12 +43,12 @@
 
 local function foo ()
   local a
-  a = 3;
+  a = k3;
   a = 0; a = 0.0; a = -7 + 7
-  a = 3.78/4; a = 3.78/4
-  a = -3.78/4; a = 3.78/4; a = -3.78/4
+  a = k3_78/4; a = k3_78_4
+  a = -k3_78/4; a = k3_78/4; a = -3.78/4
   a = -3.79/4; a = 0.0; a = -0;
-  a = 3; a = 3.0; a = 3; a = 3.0
+  a = k3; a = 3.0; a = 3; a = 3.0
 end
 
 checkKlist(foo, {3.78/4, -3.78/4, -3.79/4})
@@ -86,10 +102,11 @@
 
 -- sequence of LOADNILs
 check(function ()
+  local <const> kNil = nil
   local a,b,c
   local d; local e;
   local f,g,h;
-  d = nil; d=nil; b=nil; a=nil; c=nil;
+  d = nil; d=nil; b=nil; a=kNil; c=nil;
 end, 'LOADNIL', 'RETURN0')
 
 check(function ()
@@ -109,7 +126,7 @@
 
 
 -- infinite loops
-check(function () while true do local a = -1 end end,
+check(function () while kTrue do local a = -1 end end,
 'LOADI', 'JMP', 'RETURN0')
 
 check(function () while 1 do local a = -1 end end,
@@ -125,9 +142,9 @@
 
 -- not
 check(function () return not not nil end, 'LOADBOOL', 'RETURN1')
-check(function () return not not false end, 'LOADBOOL', 'RETURN1')
+check(function () return not not kFalse end, 'LOADBOOL', 'RETURN1')
 check(function () return not not true end, 'LOADBOOL', 'RETURN1')
-check(function () return not not 1 end, 'LOADBOOL', 'RETURN1')
+check(function () return not not k3 end, 'LOADBOOL', 'RETURN1')
 
 -- direct access to locals
 check(function ()
@@ -144,7 +161,8 @@
 -- direct access to constants
 check(function ()
   local a,b
-  a.x = 3.2
+  local c = kNil
+  a[kx] = 3.2
   a.x = b
   a[b] = 'x'
 end,
@@ -152,8 +170,9 @@
 
 -- "get/set table" with numeric indices
 check(function (a)
+  local <const> k255 = 255
   a[1] = a[100]
-  a[255] = a[256]
+  a[k255] = a[256]
   a[256] = 5
 end,
   'GETI', 'SETI',
@@ -170,7 +189,7 @@
 
 check(function ()
   local a,b
-  a[true] = false
+  a[kTrue] = false
 end,
   'LOADNIL', 'LOADBOOL', 'SETTABLE', 'RETURN0')
 
@@ -238,37 +257,39 @@
 end
 
 checkF(function () return 0.0 end, 0.0)
-checkI(function () return 0 end, 0)
-checkI(function () return -0//1 end, 0)
+checkI(function () return k0 end, 0)
+checkI(function () return -k0//1 end, 0)
 checkK(function () return 3^-1 end, 1/3)
 checkK(function () return (1 + 1)^(50 + 50) end, 2^100)
 checkK(function () return (-2)^(31 - 2) end, -0x20000000 + 0.0)
-checkF(function () return (-3^0 + 5) // 3.0 end, 1.0)
-checkI(function () return -3 % 5 end, 2)
+checkF(function () return (-k3^0 + 5) // 3.0 end, 1.0)
+checkI(function () return -k3 % 5 end, 2)
 checkF(function () return -((2.0^8 + -(-1)) % 8)/2 * 4 - 3 end, -5.0)
 checkF(function () return -((2^8 + -(-1)) % 8)//2 * 4 - 3 end, -7.0)
 checkI(function () return 0xF0.0 | 0xCC.0 ~ 0xAA & 0xFD end, 0xF4)
-checkI(function () return ~(~0xFF0 | 0xFF0) end, 0)
+checkI(function () return ~(~kFF0 | kFF0) end, 0)
 checkI(function () return ~~-1024.0 end, -1024)
-checkI(function () return ((100 << 6) << -4) >> 2 end, 100)
+checkI(function () return ((100 << k6) << -4) >> 2 end, 100)
 
 -- borders around MAXARG_sBx ((((1 << 17) - 1) >> 1) == 65535)
 local a = 17; local sbx = ((1 << a) - 1) >> 1   -- avoid folding
-checkI(function () return 65535 end, sbx)
-checkI(function () return -65535 end, -sbx)
-checkI(function () return 65536 end, sbx + 1)
-checkK(function () return 65537 end, sbx + 2)
-checkK(function () return -65536 end, -(sbx + 1))
+local <const> border = 65535
+checkI(function () return border end, sbx)
+checkI(function () return -border end, -sbx)
+checkI(function () return border + 1 end, sbx + 1)
+checkK(function () return border + 2 end, sbx + 2)
+checkK(function () return -(border + 1) end, -(sbx + 1))
 
-checkF(function () return 65535.0 end, sbx + 0.0)
-checkF(function () return -65535.0 end, -sbx + 0.0)
-checkF(function () return 65536.0 end, (sbx + 1.0))
-checkK(function () return 65537.0 end, (sbx + 2.0))
-checkK(function () return -65536.0 end, -(sbx + 1.0))
+local <const> border = 65535.0
+checkF(function () return border end, sbx + 0.0)
+checkF(function () return -border end, -sbx + 0.0)
+checkF(function () return border + 1 end, (sbx + 1.0))
+checkK(function () return border + 2 end, (sbx + 2.0))
+checkK(function () return -(border + 1) end, -(sbx + 1.0))
 
 
 -- immediate operands
-checkR(function (x) return x + 1 end, 10, 11, 'ADDI', 'RETURN1')
+checkR(function (x) return x + k1 end, 10, 11, 'ADDI', 'RETURN1')
 checkR(function (x) return 128 + x end, 0.0, 128.0, 'ADDI', 'RETURN1')
 checkR(function (x) return x * -127 end, -1.0, 127.0, 'MULI', 'RETURN1')
 checkR(function (x) return 20 * x end, 2, 40, 'MULI', 'RETURN1')
@@ -276,7 +297,7 @@
 checkR(function (x) return x / 40 end, 40, 1.0, 'DIVI', 'RETURN1')
 checkR(function (x) return x // 1 end, 10.0, 10.0, 'IDIVI', 'RETURN1')
 checkR(function (x) return x % (100 - 10) end, 91, 1, 'MODI', 'RETURN1')
-checkR(function (x) return 1 << x end, 3, 8, 'SHLI', 'RETURN1')
+checkR(function (x) return k1 << x end, 3, 8, 'SHLI', 'RETURN1')
 checkR(function (x) return x << 2 end, 10, 40, 'SHRI', 'RETURN1')
 checkR(function (x) return x >> 2 end, 8, 2, 'SHRI', 'RETURN1')
 checkR(function (x) return x & 1 end, 9, 1, 'BANDK', 'RETURN1')
@@ -295,7 +316,7 @@
 
 -- no foldings (and immediate operands)
 check(function () return -0.0 end, 'LOADF', 'UNM', 'RETURN1')
-check(function () return 3/0 end, 'LOADI', 'DIVI', 'RETURN1')
+check(function () return k3/0 end, 'LOADI', 'DIVI', 'RETURN1')
 check(function () return 0%0 end, 'LOADI', 'MODI', 'RETURN1')
 check(function () return -4//0 end, 'LOADI', 'IDIVI', 'RETURN1')
 check(function (x) return x >> 2.0 end, 'LOADF', 'SHR', 'RETURN1')
@@ -335,7 +356,7 @@
 
 do   -- tests for table access in upvalues
   local t
-  check(function () t.x = t.y end, 'GETTABUP', 'SETTABUP')
+  check(function () t[kx] = t.y end, 'GETTABUP', 'SETTABUP')
   check(function (a) t[a()] = t[a()] end,
   'MOVE', 'CALL', 'GETUPVAL', 'MOVE', 'CALL',
   'GETUPVAL', 'GETTABLE', 'SETTABLE')
@@ -379,6 +400,12 @@
 end
 )
 
+checkequal(function () return 6 or true or nil end,
+           function () return k6 or kTrue or kNil end)
+
+checkequal(function () return 6 and true or nil end,
+           function () return k6 and kTrue or kNil end)
+
 
 print 'OK'
 
diff --git a/testes/constructs.lua b/testes/constructs.lua
index fe4db2c..8a549e1 100644
--- a/testes/constructs.lua
+++ b/testes/constructs.lua
@@ -287,7 +287,7 @@
 ------------------------------------------------------------------
 
 -- sometimes will be 0, sometimes will not...
-_ENV.GLOB1 = math.floor(os.time()) % 2
+_ENV.GLOB1 = math.random(0, 1)
 
 -- basic expressions with their respective values
 local basiccases = {
@@ -298,6 +298,26 @@
   {"(0==_ENV.GLOB1)", 0 == _ENV.GLOB1},
 }
 
+local prog
+
+if _ENV.GLOB1 == 0 then
+  basiccases[2][1] = "F"   -- constant false
+
+  prog = [[
+    local <const> F = false
+    if %s then IX = true end
+    return %s
+]]
+else
+  basiccases[4][1] = "k10"   -- constant 10
+
+  prog = [[
+    local <const> k10 = 10
+    if %s then IX = true end
+    return %s
+  ]]
+end
+
 print('testing short-circuit optimizations (' .. _ENV.GLOB1 .. ')')
 
 
@@ -337,8 +357,6 @@
 for i = 2, level do cases[i] = createcases(i) end
 print("+")
 
-local prog = [[if %s then IX = true end; return %s]]
-
 local i = 0
 for n = 1, level do
   for _, v in pairs(cases[n]) do
diff --git a/testes/locals.lua b/testes/locals.lua
index 0de00a9..1b82dd7 100644
--- a/testes/locals.lua
+++ b/testes/locals.lua
@@ -324,7 +324,7 @@
 
   -- errors due to non-closable values
   local function foo ()
-    local <toclose> x = 34
+    local <toclose> x = {}
   end
   local stat, msg = pcall(foo)
   assert(not stat and string.find(msg, "variable 'x'"))
diff --git a/testes/math.lua b/testes/math.lua
index c45a91a..befce12 100644
--- a/testes/math.lua
+++ b/testes/math.lua
@@ -270,7 +270,7 @@
 end
 
 do
-  local NaN = 0/0
+  local <const> NaN = 0/0
   assert(not (NaN < 0))
   assert(not (NaN > minint))
   assert(not (NaN <= -9))
@@ -767,7 +767,8 @@
 
 do
   print("testing -0 and NaN")
-  local mz, z = -0.0, 0.0
+  local <const> mz = -0.0
+  local <const> z = 0.0
   assert(mz == z)
   assert(1/mz < 0 and 0 < 1/z)
   local a = {[mz] = 1}
@@ -775,17 +776,18 @@
   a[z] = 2
   assert(a[z] == 2 and a[mz] == 2)
   local inf = math.huge * 2 + 1
-  mz, z = -1/inf, 1/inf
+  local <const> mz = -1/inf
+  local <const> z = 1/inf
   assert(mz == z)
   assert(1/mz < 0 and 0 < 1/z)
-  local NaN = inf - inf
+  local <const> NaN = inf - inf
   assert(NaN ~= NaN)
   assert(not (NaN < NaN))
   assert(not (NaN <= NaN))
   assert(not (NaN > NaN))
   assert(not (NaN >= NaN))
   assert(not (0 < NaN) and not (NaN < 0))
-  local NaN1 = 0/0
+  local <const> NaN1 = 0/0
   assert(NaN ~= NaN1 and not (NaN <= NaN1) and not (NaN1 <= NaN))
   local a = {}
   assert(not pcall(rawset, a, NaN, 1))
@@ -814,8 +816,8 @@
 -- the first call after seed 1007 should return 0x7a7040a5a323c9d6
 do
   -- all computations assume at most 32-bit integers
-  local h = 0x7a7040a5   -- higher half
-  local l = 0xa323c9d6   -- lower half
+  local <const> h = 0x7a7040a5   -- higher half
+  local <const> l = 0xa323c9d6   -- lower half
 
   math.randomseed(1007)
   -- get the low 'intbits' of the 64-bit expected result