error: the value NIL is not of type SB-C::NODE

Bug #551227 reported by Frank Duncan on 2010-03-29
This bug affects 3 people
Affects Status Importance Assigned to Milestone

Bug Description

When defining a function with a magic set of conditions, get a weird error.

This is the offending code:

(defun test (a b) (if (some (lambda (x) (zerop (mod a x))) b) (test a b)))

I've tried to distill it to it's essence, but that causes the error when entered in (not when the function is run)

" The value NIL is not of type SB-C::NODE.

Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL."

If you remove the recursive call to test, the function defines just fine, if you change "some" to "any", it works fine, if you change "mod" to "+", it works fine.

sbcl --version
SBCL 1.0.35.gentoo-r0

uname -a
Linux elly #1 SMP Sun Oct 18 13:37:26 CDT 2009 i686 Intel(R) Core(TM)2 Duo CPU E6750 @ 2.66GHz GenuineIntel GNU/Linux


Frank Duncan (frank-kank) wrote :

If you change "mod" to "rem" at higher debugs, same error. At debug of 0, it goes through.

Changed in sbcl:
status: New → Confirmed
importance: Undecided → High
tags: added: compiler
summary: - Weird error defining function using combination of 'some, mod, lambda,
- defun, recursion'
+ error: the value NIL is not of type SB-C::NODE
Heka Treep (zena-treep) wrote :

This may be an early bug in the `some' function (`defquantifier' in seq.lisp has two KLUDGE). For example, this code work fine:

(defun test (a b) (if (some (eval `(lambda (x) (zerop (mod ,a x)))) b) (test a b)))

also, a simple implementation of the function also works:

(declaim (inline some2))
(defun some2 (pred first-seq &rest more-seqs)
  (flet ((map-me (&rest rest)
                 (let ((pred-value (apply pred rest)))
                   (when pred-value
                     (return-from some2 pred-value)))))
    (declare (inline map-me))
    (apply #'map nil #'map-me first-seq more-seqs)

(defun test (a b) (if (some2 (lambda (x) (zerop (mod a x))) b) (test a b)))

while `defquantifier' use a compiler-macro.

Heka Treep (zena-treep) wrote :

This is (similar) code from macroexpansion of `some' compiler macro:

#'(lambda (a)
    (if (funcall #'(lambda (p s)
                     (block :block
                       (map nil
                            #'(lambda (e)
                                (return-from :block (funcall p e)))
                 #'(lambda (x) (f (mod a x)))
        (g a)))

and it ruins on ir1 phase - sb-c::node-home-lambda (from ir1utils.lisp) get this NIL value.

A slightly more minimal test case:

#'(lambda (a)
  (if (let ((s a))
        (block :block
          (map nil
               #'(lambda (e)
                   (return-from :block (f (mod a e))))
      (g a)))

Any further attempt I've made to reduce the test case caused the bug to disappear.

The failure appears to be that CONSTRAIN-REF-TYPE calls CHANGE-REF-LEAF to alter the A referred to in (G A) to be NUMBER (from the LET-converted inline-expansion of MOD). This leads to the toplevel lambda attempting to CLOSE-OVER a variable from a lambda that is not part of its lexical environment.

Based on my earlier analysis, I came up with the attached patch, which appears to do the trick. What I'm uncomfortable about is that I don't understand the constraint-propagation logic that it modifies, and I'm not entirely convinced that the method I've chosen for walking the chain of lexical environments is correct.

tags: added: review
Nikodemus Siivola (nikodemus) wrote :

See also bug #720382, which may or may not be a duplicate of this.

Changed in sbcl:
assignee: nobody → Nikodemus Siivola (nikodemus)
Nikodemus Siivola (nikodemus) wrote :

Bug #720382 is unrelated.

Attached an alternative patch: Alastair, I think LAMBDA-PARENT is the right chain to walk. Otherwise this looks sensible to me.

Assigning back to you, but if you feel uncertain or don't have the time, feel free to pass the ball right back to me.

Changed in sbcl:
assignee: Nikodemus Siivola (nikodemus) → Alastair Bridgewater (alastair-bridgewater)
status: Confirmed → Triaged

The difference between the two patches appears to be commentary and the LAMBDA-PARENT vs. NODE-HOME-LAMBDA/FUNCTIONAL-ALLOCATOR thing. Analysis of the setup of FUNCTIONAL-ALLOCATOR and LAMBDA-PARENT fields show that these two chains are more-or-less semantically equivalent, and certainly sufficiently so in this case.

The actual logic in CONSTRAIN-REF-TYPE appears to be checking an EQLity constraint between two LEAFs (LEAVes?)

Continuing from that keyboard fumble...

The actual logic in CONSTRAIN-REF-TYPE appears to be checking an EQLity constraint between two LEAFs (LEAVes?) that are somehow similar-but-not-too-similar, and substituting one for the other in a given REF. The problem comes when the OTHER LEAF is no longer visible. Preventing the substitution is one possible (and easy) alternative, but another could be to promote the OTHER LEAF to a sufficiently outer lambda (if there is one) so that it is still available in the context of the REF.

I'm a lot happier with these patches now than when I posted my original version, even if I still don't know what's going on during constraint propagation. Will commit "soonish".


Changed in sbcl:
status: Triaged → Fix Committed
assignee: Alastair Bridgewater (alastair-bridgewater) → nobody
Changed in sbcl:
status: Fix Committed → Fix Released
To post a comment you must log in.
This report contains Public information  Edit
Everyone can see this information.

Other bug subscribers