Skip to content

Array-literal pattern matching reads 0-based indices from 1-based tables #49

@Unisay

Description

@Unisay

Matching on an array literal pattern crashes at runtime. The binders are addressed with the de Bruijn-style 0-based index straight from the IR, but arrays are stored as 1-based Lua tables, so every element is read one slot too low — the first read is always nil.

Repro:

module Main where

import Prelude
import Effect (Effect)
import Effect.Console (log)

firstTwo :: Array Int -> Int
firstTwo = case _ of
  [a, b] -> a + b
  _ -> -1

main :: Effect Unit
main = log (show (firstTwo [10, 20]))

Generated Lua for the match (note v[0]/v[1] against a { [1]=10, [2]=20 } table):

if 2 == #(v) then
  return M.Data_Semiring_semiringInt.add(v[0])(v[1])

Running it:

lua: attempt to perform arithmetic on upvalue 'x' (a nil value)

The index originates in fromCfn in lib/Language/PureScript/Backend/IR.hs, where array binders are numbered zip [0 ..] binders and turned into TakeIndex index. IR.ArrayIndex is then emitted in lib/Language/PureScript/Backend/Lua.hs as Lua.varIndex (Lua.Integer index) with no offset, while the array constructor and the arrays FFI (indexImpl returns xs[i + 1]) are 1-based. The Lua-level ArrayIndex needs index + 1, or the offset has to be applied somewhere on the way to codegen.

No golden currently exercises array-literal patterns, which is why this went unnoticed. A fix should add an eval golden over the repro above.

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