Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
slburson opened this issue Jun 5, 2023 · 1 comment

Comments

@slburson
Copy link

slburson commented Jun 5, 2023

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 added a commit to easye/abcl that referenced this issue Jul 15, 2023
@easye
Copy link
Collaborator

easye commented Jul 15, 2023

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 #610.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants