lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


As well I consider that the documented syntax of table constructors, where explicit keys can be arbitrary expressions whose value is unpredictable, such as a function call, is fundamentally flawed if the behavior and notably the order of assignments, is not defined at all. Thus syntax serves no serious purpose

I would say the same with the unary operator # which can return anything, as well as the corel ibrary function table.maxn()....

Lack of specification there cause nasty bugs that are later hard to locate! And the undefined behavior cannot even  be detected reliably at runtime because the sequence order is not easy to reproduce and depends on many unknown and undetectable factors.... 

Le jeu. 23 juil. 2020 à 16:20, Coda Highland <chighland@gmail.com> a écrit :
The documentation doesn't define the behavior, but the source code does. This isn't some underlying UB issue where you could get different results based on the underlying hardware. Given the official releases of the Lua interpreters and knowing the code they were built with, this trickery is entirely well-behaved and reliable.

That said, arguing about this just indicates that people are talking on different levels. Nobody here is WRONG. But one side is arguing about what you SHOULD do, while the other is arguing about what you CAN do.

Duh, of course it's a bad idea. You would never want to write code that relies on this behavior. There would be no promise that it would continue to operate as intended in the future. Even for version detection this isn't actually a GOOD idea because what if you're running on an implementation of Lua that isn't from PUC-Rio or LuaJIT? It certainly wouldn't identify Fengari or Ravi, and there's not even any guarantee that the version this function returns would match the version of the Lua language that those versions are intended to follow.

But as has been repeatedly asserted: if you're using one of the versions this function supports... it does WORK.

/s/ Adam


On Thu, Jul 23, 2020 at 8:53 AM Philippe Verdy <verdyp@gmail.com> wrote:
If the documentation does not define the  behavior, then the detection is also undefined as you may just test one case for which you would get one result or another case where you would get the opposite answer.
The detection will ever be fullproof. And it is not reliable to depend on this detection, except by not writing ANY code that depends on an undefined behavior.

This means that ANY code that uses table constructors mixing implicit (autoincremented) integer keys and other explicit integer keys IS UNRELIALE. and it will remain unreliable and will cause unexpected bugs on all versions of LUA that still don't specify the behavior and checks/enforces the rules.


It's very strange then that Lua accepts a simple syntax like {1, [1]=2} without being able to say what will be the value stored at index [1]. Your test may give an answer correct only for some sets of keys, where the order of assignments will be in one way, but for other sets of keys, you could as well get the inverse result, and you would NOT detect LuaJIT, and your code would make false decisions.

In summary, table constructors should ONLY be pure sequences with implicit keys, OR all members should be explicitly keyed.

I see it as a major defect of the language that should be solved by enforcing the rules to define the expected behavior. This should not break existing applications more than when they already use today the undefined behavior assuming that it is one way when it could still be the other way even on exactly the same platform instance.

Given that, this means that Lua is unable to say that it is portable, and every developer is forced to test their apps on many implementations, and some will be never be tested, notably newer LUA engines that could appear at any time or could be upgraded.

This means that for critical applications, YOU MUST NOT upgrade Lua, but this application Must provide its own copy of the engine. And reusability is also compromised notably for external Lua libraries used that would depend also on a behavior which is defined only for some versions of implementations. 

And this can cause serious security bugs that could be exploited later. 

Undefined behavior has already caused lot of damages in many languages and platforms. Inclicluding hardware platforms like CPUs. LUA devopers should make efforts to define the behavior and enforce them by conformance and coverage tests so that  platforms can fix them and then be safely upgradable and serviceable.

Until then, do not upgrade your existing Lua engine, and do not use any third party library that was not explicitly tested fie exactly the same version of the implementation.

Anlther way to solve this problem would be to develop a minter that will detect the code using undefined behavior, so that this code can be rewritten.

The assumptions that Lua tables are capable of mixing sequences and associative keyed arrays is then wrong, it has never been specified correctly. 

Le mer. 22 juil. 2020 à 21:05, Andrew Gierth <andrew@tao11.riddles.org.uk> a écrit :
>>>>> "Philippe" == Philippe Verdy <verdyp@gmail.com> writes:

 Philippe>        if ({false, [1] = true})[1] then
 Philippe>              return 'LuaJIT'

 Philippe> I'm not sure that the evaluation of the table constructor is
 Philippe> documented correctly. Here you assign two different boolean
 Philippe> values to the same index [1], this creates a collision and
 Philippe> the evaluation order matters.

The evaluation order matters, and LuaJIT happens to do it the opposite
way to (all) the reference implementations; the manual explicitly states
that the order of evaluation is undefined:

  The order of the assignments in a constructor is undefined. (This
  order would be relevant only when there are repeated keys.)

Hence using it to distinguish LuaJIT from other implementations.

Note that almost everything in this version detection logic is based on
language features that are left undefined. For example the [f()==f()]
test is seeing whether a closure with no upvalues allocates a new object
or not each time, of which the manual says:

  Functions created at different times but with no detectable
  differences may be classified as equal or not (depending on internal
  caching details).

Even the 1/0 parts rely on the implementation using IEEE floats, or at
least some float implementation that has both +/- infinity and +/- 0,
and that for integers in 5.3/5.4 there is no separate -0; if you build
Lua with only integer support, the code will not work.

--
Andrew.