Skip to content

ordBooleanImpl aliases unsafeCoerceImpl: compare on Boolean crashes under Lua 5.1 #10

@Unisay

Description

@Unisay

src/Data/Ord.lua defines a single comparison helper and reuses it for every type:

local unsafeCoerceImpl = function(lt)
  return function(eq)
    return function(gt)
      return function(x)
        return function(y)
          if x < y then
            return lt
          elseif x == y then
            return eq
          else
            return gt
          end
        end
      end
    end
  end
end

return {
  ordBooleanImpl = (unsafeCoerceImpl),
  ...
}

For Int, Number, String and Char the < operator is well defined in Lua, so reusing the helper is fine. For Boolean it is not. Lua 5.1 (the pslua target) raises a runtime error on relational operators between booleans:

$ lua -e 'print(pcall(function() return false < true end))'
false	(command line):1: attempt to compare two boolean values

So any use of Ord Boolean (compare, <, <=, min, max, clamp) crashes at runtime instead of returning an ordering. The bug stays silent until a program actually compares two booleans.

Fix

Give ordBooleanImpl its own implementation that orders false < true without relying on <:

ordBooleanImpl = (function(lt)
  return function(eq)
    return function(gt)
      return function(x)
        return function(y)
          if not x and y then
            return lt
          elseif x == y then
            return eq
          else
            return gt
          end
        end
      end
    end
  end
end),

A working implementation already exists in @UnrelatedString's #3. It will be carried over from there with credit as part of the upstream-sync pass. The other ord*Impl aliases are correct and should stay on the shared helper.

Test coverage

The fix must ship with a regression test that fails on the current alias and passes after the fix: assert compare false true == LT, compare true false == GT, compare true true == EQ, and that min/max on Boolean do not error. This belongs in the prelude test suite being revived during upstream-sync. Without it the crash can regress unnoticed, since luacheck does not exercise the comparison.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions