armedbear / abcl

Armed Bear Common Lisp <git+https://github.com/armedbear/abcl/> <--> <svn+https://abcl.org/svn> Bridge
https://abcl.org#rdfs:seeAlso<https://gitlab.common-lisp.net/abcl/abcl>
Other
289 stars 29 forks source link

LABELS local function fails to shadow top-level function under some circumstances #591

Open slburson opened 1 year ago

slburson commented 1 year ago

To see this, in a fresh ABCL 1.9.1, do:

(ql:quickload "fset")
(in-package :fset-user)
(defun foo (x)
  (labels ((bar (x)
             (cond ((null x) nil)
                   ((eq (car x) '&rest) (tail (cadr x)))
                   (t (cons (car x) (bar (cdr x))))))
           (tail (x)
             (list 'local-tail x)))
    (bar x)))
(compile 'foo)
(foo '(a &rest b))

Expected: (A LOCAL-TAIL B)

Actual: 
The value B is not of type LIST.
   [Condition of type TYPE-ERROR]

What seems to be going on there is that fset:tail, which is imported, is defined thus:

(defun tail (list)
  "Another name for the `cdr' operation on lists."
  (cdr list))

(declaim (inline tail))

My guess is that the inline declaration is somehow confusing matters so that the local function tail doesn't get called from bar. But oddly, I have not been able to produce a simple example, without using FSet, that fails. Evidently, there's some other condition required, that I haven't identified, for the bug to bite. It's easy to see that the name matters, though; just rename the local function:

(defun foo (x)
           (labels ((bar (x)
                      (cond ((null x) nil)
                            ((eq (car x) '&rest) (tailx (cadr x)))
                            (t (cons (car x) (bar (cdr x))))))
                    (tailx (x)
                      (list 'local-tail x)))
             (bar x)))

This works as expected.

easye commented 1 year ago

After tracing the precompiler with

(trace)
(JVM:COMPILE-DEFUN JVM::P1-COMPILAND
                   JVM::CONSTRUCT-FLET/LABELS-FUNCTION
                   JVM::P1-LABELS)

one can (sorta) see in

; Loading /Users/evenson/work/abcl/t/compiler-inline.lisp ...
; Compiling /Users/evenson/work/abcl/t/eg/inline-labels.lisp ...
; (IN-PACKAGE :FSET-USER)
; (DEFUN FOO ...)
  0: (JVM:COMPILE-DEFUN FSET-USER::FOO (LAMBDA (FSET-USER::X) (BLOCK FSET-USER::FOO (LABELS ((FSET-USER::BAR (FSET-USER::X) (NEW-LET:COND ((NULL FSET-USER::X) NIL) ((EQ (CAR FSET-USER::X) (QUOTE &REST)) (FSET:TAIL (CADR FSET-USER::X))) (T (CONS (CAR FSET-USER::X) (FSET-USER::BAR (CDR FSET-USER::X)))))) (FSET:TAIL (FSET-USER::X) (LIST (QUOTE FSET-USER::LOCAL-TAIL) FSET-USER::X))) (FSET-USER::BAR FSET-USER::X)))) NIL #P"/Users/evenson/work/abcl/t/eg/inline_labels_1.cls" #<FILE-STREAM {67E5EF70}> NIL)
    1: (JVM::P1-COMPILAND #<JVM::COMPILAND {61C7079D}>)
      2: (JVM::P1-LABELS (LABELS ((FSET-USER::BAR (FSET-USER::X) (BLOCK #:G698178 (IF (NULL FSET-USER::X) (RETURN-FROM #:G698178 (PROGN NIL))) (IF (EQ (CAR FSET-USER::X) (QUOTE &REST)) (RETURN-FROM #:G698178 (LET ((LIST (CADR FSET-USER::X))) (BLOCK FSET:TAIL (CDR LIST))))) (RETURN-FROM #:G698178 (CONS (CAR FSET-USER::X) (FSET-USER::BAR (CDR FSET-USER::X)))))) (FSET:TAIL (FSET-USER::X) (LIST (QUOTE FSET-USER::LOCAL-TAIL) FSET-USER::X))) (FSET-USER::BAR FSET-USER::X)))
        3: (JVM::CONSTRUCT-FLET/LABELS-FUNCTION (FSET-USER::BAR (FSET-USER::X) (BLOCK #:G698178 (IF (NULL FSET-USER::X) (RETURN-FROM #:G698178 (PROGN NIL))) (IF (EQ (CAR FSET-USER::X) (QUOTE &REST)) (RETURN-FROM #:G698178 (LET ((LIST (CADR FSET-USER::X))) (BLOCK FSET:TAIL (CDR LIST))))) (RETURN-FROM #:G698178 (CONS (CAR FSET-USER::X) (FSET-USER::BAR (CDR FSET-USER::X)))))))
        3: CONSTRUCT-FLET/LABELS-FUNCTION returned #<JVM::LOCAL-FUNCTION {6D80881C}>
        3: (JVM::CONSTRUCT-FLET/LABELS-FUNCTION (FSET:TAIL (FSET-USER::X) (LIST (QUOTE FSET-USER::LOCAL-TAIL) FSET-USER::X)))
        3: CONSTRUCT-FLET/LABELS-FUNCTION returned #<JVM::LOCAL-FUNCTION {2AF1F898}>
        3: (JVM::P1-COMPILAND #<JVM::COMPILAND {62A70CD9}>)
        3: P1-COMPILAND returned (LAMBDA (FSET-USER::X) #<JVM::BLOCK-NODE {3B0504FF}>)
        3: (JVM::P1-COMPILAND #<JVM::COMPILAND {522CBBEB}>)
        3: P1-COMPILAND returned (LAMBDA (FSET-USER::X) #<JVM::BLOCK-NODE {77BCBC0E}>)
      2: P1-LABELS returned #<JVM::LABELS-NODE {2DA7A7E4}>
    1: P1-COMPILAND returned (LAMBDA (FSET-USER::X) #<JVM::BLOCK-NODE {3E167C53}>)
  0: COMPILE-DEFUN returned #<JVM::ABCL-CLASS-FILE {4C413149}>

that between the calls to jvm:compile-defun and jvm::p1-compiland, the fset-user:tail symbol has been expanded to its inline declaration. Which is sorta correct as the the function-name of the labels function is the symbol fset-user:tail which has been inherited from fset:tail. I need to think through how the Lisp reader should be processing the symbols in LABELS:

  1. Should the labels function-name really be fset:tail? Or should it be another symbol in an "implicit package" somehow?

  2. In the LABELS function definitions, how does one distinguish between something referring to a labels function-name and something in the current package?

SBCL and ECL succeed on the test in https://github.com/armedbear/abcl/pull/610.