mirror of
https://github.com/lua/lua.git
synced 2025-01-28 06:03:00 +08:00
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).
This commit is contained in:
parent
f99509581e
commit
8c8a91f2ef
8
lstate.h
8
lstate.h
@ -138,9 +138,11 @@ typedef struct CallInfo {
|
|||||||
#define CIST_YPCALL (1<<3) /* call is a yieldable protected call */
|
#define CIST_YPCALL (1<<3) /* call is a yieldable protected call */
|
||||||
#define CIST_TAIL (1<<4) /* call was tail called */
|
#define CIST_TAIL (1<<4) /* call was tail called */
|
||||||
#define CIST_HOOKYIELD (1<<5) /* last hook called yielded */
|
#define CIST_HOOKYIELD (1<<5) /* last hook called yielded */
|
||||||
#define CIST_LEQ (1<<6) /* using __lt for __le */
|
#define CIST_FIN (1<<6) /* call is running a finalizer */
|
||||||
#define CIST_FIN (1<<7) /* call is running a finalizer */
|
#define CIST_TRAN (1<<7) /* 'ci' has transfer information */
|
||||||
#define CIST_TRAN (1<<8) /* 'ci' has transfer information */
|
#if defined(LUA_COMPAT_LT_LE)
|
||||||
|
#define CIST_LEQ (1<<8) /* using __lt for __le */
|
||||||
|
#endif
|
||||||
|
|
||||||
/* active function is a Lua function */
|
/* active function is a Lua function */
|
||||||
#define isLua(ci) (!((ci)->callstatus & CIST_C))
|
#define isLua(ci) (!((ci)->callstatus & CIST_C))
|
||||||
|
1
ltests.h
1
ltests.h
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
/* test Lua with compatibility code */
|
/* test Lua with compatibility code */
|
||||||
#define LUA_COMPAT_MATHLIB
|
#define LUA_COMPAT_MATHLIB
|
||||||
|
#define LUA_COMPAT_LT_LE
|
||||||
|
|
||||||
|
|
||||||
#define LUA_DEBUG
|
#define LUA_DEBUG
|
||||||
|
2
ltm.c
2
ltm.c
@ -188,6 +188,7 @@ int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2,
|
|||||||
TMS event) {
|
TMS event) {
|
||||||
if (callbinTM(L, p1, p2, L->top, event)) /* try original event */
|
if (callbinTM(L, p1, p2, L->top, event)) /* try original event */
|
||||||
return !l_isfalse(s2v(L->top));
|
return !l_isfalse(s2v(L->top));
|
||||||
|
#if defined(LUA_COMPAT_LT_LE)
|
||||||
else if (event == TM_LE) {
|
else if (event == TM_LE) {
|
||||||
/* try '!(p2 < p1)' for '(p1 <= p2)' */
|
/* try '!(p2 < p1)' for '(p1 <= p2)' */
|
||||||
L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */
|
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,
|
|||||||
}
|
}
|
||||||
/* else error will remove this 'ci'; no need to clear mark */
|
/* else error will remove this 'ci'; no need to clear mark */
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
luaG_ordererror(L, p1, p2); /* no metamethod found */
|
luaG_ordererror(L, p1, p2); /* no metamethod found */
|
||||||
return 0; /* to avoid warnings */
|
return 0; /* to avoid warnings */
|
||||||
}
|
}
|
||||||
|
@ -295,7 +295,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.2.
|
@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.3.
|
||||||
** You can define it to get all options, or change specific options
|
** You can define it to get all options, or change specific options
|
||||||
** to fit your specific needs.
|
** to fit your specific needs.
|
||||||
*/
|
*/
|
||||||
@ -316,6 +316,12 @@
|
|||||||
*/
|
*/
|
||||||
#define LUA_COMPAT_APIINTCASTS
|
#define LUA_COMPAT_APIINTCASTS
|
||||||
|
|
||||||
|
/*
|
||||||
|
@@ LUA_COMPAT_LT_LE controls the emulation of the '__le' metamethod
|
||||||
|
** using '__lt'.
|
||||||
|
*/
|
||||||
|
#define LUA_COMPAT_LT_LE
|
||||||
|
|
||||||
#endif /* } */
|
#endif /* } */
|
||||||
|
|
||||||
|
|
||||||
|
2
lvm.c
2
lvm.c
@ -754,10 +754,12 @@ void luaV_finishOp (lua_State *L) {
|
|||||||
case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */
|
case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */
|
||||||
int res = !l_isfalse(s2v(L->top - 1));
|
int res = !l_isfalse(s2v(L->top - 1));
|
||||||
L->top--;
|
L->top--;
|
||||||
|
#if defined(LUA_COMPAT_LT_LE)
|
||||||
if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */
|
if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */
|
||||||
ci->callstatus ^= CIST_LEQ; /* clear mark */
|
ci->callstatus ^= CIST_LEQ; /* clear mark */
|
||||||
res = !res; /* negate result */
|
res = !res; /* negate result */
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP);
|
lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP);
|
||||||
if (res != GETARG_k(inst)) /* condition failed? */
|
if (res != GETARG_k(inst)) /* condition failed? */
|
||||||
ci->u.l.savedpc++; /* skip jump instruction */
|
ci->u.l.savedpc++; /* skip jump instruction */
|
||||||
|
@ -474,17 +474,7 @@ The result of the call is always converted to a boolean.
|
|||||||
|
|
||||||
@item{@idx{__le}|
|
@item{@idx{__le}|
|
||||||
the less equal (@T{<=}) operation.
|
the less equal (@T{<=}) operation.
|
||||||
Unlike other operations,
|
Behavior similar to the less than operation.
|
||||||
the less-equal operation can use two different events.
|
|
||||||
First, Lua looks for the @idx{__le} metamethod in both operands,
|
|
||||||
like in the less than operation.
|
|
||||||
If it cannot find such a metamethod,
|
|
||||||
then it will try the @idx{__lt} metamethod,
|
|
||||||
assuming that @T{a <= b} is equivalent to @T{not (b < a)}.
|
|
||||||
As with the other comparison operators,
|
|
||||||
the result is always a boolean.
|
|
||||||
(This use of the @idx{__lt} event can be removed in future versions;
|
|
||||||
it is also slower than a real @idx{__le} metamethod.)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@item{@idx{__index}|
|
@item{@idx{__index}|
|
||||||
@ -1643,7 +1633,8 @@ all operations @emphx{wrap around},
|
|||||||
according to the usual rules of two-complement arithmetic.
|
according to the usual rules of two-complement arithmetic.
|
||||||
(In other words,
|
(In other words,
|
||||||
they return the unique representable integer
|
they return the unique representable integer
|
||||||
that is equal modulo @M{2@sp{64}} to the mathematical result.)
|
that is equal modulo @M{2@sp{n}} to the mathematical result,
|
||||||
|
where @M{n} is the number of bits of the integer type.)
|
||||||
}
|
}
|
||||||
|
|
||||||
@sect3{bitwise| @title{Bitwise Operators}
|
@sect3{bitwise| @title{Bitwise Operators}
|
||||||
@ -8537,6 +8528,12 @@ For instance, the result of @T{"1" + "2"} now is an integer,
|
|||||||
not a float.
|
not a float.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@item{
|
||||||
|
The use of the @idx{__lt} metamethod to emulate @id{__le}
|
||||||
|
has been removed.
|
||||||
|
When needed, this metamethod must be explicitly defined.
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
-- $Id: testes/coroutine.lua $
|
-- $Id: testes/coroutine.lua 2018-07-25 15:31:04 -0300 $
|
||||||
-- See Copyright Notice in file all.lua
|
-- See Copyright Notice in file all.lua
|
||||||
|
|
||||||
print "testing coroutines"
|
print "testing coroutines"
|
||||||
@ -619,10 +619,8 @@ end
|
|||||||
|
|
||||||
assert(run(function () if (a>=b) then return '>=' else return '<' end end,
|
assert(run(function () if (a>=b) then return '>=' else return '<' end end,
|
||||||
{"le", "sub"}) == "<")
|
{"le", "sub"}) == "<")
|
||||||
-- '<=' using '<'
|
|
||||||
mt.__le = nil
|
|
||||||
assert(run(function () if (a<=b) then return '<=' else return '>' end end,
|
assert(run(function () if (a<=b) then return '<=' else return '>' end end,
|
||||||
{"lt"}) == "<=")
|
{"le", "sub"}) == "<=")
|
||||||
assert(run(function () if (a==b) then return '==' else return '~=' end end,
|
assert(run(function () if (a==b) then return '==' else return '~=' end end,
|
||||||
{"eq"}) == "~=")
|
{"eq"}) == "~=")
|
||||||
|
|
||||||
@ -677,7 +675,7 @@ do -- a few more tests for comparsion operators
|
|||||||
return val(a) < val(b)
|
return val(a) < val(b)
|
||||||
end,
|
end,
|
||||||
}
|
}
|
||||||
local mt2 = { __lt = mt1.__lt } -- no __le
|
local mt2 = { __lt = mt1.__lt, __le = mt1.__le }
|
||||||
|
|
||||||
local function run (f)
|
local function run (f)
|
||||||
local co = coroutine.wrap(f)
|
local co = coroutine.wrap(f)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
-- $Id: testes/events.lua $
|
-- $Id: testes/events.lua 2018-07-25 15:31:04 -0300 $
|
||||||
-- See Copyright Notice in file all.lua
|
-- See Copyright Notice in file all.lua
|
||||||
|
|
||||||
print('testing metatables')
|
print('testing metatables')
|
||||||
@ -217,6 +217,13 @@ t.__lt = function (a,b,c)
|
|||||||
return a<b, "dummy"
|
return a<b, "dummy"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
t.__le = function (a,b,c)
|
||||||
|
assert(c == nil)
|
||||||
|
if type(a) == 'table' then a = a.x end
|
||||||
|
if type(b) == 'table' then b = b.x end
|
||||||
|
return a<=b, "dummy"
|
||||||
|
end
|
||||||
|
|
||||||
function Op(x) return setmetatable({x=x}, t) end
|
function Op(x) return setmetatable({x=x}, t) end
|
||||||
|
|
||||||
local function test ()
|
local function test ()
|
||||||
@ -236,15 +243,6 @@ end
|
|||||||
|
|
||||||
test()
|
test()
|
||||||
|
|
||||||
t.__le = function (a,b,c)
|
|
||||||
assert(c == nil)
|
|
||||||
if type(a) == 'table' then a = a.x end
|
|
||||||
if type(b) == 'table' then b = b.x end
|
|
||||||
return a<=b, "dummy"
|
|
||||||
end
|
|
||||||
|
|
||||||
test() -- retest comparisons, now using both `lt' and `le'
|
|
||||||
|
|
||||||
|
|
||||||
-- test `partial order'
|
-- test `partial order'
|
||||||
|
|
||||||
@ -266,14 +264,6 @@ t.__lt = function (a,b)
|
|||||||
return next(b) ~= nil
|
return next(b) ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
t.__le = nil
|
|
||||||
|
|
||||||
assert(Set{1,2,3} < Set{1,2,3,4})
|
|
||||||
assert(not(Set{1,2,3,4} < Set{1,2,3,4}))
|
|
||||||
assert((Set{1,2,3,4} <= Set{1,2,3,4}))
|
|
||||||
assert((Set{1,2,3,4} >= Set{1,2,3,4}))
|
|
||||||
assert((Set{1,3} <= Set{3,5})) -- wrong!! model needs a `le' method ;-)
|
|
||||||
|
|
||||||
t.__le = function (a,b)
|
t.__le = function (a,b)
|
||||||
for k in pairs(a) do
|
for k in pairs(a) do
|
||||||
if not b[k] then return false end
|
if not b[k] then return false end
|
||||||
@ -281,10 +271,15 @@ t.__le = function (a,b)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
assert(not (Set{1,3} <= Set{3,5})) -- now its OK!
|
assert(Set{1,2,3} < Set{1,2,3,4})
|
||||||
|
assert(not(Set{1,2,3,4} < Set{1,2,3,4}))
|
||||||
|
assert((Set{1,2,3,4} <= Set{1,2,3,4}))
|
||||||
|
assert((Set{1,2,3,4} >= Set{1,2,3,4}))
|
||||||
|
assert(not (Set{1,3} <= Set{3,5}))
|
||||||
assert(not(Set{1,3} <= Set{3,5}))
|
assert(not(Set{1,3} <= Set{3,5}))
|
||||||
assert(not(Set{1,3} >= Set{3,5}))
|
assert(not(Set{1,3} >= Set{3,5}))
|
||||||
|
|
||||||
|
|
||||||
t.__eq = function (a,b)
|
t.__eq = function (a,b)
|
||||||
for k in pairs(a) do
|
for k in pairs(a) do
|
||||||
if not b[k] then return false end
|
if not b[k] then return false end
|
||||||
@ -376,6 +371,7 @@ t1 = {}; c = {}; setmetatable(c, t1)
|
|||||||
d = {}
|
d = {}
|
||||||
t1.__eq = function () return true end
|
t1.__eq = function () return true end
|
||||||
t1.__lt = function () return true end
|
t1.__lt = function () return true end
|
||||||
|
t1.__le = function () return false end
|
||||||
setmetatable(d, t1)
|
setmetatable(d, t1)
|
||||||
assert(c == d and c < d and not(d <= c))
|
assert(c == d and c < d and not(d <= c))
|
||||||
t2 = {}
|
t2 = {}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user