August 2019

To prevent an infinite loop in Lisp, don't evaluate the full list. Evaluate the first element before evaluating the rest of the list.

Example:

(def sort (sortfn thelist)
   (while t (= a 1)))

This function shouldn't go in an infinite loop when defined. It goes in an infinite loop when called, but it shouldn't when defined.

The reason a function may go in an infinite loop when defined — even if the source code doesn't contain an explicit infinite loop like this simplified example — is that the function may call macros while the macros are missing. Anything can happen then. The sort function in Lip once went in an infinite loop because the def macro that escapes its parameters was missing.

To fix this, the IR can contain logic that skips evaluation of the remaining elements of the list and skips the function call, if the first element returns an error.

This is a reasonable fix. A similar check with an IR_KVERR instruction was needed for indirect assignment without GC and no syntax.

The twist is IR_MFAERR leads to a memory leak with indirect assignment without GC and no syntax. To link together the individual pieces of indirect get, after the first element is retrieved it's used as a free reference with an IR_REF to avoid creating a copy. An IR_REF worked well for this, but it was too general. It needs to become more specific: IR_KVREF, which considers the possibility that it's an error from IR_MFAERR that protects from an infinite loop. Since errors are newly allocated, IR_KVREF must always create a chained reference to the error to avoid a memory leak, instead of a free reference like IR_REF.