aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-08-24 10:17:54 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-08-24 10:17:54 -0300
commit8c8a91f2ef7acccb99e3737913faad8d48b39571 (patch)
tree0807151944b7f7fd00eedfcfe94b4ee26fe25b21
parentf99509581ee73c1c2dbddb3398e87c098771d31f (diff)
downloadlua-8c8a91f2ef7acccb99e3737913faad8d48b39571.tar.gz
lua-8c8a91f2ef7acccb99e3737913faad8d48b39571.tar.bz2
lua-8c8a91f2ef7acccb99e3737913faad8d48b39571.zip
Deprecated the emulation of '__le' using '__lt'
As hinted in the manual for Lua 5.3, the emulation of the metamethod for '__le' using '__le' has been deprecated. It is slow, complicates the logic, and it is easy to avoid this emulation by defining a proper '__le' function. Moreover, often this emulation was used wrongly, with a programmer assuming that an order is total when it is not (e.g., NaN in floating-point numbers).
-rw-r--r--lstate.h8
-rw-r--r--ltests.h1
-rw-r--r--ltm.c2
-rw-r--r--luaconf.h8
-rw-r--r--lvm.c2
-rw-r--r--manual/manual.of21
-rw-r--r--testes/coroutine.lua8
-rw-r--r--testes/events.lua34
8 files changed, 44 insertions, 40 deletions
diff --git a/lstate.h b/lstate.h
index 2a36bd96..5461b291 100644
--- a/lstate.h
+++ b/lstate.h
@@ -138,9 +138,11 @@ typedef struct CallInfo {
138#define CIST_YPCALL (1<<3) /* call is a yieldable protected call */ 138#define CIST_YPCALL (1<<3) /* call is a yieldable protected call */
139#define CIST_TAIL (1<<4) /* call was tail called */ 139#define CIST_TAIL (1<<4) /* call was tail called */
140#define CIST_HOOKYIELD (1<<5) /* last hook called yielded */ 140#define CIST_HOOKYIELD (1<<5) /* last hook called yielded */
141#define CIST_LEQ (1<<6) /* using __lt for __le */ 141#define CIST_FIN (1<<6) /* call is running a finalizer */
142#define CIST_FIN (1<<7) /* call is running a finalizer */ 142#define CIST_TRAN (1<<7) /* 'ci' has transfer information */
143#define CIST_TRAN (1<<8) /* 'ci' has transfer information */ 143#if defined(LUA_COMPAT_LT_LE)
144#define CIST_LEQ (1<<8) /* using __lt for __le */
145#endif
144 146
145/* active function is a Lua function */ 147/* active function is a Lua function */
146#define isLua(ci) (!((ci)->callstatus & CIST_C)) 148#define isLua(ci) (!((ci)->callstatus & CIST_C))
diff --git a/ltests.h b/ltests.h
index 54bc4f5f..d44974a4 100644
--- a/ltests.h
+++ b/ltests.h
@@ -13,6 +13,7 @@
13 13
14/* test Lua with compatibility code */ 14/* test Lua with compatibility code */
15#define LUA_COMPAT_MATHLIB 15#define LUA_COMPAT_MATHLIB
16#define LUA_COMPAT_LT_LE
16 17
17 18
18#define LUA_DEBUG 19#define LUA_DEBUG
diff --git a/ltm.c b/ltm.c
index 1c1a18b7..5c148180 100644
--- a/ltm.c
+++ b/ltm.c
@@ -188,6 +188,7 @@ int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2,
188 TMS event) { 188 TMS event) {
189 if (callbinTM(L, p1, p2, L->top, event)) /* try original event */ 189 if (callbinTM(L, p1, p2, L->top, event)) /* try original event */
190 return !l_isfalse(s2v(L->top)); 190 return !l_isfalse(s2v(L->top));
191#if defined(LUA_COMPAT_LT_LE)
191 else if (event == TM_LE) { 192 else if (event == TM_LE) {
192 /* try '!(p2 < p1)' for '(p1 <= p2)' */ 193 /* try '!(p2 < p1)' for '(p1 <= p2)' */
193 L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */ 194 L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */
@@ -197,6 +198,7 @@ int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2,
197 } 198 }
198 /* else error will remove this 'ci'; no need to clear mark */ 199 /* else error will remove this 'ci'; no need to clear mark */
199 } 200 }
201#endif
200 luaG_ordererror(L, p1, p2); /* no metamethod found */ 202 luaG_ordererror(L, p1, p2); /* no metamethod found */
201 return 0; /* to avoid warnings */ 203 return 0; /* to avoid warnings */
202} 204}
diff --git a/luaconf.h b/luaconf.h
index cc8e1bdc..126257dc 100644
--- a/luaconf.h
+++ b/luaconf.h
@@ -295,7 +295,7 @@
295*/ 295*/
296 296
297/* 297/*
298@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.2. 298@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.3.
299** You can define it to get all options, or change specific options 299** You can define it to get all options, or change specific options
300** to fit your specific needs. 300** to fit your specific needs.
301*/ 301*/
@@ -316,6 +316,12 @@
316*/ 316*/
317#define LUA_COMPAT_APIINTCASTS 317#define LUA_COMPAT_APIINTCASTS
318 318
319/*
320@@ LUA_COMPAT_LT_LE controls the emulation of the '__le' metamethod
321** using '__lt'.
322*/
323#define LUA_COMPAT_LT_LE
324
319#endif /* } */ 325#endif /* } */
320 326
321 327
diff --git a/lvm.c b/lvm.c
index 7550dcc8..add26942 100644
--- a/lvm.c
+++ b/lvm.c
@@ -754,10 +754,12 @@ void luaV_finishOp (lua_State *L) {
754 case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */ 754 case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */
755 int res = !l_isfalse(s2v(L->top - 1)); 755 int res = !l_isfalse(s2v(L->top - 1));
756 L->top--; 756 L->top--;
757#if defined(LUA_COMPAT_LT_LE)
757 if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */ 758 if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */
758 ci->callstatus ^= CIST_LEQ; /* clear mark */ 759 ci->callstatus ^= CIST_LEQ; /* clear mark */
759 res = !res; /* negate result */ 760 res = !res; /* negate result */
760 } 761 }
762#endif
761 lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP); 763 lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP);
762 if (res != GETARG_k(inst)) /* condition failed? */ 764 if (res != GETARG_k(inst)) /* condition failed? */
763 ci->u.l.savedpc++; /* skip jump instruction */ 765 ci->u.l.savedpc++; /* skip jump instruction */
diff --git a/manual/manual.of b/manual/manual.of
index 5a8b1b2c..47a551bf 100644
--- a/manual/manual.of
+++ b/manual/manual.of
@@ -474,17 +474,7 @@ The result of the call is always converted to a boolean.
474 474
475@item{@idx{__le}| 475@item{@idx{__le}|
476the less equal (@T{<=}) operation. 476the less equal (@T{<=}) operation.
477Unlike other operations, 477Behavior similar to the less than operation.
478the less-equal operation can use two different events.
479First, Lua looks for the @idx{__le} metamethod in both operands,
480like in the less than operation.
481If it cannot find such a metamethod,
482then it will try the @idx{__lt} metamethod,
483assuming that @T{a <= b} is equivalent to @T{not (b < a)}.
484As with the other comparison operators,
485the result is always a boolean.
486(This use of the @idx{__lt} event can be removed in future versions;
487it is also slower than a real @idx{__le} metamethod.)
488} 478}
489 479
490@item{@idx{__index}| 480@item{@idx{__index}|
@@ -1643,7 +1633,8 @@ all operations @emphx{wrap around},
1643according to the usual rules of two-complement arithmetic. 1633according to the usual rules of two-complement arithmetic.
1644(In other words, 1634(In other words,
1645they return the unique representable integer 1635they return the unique representable integer
1646that is equal modulo @M{2@sp{64}} to the mathematical result.) 1636that is equal modulo @M{2@sp{n}} to the mathematical result,
1637where @M{n} is the number of bits of the integer type.)
1647} 1638}
1648 1639
1649@sect3{bitwise| @title{Bitwise Operators} 1640@sect3{bitwise| @title{Bitwise Operators}
@@ -8537,6 +8528,12 @@ For instance, the result of @T{"1" + "2"} now is an integer,
8537not a float. 8528not a float.
8538} 8529}
8539 8530
8531@item{
8532The use of the @idx{__lt} metamethod to emulate @id{__le}
8533has been removed.
8534When needed, this metamethod must be explicitly defined.
8535}
8536
8540} 8537}
8541 8538
8542} 8539}
diff --git a/testes/coroutine.lua b/testes/coroutine.lua
index 182c1e18..36eae44a 100644
--- a/testes/coroutine.lua
+++ b/testes/coroutine.lua
@@ -1,4 +1,4 @@
1-- $Id: testes/coroutine.lua $ 1-- $Id: testes/coroutine.lua 2018-07-25 15:31:04 -0300 $
2-- See Copyright Notice in file all.lua 2-- See Copyright Notice in file all.lua
3 3
4print "testing coroutines" 4print "testing coroutines"
@@ -619,10 +619,8 @@ end
619 619
620assert(run(function () if (a>=b) then return '>=' else return '<' end end, 620assert(run(function () if (a>=b) then return '>=' else return '<' end end,
621 {"le", "sub"}) == "<") 621 {"le", "sub"}) == "<")
622-- '<=' using '<'
623mt.__le = nil
624assert(run(function () if (a<=b) then return '<=' else return '>' end end, 622assert(run(function () if (a<=b) then return '<=' else return '>' end end,
625 {"lt"}) == "<=") 623 {"le", "sub"}) == "<=")
626assert(run(function () if (a==b) then return '==' else return '~=' end end, 624assert(run(function () if (a==b) then return '==' else return '~=' end end,
627 {"eq"}) == "~=") 625 {"eq"}) == "~=")
628 626
@@ -677,7 +675,7 @@ do -- a few more tests for comparsion operators
677 return val(a) < val(b) 675 return val(a) < val(b)
678 end, 676 end,
679 } 677 }
680 local mt2 = { __lt = mt1.__lt } -- no __le 678 local mt2 = { __lt = mt1.__lt, __le = mt1.__le }
681 679
682 local function run (f) 680 local function run (f)
683 local co = coroutine.wrap(f) 681 local co = coroutine.wrap(f)
diff --git a/testes/events.lua b/testes/events.lua
index 21a822a7..c4d43d51 100644
--- a/testes/events.lua
+++ b/testes/events.lua
@@ -1,4 +1,4 @@
1-- $Id: testes/events.lua $ 1-- $Id: testes/events.lua 2018-07-25 15:31:04 -0300 $
2-- See Copyright Notice in file all.lua 2-- See Copyright Notice in file all.lua
3 3
4print('testing metatables') 4print('testing metatables')
@@ -217,6 +217,13 @@ t.__lt = function (a,b,c)
217 return a<b, "dummy" 217 return a<b, "dummy"
218end 218end
219 219
220t.__le = function (a,b,c)
221 assert(c == nil)
222 if type(a) == 'table' then a = a.x end
223 if type(b) == 'table' then b = b.x end
224 return a<=b, "dummy"
225end
226
220function Op(x) return setmetatable({x=x}, t) end 227function Op(x) return setmetatable({x=x}, t) end
221 228
222local function test () 229local function test ()
@@ -236,15 +243,6 @@ end
236 243
237test() 244test()
238 245
239t.__le = function (a,b,c)
240 assert(c == nil)
241 if type(a) == 'table' then a = a.x end
242 if type(b) == 'table' then b = b.x end
243 return a<=b, "dummy"
244end
245
246test() -- retest comparisons, now using both `lt' and `le'
247
248 246
249-- test `partial order' 247-- test `partial order'
250 248
@@ -266,14 +264,6 @@ t.__lt = function (a,b)
266 return next(b) ~= nil 264 return next(b) ~= nil
267end 265end
268 266
269t.__le = nil
270
271assert(Set{1,2,3} < Set{1,2,3,4})
272assert(not(Set{1,2,3,4} < Set{1,2,3,4}))
273assert((Set{1,2,3,4} <= Set{1,2,3,4}))
274assert((Set{1,2,3,4} >= Set{1,2,3,4}))
275assert((Set{1,3} <= Set{3,5})) -- wrong!! model needs a `le' method ;-)
276
277t.__le = function (a,b) 267t.__le = function (a,b)
278 for k in pairs(a) do 268 for k in pairs(a) do
279 if not b[k] then return false end 269 if not b[k] then return false end
@@ -281,10 +271,15 @@ t.__le = function (a,b)
281 return true 271 return true
282end 272end
283 273
284assert(not (Set{1,3} <= Set{3,5})) -- now its OK! 274assert(Set{1,2,3} < Set{1,2,3,4})
275assert(not(Set{1,2,3,4} < Set{1,2,3,4}))
276assert((Set{1,2,3,4} <= Set{1,2,3,4}))
277assert((Set{1,2,3,4} >= Set{1,2,3,4}))
278assert(not (Set{1,3} <= Set{3,5}))
285assert(not(Set{1,3} <= Set{3,5})) 279assert(not(Set{1,3} <= Set{3,5}))
286assert(not(Set{1,3} >= Set{3,5})) 280assert(not(Set{1,3} >= Set{3,5}))
287 281
282
288t.__eq = function (a,b) 283t.__eq = function (a,b)
289 for k in pairs(a) do 284 for k in pairs(a) do
290 if not b[k] then return false end 285 if not b[k] then return false end
@@ -376,6 +371,7 @@ t1 = {}; c = {}; setmetatable(c, t1)
376d = {} 371d = {}
377t1.__eq = function () return true end 372t1.__eq = function () return true end
378t1.__lt = function () return true end 373t1.__lt = function () return true end
374t1.__le = function () return false end
379setmetatable(d, t1) 375setmetatable(d, t1)
380assert(c == d and c < d and not(d <= c)) 376assert(c == d and c < d and not(d <= c))
381t2 = {} 377t2 = {}