lambdacrashclosurescommon-lispallegro-cl

Weird non-lisp errors with capturing labels with a lambda and `reduce`


I stumbled across a very weird bug (?) while I was developing an application in Allegro Common Lisp, v9.0 for Windows. I do not get the regular lisp errors, instead I get system errors encapsulated in a lisp condition. I managed to create a simple test-case to reproduce the error (it's very silly):

(defun lambda-producer ()
  (labels ((producer-label (x y)
                           (if (> x y) (- x y) (- y x)))
           (producer-label2 (x y)
                            (max (producer-label x 2) (producer-label y 3))))
  (lambda (x)
    (reduce (lambda (a b) (producer-label2 a b)) x))))

;; Get errors when calling:
(funcall (lambda-producer) (list 2 1 5 12))

I get a Stack overflow error for this test case every time I try it.

In my original code I got a plethora of errors, mostly segmentation faults, once a 'can not take car of <..> because it is not listp' and once Allegro crashed.

When running the same snippet (or my original code) in CLisp, everything runs fine and the REPL gives an answer.

My question: Am I doing something wrong or illegal here, or is there a bug in Allegro CL? If so, can we determine what that bug is and what to do about it?


Some experimentation seems to indicate that the reduce and double labels are essential in reproducing the error; replacing reduce with a funcall or if there is only one label does not give an error.


Solution

  • This is a compiler bug. Please contact Franz.

    CG-USER(8): (disassemble (lambda-producer))
    ;; disassembly of #<Closure Template Function (:INTERNAL LAMBDA-PRODUCER 0)>
    ;; formals: X
    ;; closure vectors:
    ;;      0: #<Closure (LABELS LAMBDA-PRODUCER PRODUCER-LABEL2) @ #xdfdda7a>
    ;;      1: #<Function (LABELS LAMBDA-PRODUCER PRODUCER-LABEL)>
    ;; constant vector:
    0: #<Closure Template Function (:INTERNAL (:INTERNAL LAMBDA-PRODUCER 0) 0)>
    1: REDUCE
    
    ;; code start: #x21869018:
       0: 55          pushl ebp
       1: 89 e5     movl    ebp,esp
       3: 83 ec 38  subl    esp,$56
       6: 89 75 fc  movl    [ebp-4],esi
       9: 89 5d e4  movl    [ebp-28],ebx
      12: 39 63 1a  cmpl    [ebx+26],esp
      15: 76 03     jbe 20
      17: ff 57 43  call    *[edi+67]      ; SYS::TRAP-STACK-OVFL
      20: 83 f9 01  cmpl    ecx,$1
      23: 74 03     jz  28
      25: ff 57 8b  call    *[edi-117]    ; SYS::TRAP-WNAERR
      28: 8b 5d 00  movl    ebx,[ebp+0]
      31: 8b 5b ec  movl    ebx,[ebx-20]
      34: 8b 5b fa  movl    ebx,[ebx-6]
      37: 80 7f cb 00 cmpb  [edi-53],$0        ; SYS::C_INTERRUPT-PENDING
      41: 74 03     jz  46
      43: ff 57 87  call    *[edi-121]    ; SYS::TRAP-SIGNAL-HIT
      46: 8b 56 12  movl    edx,[esi+18] ; #<Closure Template Function (:INTERNAL (:INTERNAL LAMBDA-PRODUCER 0) 0)>
      49: ff b7 43 fe pushl [edi-445] ; SYS::CLOSURE-HEADER
          ff ff 
      55: 8f 45 d0  popl    [ebp-48]
      58: ff b7 47 fe pushl [edi-441] ; SYS::CLOSURE-ADDRESS
          ff ff 
      64: 8f 45 d4  popl    [ebp-44]
      67: 89 55 d8  movl    [ebp-40],edx
      70: 89 5d dc  movl    [ebp-36],ebx
      73: 8d 5d e2  leal    ebx,[ebp-30]
      76: 89 c2     movl    edx,eax
      78: 89 d8     movl    eax,ebx
      80: 8b 5e 16  movl    ebx,[esi+22] ; REDUCE
      83: ff 57 27  call    *[edi+39]      ; SYS::TRAMP-TWO
      86: 89 7d f0  movl    [ebp-16],edi
      89: c9          leave
      90: 8b 75 fc  movl    esi,[ebp-4]
      93: c3          ret
    
    ;; disassembly of #<Closure Template Function (:INTERNAL (:INTERNAL LAMBDA-PRODUCER 0) 0)>
    ;; formals: A B
    
    ;; code start: #x2186ccc0:
       0: 55          pushl ebp
       1: 89 e5     movl    ebp,esp
       3: 83 ec 30  subl    esp,$48
       6: 89 75 fc  movl    [ebp-4],esi
       9: 89 5d e4  movl    [ebp-28],ebx
      12: 39 63 1a  cmpl    [ebx+26],esp
      15: 76 03     jbe 20
      17: ff 57 43  call    *[edi+67]      ; SYS::TRAP-STACK-OVFL
      20: 83 f9 02  cmpl    ecx,$2
      23: 74 03     jz  28
      25: ff 57 8b  call    *[edi-117]    ; SYS::TRAP-WNAERR
      28: 8b 5d 00  movl    ebx,[ebp+0]
      31: 8b 5b ec  movl    ebx,[ebx-20]
      34: 8b 5b fa  movl    ebx,[ebx-6]
      37: 80 7f cb 00 cmpb  [edi-53],$0        ; SYS::C_INTERRUPT-PENDING
      41: 74 03     jz  46
      43: ff 57 87  call    *[edi-121]    ; SYS::TRAP-SIGNAL-HIT
      46: ff 73 f6  pushl   [ebx-10]
      49: 8f 45 dc  popl    [ebp-36]        ; EXCL::|local-0|
      52: 8b 75 dc  movl    esi,[ebp-36] ; EXCL::|local-0|
      55: 89 fb     movl    ebx,edi
      57: b9 02 00 00 movl  ecx,$2
          00 
      62: ff 57 23  call    *[edi+35]      ; SYS::FUNCALL-TRAMP
      65: 89 7d f0  movl    [ebp-16],edi
      68: c9          leave
      69: 8b 75 fc  movl    esi,[ebp-4]
      72: c3          ret
      73: 90          nop
    

    At 49 and 52 of the second closure template, you can see that it's feeding itself to sys::funcall-tramp1 with 2 arguments2.


    1. sys::funcall-tramp is an internal that I think might perform or otherwise allow tail calls.

    2. According to ecx at 57, since ecx is what seems to be used to check if the internal sys::trap-wnaerr is called; I suppose wna stands for wrong number of arguments.