Ed's Blog

A PhD Student's Musings

Typechecking Fail

If you spend enough time around me, you will probably hear me extol the virtues of statically typed languages such as ML or Haskell. Statically typed languages are great, because they find so many bugs before you even run your program. In fact, a good friend of mine jokes about OCaml program that “If it compiles it is correct!” This is not true, of course. OCaml does not check that your algorithm’s logic is correct, for instance.

Yesterday I encountered what I would consider a “type bug” in OCaml. There was human error involved, but I was still surprised that such a bug is possible in a type-checked program.

In BAP, we have a utility function, Util.timeout, which performs a computation, and if it takes too long, aborts it and raises an exception. The timeout function has type: int -> ('a -> 'b) -> 'a -> 'b, or in plain english, takes the number of seconds, a function f to execute, and a value x such that f x is computed.

The problem we observed was that timeouts were simply not working: the code would run for a very long time. The timeout function was being called, though, and at first glance I thought it looked fine: Util.timeout 3 Asmir_disasm.vsa_at p addr vsa_at takes two arguments, so this makes some intuitive sense. But a closer look reveals that we are psssing 4 arguments to Util.timeout, and it only takes 3 — How is this possible?

The problem is that timeout is polymorphic, and the type inference engine decided that 'a = Asmir.asmprogram and 'b = Type.addr -> Cfg.AST.G.t * unit, or in plain english: we want to partially evaluate the vsa_at function on only one argument, and timeout if that takes too long. Unfortunately, partial evaluation is usually very fast. The slowdown occurs when the extra argument, addr is then consumed by this partially evaluated function. Another way of viewing this is that OCaml thinks we wanted to do (Util.timeout 3 Asmir_disasm.vsa_at p) addr.

The solution is simply to force the partial evaluation to occur earlier, i.e., Util.timeout 3 (Asmir_disasm.vsa_at p) addr. I think that such a bug should at least trigger a warning, or maybe even require explicit parenthesization in such cases to denote currying is intentionally being applied. However, the consensus at lunch was that I am wrong, and the programmer should not make such mistakes. But isn’t the point of static type checking to find dumb mistakes?