euslisp / EusLisp

EusLisp is an integrated programming system for the research on intelligent robots based on Common Lisp and Object-Oriented programming. [Manual](http://euslisp.github.io/EusLisp/manual.html ) [マニュアル](http://euslisp.github.io/EusLisp/jmanual.html )
Other
57 stars 50 forks source link

Segmentation fault occurs in funcall-ing a lambda-closure, when a function sets itself as the lambda-closure inside #463

Open knorth55 opened 3 years ago

knorth55 commented 3 years ago

when a function (spam1) sets itself as a lambda-closure inside (setq *spam-func* #'spam1), segmentation fault occurs in funcall (funcall *spam-func* (list 1))

test code: https://gist.github.com/knorth55/5f0e02f8c0103323d51d04aea482f57a#file-spam-l

Funcall case

'spam : segmentation fault

(defun spam1 (x)
  (setq *spam-func* #'spam1)
  (format t "spam1: ~A ~%" x)
  (unix::sleep 1))

;; segmentation fault
(spam1 0)
(print *spam-func*)
(funcall *spam-func* 1)
$ eus spam.l
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; helpsub ;; eushelp ;; fstringdouble 
EusLisp 9.27(b5be4c1) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
spam1: 0 
(lambda-closure spam1 15975616 0 (x) (setq *spam-func* #'spam1) (format t "spam1: ~A ~%" x) (unix:sleep 1))
;; Segmentation Fault.
;; in (setq *spam-func* #'spam1)
;; You are still in a signal handler.
;;Try reset or throw to upper level as soon as possible.
;; code=-916475408 x=c95fb0c0 addr=4
Fatal: 

'(lambda (x) (spam x)): OK

When we use (setq *spam-func* #'(lambda (x) (spam2 x))), we can avoid the segmentation fault.

(defun spam2 (x)
  (setq *spam-func* #'(lambda (xx) (spam2 xx)))
  (format t "spam2: ~A ~%" x)
  (unix::sleep 1))

;; OK
(spam2 0)
(print *spam-func*)
(funcall *spam-func* 1)
$ roseus spam.l 
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; intersection ;; geoclasses ;; geopack ;; geobody ;; primt ;; compose ;; polygon ;; viewing ;; viewport ;; viewsurface ;; hid ;; shadow ;; bodyrel ;; dda ;; helpsub ;; eushelp ;; xforeign ;; Xdecl ;; Xgraphics ;; Xcolor ;; Xeus ;; Xevent ;; Xpanel ;; Xitem ;; Xtext ;; Xmenu ;; Xscroll ;; Xcanvas ;; Xtop ;; Xapplwin 
connected to Xserver DISPLAY=:0
X events are being asynchronously monitored.
;; pixword ;; RGBHLS ;; convolve ;; piximage ;; pbmfile ;; image_correlation ;; oglforeign ;; gldecl ;; glconst ;; glforeign ;; gluconst ;; gluforeign ;; glxconst ;; glxforeign ;; eglforeign ;; eglfunc ;; glutil ;; gltexture ;; glprim ;; gleus ;; glview ;; toiv-undefined ;; fstringdouble irtmath irtutil irtc irtgeoc irtgraph ___time ___pgsql irtgeo euspqp pqp irtscene irtmodel irtdyna irtrobot irtsensor irtbvh irtcollada irtstl irtwrl irtpointcloud eusbullet bullet irtcollision irtx eusjpeg euspng png irtimage irtglrgb 
;; extending gcstack 0x61ae650[16374] --> 0x6638e90[32748] top=3c53
irtgl irtglc irtviewer 
EusLisp 9.27(b5be4c1 80d69c9) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
roseus ;; loading roseus("1.7.4-97-gb214284") on euslisp((9.27 ecublens Fri Nov 6 17:37:52 JST 2020 b5be4c1 80d69c9))
eustf roseus_c_util spam2: 0 
(lambda-closure nil 35317752 0 (x) (spam2 x))
spam2: 1 

Apply

'spam : segmentation fault

(defun spam1 (x)
  (setq *spam-func* #'spam1)
  (format t "spam1: ~A ~%" x)
  (unix::sleep 1))

;; segmentation fault
(spam1 0)
(print *spam-func*)
(apply *spam-func* (list 1))
$ eus spam.l
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; helpsub ;; eushelp ;; fstringdouble 
EusLisp 9.27(b5be4c1) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
spam1: 0 
(lambda-closure spam1 32879808 0 (x) (setq *spam-func* #'spam1) (format t "spam1: ~A ~%" x) (unix:sleep 1))
;; Segmentation Fault.
;; in (apply *spam-func* (list 1))
;; You are still in a signal handler.
;;Try reset or throw to upper level as soon as possible.
;; code=372082800 x=162d8740 addr=1f5b4c0
Fatal: 

'(lambda (x) (spam x)) : segmentation fault

(defun spam2 (x)
  (setq *spam-func* #'(lambda (xx) (spam2 xx)))
  (format t "spam2: ~A ~%" x)
  (unix::sleep 1))

;; segmentation fault
(spam2 0)
(print *spam-func*)
(apply *spam-func* (list 1))
$ eus spam.l
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; helpsub ;; eushelp ;; fstringdouble 
EusLisp 9.27(b5be4c1) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
spam2: 0 
(lambda-closure nil 9147584 0 (xx) (spam2 xx))
;; Segmentation Fault.
;; in (apply *spam-func* (list 1))
;; You are still in a signal handler.
;;Try reset or throw to upper level as soon as possible.
;; code=-584781136 x=dd24f180 addr=8b94c0
Fatal: 

'spam-test: segmentation fault

(defun spam3-test (xx)
  (format t "spam3-test: ~A ~%" xx)
  (unix::sleep 1))

(defun spam3 (x)
  (setq *spam-func* #'spam3-test)
  (format t "spam3: ~A ~%" x)
  (unix::sleep 1))

;; segmentation fault
(spam3 0)
(print *spam-func*)
(apply *spam-func* (list 1))
$ eus spam.l
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; helpsub ;; eushelp ;; fstringdouble 
EusLisp 9.27(b5be4c1) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
spam3: 0 
(lambda-closure spam3-test 10745024 0 (xx) (format t "spam3-test: ~A ~%" xx) (unix:sleep 1))
;; Segmentation Fault.
;; in (apply *spam-func* (list 1))
;; You are still in a signal handler.
;;Try reset or throw to upper level as soon as possible.
;; code=1339940976 x=4fdddf40 addr=a3f4c0
Fatal: 

'(lambda (x) (spam-test x)): segmentation fault

(defun spam4-test (xx)
  (format t "spam3-test: ~A ~%" xx)
  (unix::sleep 1))

(defun spam4 (x)
  (setq *spam-func* #'(lambda (xx) (spam4-test xx)))
  (format t "spam4: ~A ~%" x)
  (unix::sleep 1))

;; segmentation fault
(spam4 0)
(print *spam-func*)
(apply *spam-func* (list 1))
$ eus spam.l
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; helpsub ;; eushelp ;; fstringdouble 
EusLisp 9.27(b5be4c1) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
spam4: 0 
(lambda-closure nil 17814720 0 (xx) (spam4-test xx))
;; Segmentation Fault.
;; in (apply *spam-func* (list 1))
;; You are still in a signal handler.
;;Try reset or throw to upper level as soon as possible.
;; code=693430448 x=2954e780 addr=10fd4c0
Fatal: 

'(lambda (xx) (format t "spam5-lambda: ~A ~%" xx) : segmentation fault

(defun spam5 (x)
  (setq *spam-func*
        #'(lambda (xx)
            (format t "spam5-lambda: ~A ~%" xx)))
  (format t "spam5: ~A ~%" x)
  (unix::sleep 1))

;; segmentation fault
(spam5 0)
(print *spam-func*)
(apply *spam-func* (list 1))
$ eus spam.l
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; helpsub ;; eushelp ;; fstringdouble 
EusLisp 9.27(b5be4c1) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
spam5: 0 
(lambda-closure nil 31184064 0 (xx) (format t "spam5-lambda: ~A ~%" xx))
;; Segmentation Fault.
;; in (apply *spam-func* (list 1))
;; You are still in a signal handler.
;;Try reset or throw to upper level as soon as possible.
;; code=2103082992 x=7d5a7ec0 addr=1dbd4c0
Fatal: 

`(lambda-closure nil 0 0 (xx) (format t "spam6-lambda: ~A ~%" xx)) : OK

(defun spam6 (x)
  (setq *spam-func*
        `(lambda-closure nil 0 0 (xx)
                         (format t "spam6-lambda: ~A ~%" xx)))
  (format t "spam6: ~A ~%" x)
  (unix::sleep 1))

;; OK 
(spam6 0)
(print *spam-func*)
(apply *spam-func* (list 1))
$ eus spam.l 
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; helpsub ;; eushelp ;; fstringdouble 
EusLisp 9.27(b5be4c1) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
spam6: 0 
(lambda-closure nil 0 0 (xx) (format t "spam6-lambda: ~A ~%" xx))
spam6-lambda: 1 
1.eus$ 
knorth55 commented 3 years ago

I got this error when I try to add a new SMACH node in node callback function something like this.

(defun smach-callback (userdata)
  (send *sm* :add-node (instance state :init :name "new node" #'smach-callback)))

https://github.com/knorth55/rmui/blob/5040cd8a065903a2cadfefc92bc3b5093f5ac09e/rmuieus/euslisp/common/common-statenet-server.l#L48

knorth55 commented 3 years ago

I add test code and also tested with apply, but it is more complicated... test code: https://gist.github.com/knorth55/5f0e02f8c0103323d51d04aea482f57a#file-spam-l

Apply

'spam : segmentation fault

(defun spam1 (x)
  (setq *spam-func* #'spam1)
  (format t "spam1: ~A ~%" x)
  (unix::sleep 1))

;; segmentation fault
(spam1 0)
(print *spam-func*)
(apply *spam-func* (list 1))
$ eus spam.l
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; helpsub ;; eushelp ;; fstringdouble 
EusLisp 9.27(b5be4c1) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
spam1: 0 
(lambda-closure spam1 32879808 0 (x) (setq *spam-func* #'spam1) (format t "spam1: ~A ~%" x) (unix:sleep 1))
;; Segmentation Fault.
;; in (apply *spam-func* (list 1))
;; You are still in a signal handler.
;;Try reset or throw to upper level as soon as possible.
;; code=372082800 x=162d8740 addr=1f5b4c0
Fatal: 

'(lambda (x) (spam x)) : segmentation fault

(defun spam2 (x)
  (setq *spam-func* #'(lambda (xx) (spam2 xx)))
  (format t "spam2: ~A ~%" x)
  (unix::sleep 1))

;; segmentation fault
(spam2 0)
(print *spam-func*)
(apply *spam-func* (list 1))
$ eus spam.l
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; helpsub ;; eushelp ;; fstringdouble 
EusLisp 9.27(b5be4c1) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
spam2: 0 
(lambda-closure nil 9147584 0 (xx) (spam2 xx))
;; Segmentation Fault.
;; in (apply *spam-func* (list 1))
;; You are still in a signal handler.
;;Try reset or throw to upper level as soon as possible.
;; code=-584781136 x=dd24f180 addr=8b94c0
Fatal: 

'spam-test: segmentation fault

(defun spam3-test (xx)
  (format t "spam3-test: ~A ~%" xx)
  (unix::sleep 1))

(defun spam3 (x)
  (setq *spam-func* #'spam3-test)
  (format t "spam3: ~A ~%" x)
  (unix::sleep 1))

;; segmentation fault
(spam3 0)
(print *spam-func*)
(apply *spam-func* (list 1))
$ eus spam.l
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; helpsub ;; eushelp ;; fstringdouble 
EusLisp 9.27(b5be4c1) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
spam3: 0 
(lambda-closure spam3-test 10745024 0 (xx) (format t "spam3-test: ~A ~%" xx) (unix:sleep 1))
;; Segmentation Fault.
;; in (apply *spam-func* (list 1))
;; You are still in a signal handler.
;;Try reset or throw to upper level as soon as possible.
;; code=1339940976 x=4fdddf40 addr=a3f4c0
Fatal: 

'(lambda (x) (spam-test x)): segmentation fault

(defun spam4-test (xx)
  (format t "spam3-test: ~A ~%" xx)
  (unix::sleep 1))

(defun spam4 (x)
  (setq *spam-func* #'(lambda (xx) (spam4-test xx)))
  (format t "spam4: ~A ~%" x)
  (unix::sleep 1))

;; segmentation fault
(spam4 0)
(print *spam-func*)
(apply *spam-func* (list 1))
$ eus spam.l
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; helpsub ;; eushelp ;; fstringdouble 
EusLisp 9.27(b5be4c1) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
spam4: 0 
(lambda-closure nil 17814720 0 (xx) (spam4-test xx))
;; Segmentation Fault.
;; in (apply *spam-func* (list 1))
;; You are still in a signal handler.
;;Try reset or throw to upper level as soon as possible.
;; code=693430448 x=2954e780 addr=10fd4c0
Fatal: 

'(lambda (xx) (format t "spam5-lambda: ~A ~%" xx) : segmentation fault

(defun spam5 (x)
  (setq *spam-func*
        #'(lambda (xx)
            (format t "spam5-lambda: ~A ~%" xx)))
  (format t "spam5: ~A ~%" x)
  (unix::sleep 1))

;; segmentation fault
(spam5 0)
(print *spam-func*)
(apply *spam-func* (list 1))
$ eus spam.l
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; helpsub ;; eushelp ;; fstringdouble 
EusLisp 9.27(b5be4c1) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
spam5: 0 
(lambda-closure nil 31184064 0 (xx) (format t "spam5-lambda: ~A ~%" xx))
;; Segmentation Fault.
;; in (apply *spam-func* (list 1))
;; You are still in a signal handler.
;;Try reset or throw to upper level as soon as possible.
;; code=2103082992 x=7d5a7ec0 addr=1dbd4c0
Fatal: 

`(lambda-closure nil 0 0 (xx) (format t "spam6-lambda: ~A ~%" xx)) : OK

(defun spam6 (x)
  (setq *spam-func*
        `(lambda-closure nil 0 0 (xx)
                         (format t "spam6-lambda: ~A ~%" xx)))
  (format t "spam6: ~A ~%" x)
  (unix::sleep 1))

;; OK 
(spam6 0)
(print *spam-func*)
(apply *spam-func* (list 1))
$ eus spam.l 
configuring by "/home/shingo/install/jskeus/eus/lib/eusrt.l"
;; readmacro ;; object ;; packsym ;; common ;; constants ;; stream ;; string ;; loader ;; pprint ;; process ;; hashtab ;; array ;; mathtran ;; eusdebug ;; eusforeign ;; extnum ;; coordinates ;; tty ;; history ;; toplevel ;; trans ;; comp ;; builtins ;; par ;; helpsub ;; eushelp ;; fstringdouble 
EusLisp 9.27(b5be4c1) for Linux64 created on ecublens(Fri Nov 6 17:37:52 JST 2020)
spam6: 0 
(lambda-closure nil 0 0 (xx) (format t "spam6-lambda: ~A ~%" xx))
spam6-lambda: 1 
1.eus$ 
knorth55 commented 3 years ago

I tried with the same functions with no arguments, and it works. function argument seems similar to let (probably?), so I assume that closure does not work properly.

(defun noarg-spam1 ()
  (setq *spam-func* #'noarg-spam1)
  (format t "noarg-spam1: ~A ~%" nil)
  (unix::sleep 1))

(defun noarg-spam2 ()
  (setq *spam-func* #'(lambda () (noarg-spam2)))
  (format t "noarg-spam2: ~A ~%" nil)
  (unix::sleep 1))

(defun noarg-spam3-test (xx)
  (format t "noarg-spam3-test: ~A ~%" xx)
  (unix::sleep 1))

(defun noarg-spam3 ()
  (setq *spam-func* #'noarg-spam3-test)
  (format t "spam3: ~A ~%" nil)
  (unix::sleep 1))

(defun noarg-spam4-test (xx)
  (format t "noarg-spam4-test: ~A ~%" xx)
  (unix::sleep 1))

(defun noarg-spam4 ()
  (setq *spam-func* #'(lambda (xx) (noarg-spam4-test xx)))
  (format t "noarg-spam4: ~A ~%" nil)
  (unix::sleep 1))

(defun noarg-spam5 ()
  (setq *spam-func*
        #'(lambda (xx)
            (format t "noarg-spam5-lambda: ~A ~%" xx)))
  (format t "noarg-spam5: ~A ~%" nil)
  (unix::sleep 1))

(defun noarg-spam6 ()
  (setq *spam-func*
        `(lambda-closure nil 0 0 (xx)
                         (format t "noarg-spam6-lambda: ~A ~%" xx)))
  (format t "spam6: ~A ~%" nil)
  (unix::sleep 1))

;; OK 
(noarg-spam1)
(format t "*spam-func*: ~A~%" *spam-func*)
(funcall *spam-func*)

;; OK
(noarg-spam2)
(format t "*spam-func*: ~A~%" *spam-func*)
(funcall *spam-func*)

;; OK
(noarg-spam1)
(format t "*spam-func*: ~A~%" *spam-func*)
(apply *spam-func* nil)

;; OK
(noarg-spam2)
(format t "*spam-func*: ~A~%" *spam-func*)
(apply *spam-func* nil)

;; OK
(noarg-spam3)
(format t "*spam-func*: ~A~%" *spam-func*)
(apply *spam-func* (list 1))

;; OK
(noarg-spam4)
(format t "*spam-func*: ~A~%" *spam-func*)
(apply *spam-func* (list 1))

;; OK
(noarg-spam5)
(format t "*spam-func*: ~A~%" *spam-func*)
(apply *spam-func* (list 1))

;; OK
(noarg-spam6)
(format t "*spam-func*: ~A~%" *spam-func*)
(apply *spam-func* (list 1))
Affonso-Gui commented 3 years ago

There are two conditions for lambda-closures to cause a segmentation fault in eus:

  1. The bind frame is non-global (non zero) (lambda-closure name bind-frame fbind-frame &rest body)
  2. The function body references a non-local variable.

The problem has nothing to do with funcall or apply or lambda, but is rather originated from trying to access the *spam-func* global variable from a non-zero bind frame.

(defun spam1 (x)
  (if (zerop x) (setq *spam-func* #'spam1))
  (format t "spam1: ~A ~%" x)
  (unix::sleep 1))

;; ok
(spam1 0)
(print *spam-func*)
(funcall *spam-func* 1)

;; segmentation fault
(funcall *spam-func* 0)

And yes, function arguments are similar to let and introduce new bind frames, so within functions with no arguments the bind frame is global (zero) and therefore segmentation faults does not occur.

(defun foo () #'foo)
(defun bar (b) #'bar)

(foo)
;; (lambda-closure foo 0 0 nil #'foo)
(bar 1)
;; (lambda-closure bar 94419140493560 0 (b) #'bar)

Segmentation also happens for fbind-frames similarly.

There is no proper solution for this problem yet, but some workarounds are:

knorth55 commented 3 years ago

@Affonso-Gui thanks, it is quite unstable implementation of closure. I'm not familiar with the closure, but I think we can disable closure function because euslisp is object-oriented lisp. Another solution is implement the closure properly, but it will take plenty of time. (BTW, whole structure of euslisp should be closure-based language, and rewrite class as one implementation of closure, i thought)

(setq *a* 0)

(let ((a 0))
  (setq *spam0-func*
        #'(lambda ()
            (setq a (+ a 1))
            (print a))))

(let ((a 0))
  (setq *spam1-func*
        `(lambda-closure nil 0 0 ()
            (setq a (+ a 1))
            (print a))))

(let ((a 0))
  (setq *spam2-func*
        #'(lambda ()
            (setq *a* (+ *a* 1))
            (print *a*))))

; (print *spam0-func*)
; (funcall *spam0-func*)

; (print *spam1-func*)
; (funcall *spam1-func*)

; (print *spam2-func*)
; (funcall *spam2-func*)
Affonso-Gui commented 3 years ago

The core of the problem itself is not that complicated. We either need to find a way to validate pointer addresses before usage or keep them while being referenced.

It is one project that has been in my list for quite some time but I was kind of reluctant to dig in the garbage collector. Maybe it's time to take a more serious try at the problem.

Affonso-Gui commented 3 years ago

@knorth55

So I took the time and was able to develop a proof-of-concept implementation of closures using cons cells instead of the current struct bindframe https://github.com/Affonso-Gui/EusLisp/commit/7e3475595d93d88073dfa1270c6bfa32fb7eb241

eus$ (let ((a 0)) (setq fn #'(lambda () (incf a))))
;; (lambda-closure nil ((a . 0)) 0 nil (incf a))                                                     
eus$ (funcall fn)
;; 1                                                                                                 
eus$ (funcall fn)
;; 2                                                                                                 
eus$ (funcall fn)
;; 3                                                                                                 
eus$ (funcall fn)
;; 4                                                                                                 
eus$ (funcall fn)
;; 5                                                                                                 
eus$ fn
;; (lambda-closure nil ((a . 5)) 0 nil (incf a))    

We probably should use some other custom class to make things a little bit more hidden from the end user, and definitely do the same for the fletframe (and maybe the specialbindframe?), but as I said this is only of a proof-on-concept implementation.

The problem with the current method is that new frames are stacked on top of a vsp which is reset after each evaluation, making it impossible to keep track of bind frames simply by using their memory address. The solution was to use proper lisp objects (in this case cons cells) rather than c structures + integer addresses. This way the objects are allocated more carefully and the gc can do its magic to keep referenced frames alive while getting rid of old ones.

The core implementation itself is rather simple, but as you can see in the diff it is kinda tricky because I had to replace all references and all NULLs to NILs.

CC. @708yamaguchi @pazeshun