Skip to content

Commit 8c8a91f

Browse files
committed
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).
1 parent f995095 commit 8c8a91f

8 files changed

Lines changed: 44 additions & 40 deletions

File tree

lstate.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,11 @@ typedef struct CallInfo {
138138
#define CIST_YPCALL (1<<3) /* call is a yieldable protected call */
139139
#define CIST_TAIL (1<<4) /* call was tail called */
140140
#define CIST_HOOKYIELD (1<<5) /* last hook called yielded */
141-
#define CIST_LEQ (1<<6) /* using __lt for __le */
142-
#define CIST_FIN (1<<7) /* call is running a finalizer */
143-
#define CIST_TRAN (1<<8) /* 'ci' has transfer information */
141+
#define CIST_FIN (1<<6) /* call is running a finalizer */
142+
#define CIST_TRAN (1<<7) /* 'ci' has transfer information */
143+
#if defined(LUA_COMPAT_LT_LE)
144+
#define CIST_LEQ (1<<8) /* using __lt for __le */
145+
#endif
144146

145147
/* active function is a Lua function */
146148
#define isLua(ci) (!((ci)->callstatus & CIST_C))

ltests.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
/* test Lua with compatibility code */
1515
#define LUA_COMPAT_MATHLIB
16+
#define LUA_COMPAT_LT_LE
1617

1718

1819
#define LUA_DEBUG

ltm.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2,
188188
TMS event) {
189189
if (callbinTM(L, p1, p2, L->top, event)) /* try original event */
190190
return !l_isfalse(s2v(L->top));
191+
#if defined(LUA_COMPAT_LT_LE)
191192
else if (event == TM_LE) {
192193
/* try '!(p2 < p1)' for '(p1 <= p2)' */
193194
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,
197198
}
198199
/* else error will remove this 'ci'; no need to clear mark */
199200
}
201+
#endif
200202
luaG_ordererror(L, p1, p2); /* no metamethod found */
201203
return 0; /* to avoid warnings */
202204
}

luaconf.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@
295295
*/
296296

297297
/*
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.
299299
** You can define it to get all options, or change specific options
300300
** to fit your specific needs.
301301
*/
@@ -316,6 +316,12 @@
316316
*/
317317
#define LUA_COMPAT_APIINTCASTS
318318

319+
/*
320+
@@ LUA_COMPAT_LT_LE controls the emulation of the '__le' metamethod
321+
** using '__lt'.
322+
*/
323+
#define LUA_COMPAT_LT_LE
324+
319325
#endif /* } */
320326

321327

lvm.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,10 +754,12 @@ void luaV_finishOp (lua_State *L) {
754754
case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */
755755
int res = !l_isfalse(s2v(L->top - 1));
756756
L->top--;
757+
#if defined(LUA_COMPAT_LT_LE)
757758
if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */
758759
ci->callstatus ^= CIST_LEQ; /* clear mark */
759760
res = !res; /* negate result */
760761
}
762+
#endif
761763
lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP);
762764
if (res != GETARG_k(inst)) /* condition failed? */
763765
ci->u.l.savedpc++; /* skip jump instruction */

manual/manual.of

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -474,17 +474,7 @@ The result of the call is always converted to a boolean.
474474

475475
@item{@idx{__le}|
476476
the less equal (@T{<=}) operation.
477-
Unlike other operations,
478-
the less-equal operation can use two different events.
479-
First, Lua looks for the @idx{__le} metamethod in both operands,
480-
like in the less than operation.
481-
If it cannot find such a metamethod,
482-
then it will try the @idx{__lt} metamethod,
483-
assuming that @T{a <= b} is equivalent to @T{not (b < a)}.
484-
As with the other comparison operators,
485-
the result is always a boolean.
486-
(This use of the @idx{__lt} event can be removed in future versions;
487-
it is also slower than a real @idx{__le} metamethod.)
477+
Behavior similar to the less than operation.
488478
}
489479

490480
@item{@idx{__index}|
@@ -1643,7 +1633,8 @@ all operations @emphx{wrap around},
16431633
according to the usual rules of two-complement arithmetic.
16441634
(In other words,
16451635
they return the unique representable integer
1646-
that is equal modulo @M{2@sp{64}} to the mathematical result.)
1636+
that is equal modulo @M{2@sp{n}} to the mathematical result,
1637+
where @M{n} is the number of bits of the integer type.)
16471638
}
16481639

16491640
@sect3{bitwise| @title{Bitwise Operators}
@@ -8537,6 +8528,12 @@ For instance, the result of @T{"1" + "2"} now is an integer,
85378528
not a float.
85388529
}
85398530

8531+
@item{
8532+
The use of the @idx{__lt} metamethod to emulate @id{__le}
8533+
has been removed.
8534+
When needed, this metamethod must be explicitly defined.
8535+
}
8536+
85408537
}
85418538

85428539
}

testes/coroutine.lua

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
-- $Id: testes/coroutine.lua $
1+
-- $Id: testes/coroutine.lua 2018-07-25 15:31:04 -0300 $
22
-- See Copyright Notice in file all.lua
33

44
print "testing coroutines"
@@ -619,10 +619,8 @@ end
619619

620620
assert(run(function () if (a>=b) then return '>=' else return '<' end end,
621621
{"le", "sub"}) == "<")
622-
-- '<=' using '<'
623-
mt.__le = nil
624622
assert(run(function () if (a<=b) then return '<=' else return '>' end end,
625-
{"lt"}) == "<=")
623+
{"le", "sub"}) == "<=")
626624
assert(run(function () if (a==b) then return '==' else return '~=' end end,
627625
{"eq"}) == "~=")
628626

@@ -677,7 +675,7 @@ do -- a few more tests for comparsion operators
677675
return val(a) < val(b)
678676
end,
679677
}
680-
local mt2 = { __lt = mt1.__lt } -- no __le
678+
local mt2 = { __lt = mt1.__lt, __le = mt1.__le }
681679

682680
local function run (f)
683681
local co = coroutine.wrap(f)

testes/events.lua

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
-- $Id: testes/events.lua $
1+
-- $Id: testes/events.lua 2018-07-25 15:31:04 -0300 $
22
-- See Copyright Notice in file all.lua
33

44
print('testing metatables')
@@ -217,6 +217,13 @@ t.__lt = function (a,b,c)
217217
return a<b, "dummy"
218218
end
219219

220+
t.__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"
225+
end
226+
220227
function Op(x) return setmetatable({x=x}, t) end
221228

222229
local function test ()
@@ -236,15 +243,6 @@ end
236243

237244
test()
238245

239-
t.__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"
244-
end
245-
246-
test() -- retest comparisons, now using both `lt' and `le'
247-
248246

249247
-- test `partial order'
250248

@@ -266,25 +264,22 @@ t.__lt = function (a,b)
266264
return next(b) ~= nil
267265
end
268266

269-
t.__le = nil
270-
271-
assert(Set{1,2,3} < Set{1,2,3,4})
272-
assert(not(Set{1,2,3,4} < Set{1,2,3,4}))
273-
assert((Set{1,2,3,4} <= Set{1,2,3,4}))
274-
assert((Set{1,2,3,4} >= Set{1,2,3,4}))
275-
assert((Set{1,3} <= Set{3,5})) -- wrong!! model needs a `le' method ;-)
276-
277267
t.__le = function (a,b)
278268
for k in pairs(a) do
279269
if not b[k] then return false end
280270
end
281271
return true
282272
end
283273

284-
assert(not (Set{1,3} <= Set{3,5})) -- now its OK!
274+
assert(Set{1,2,3} < Set{1,2,3,4})
275+
assert(not(Set{1,2,3,4} < Set{1,2,3,4}))
276+
assert((Set{1,2,3,4} <= Set{1,2,3,4}))
277+
assert((Set{1,2,3,4} >= Set{1,2,3,4}))
278+
assert(not (Set{1,3} <= Set{3,5}))
285279
assert(not(Set{1,3} <= Set{3,5}))
286280
assert(not(Set{1,3} >= Set{3,5}))
287281

282+
288283
t.__eq = function (a,b)
289284
for k in pairs(a) do
290285
if not b[k] then return false end
@@ -376,6 +371,7 @@ t1 = {}; c = {}; setmetatable(c, t1)
376371
d = {}
377372
t1.__eq = function () return true end
378373
t1.__lt = function () return true end
374+
t1.__le = function () return false end
379375
setmetatable(d, t1)
380376
assert(c == d and c < d and not(d <= c))
381377
t2 = {}

0 commit comments

Comments
 (0)