;-*- Mode:     Lisp -*-
;;;; Author:   Paul Dietz
;;;; Created:  Sat Oct 12 09:24:36 2002
;;;; Contains: Tests for LET, LET*



;;; LET and LET* are also heavily exercised in the many other tests.

;;; NOTE!  Some of these tests bind a variable with the same name
;;; more than once.  This apparently has underdetermined semantics that
;;; varies in different Lisps.

(deftest let.1
  (let ((x 0)) x)
  0)

(deftest let.2
  (let ((x 0) (y 1)) (values x y))
  0 1)

(deftest let.3
  (let ((x 0) (y 1)) (declare (special x y)) (values x y))
  0 1)

(deftest let.4
  (let ((x 0))
    (let ((x 1))
      x))
  1)

(deftest let.5
  (let ((x 0))
    (let ((#:x 1))
      x))
  0)

(deftest let.6
  (let ((x 0))
    (declare (special x))
    (let ((x 1))
      (values x (locally (declare (special x)) x))))
  1 0)

(deftest let.7
  (let ((x '(a b c)))
    (declare (dynamic-extent x))
    x)
  (a b c))

;;;(deftest let.8
;;;  (let ((x 0) (x 1)) x)
;;;  1)

(deftest let.9
  (let (x y z) (values x y z))
  nil nil nil)

;;; (deftest let.10
;;;  (let ((x 1) x) x)
;;;  nil)

(deftest let.11
  (let ((x 1))
    (list x
          (let (x)
            (declare (special x))
            x)
          x))
  (1 nil 1))

;;; (deftest let.12
;;;  (let ((x 0))
;;;    (values
;;;     (let ((x 20)
;;;        (x (1+ x)))
;;;       x)
;;;     x))
;;;   1 0)

;;; (deftest let.13
;;;  (flet ((%f () (declare (special x))
;;;          (if (boundp 'x) x 10)))
;;;    (let ((x 1)
;;;       (x (1+ (%f))))
;;;      (declare (special x))
;;;      x))
;;;  11)

;;; Tests of large number of LET variables
(deftest let.14
  (let* ((n 100)
         (vars (mapcar #'gensym (make-list n :initial-element "G")))
         (expr `(let ,(let ((i 0))
                        (mapcar #'(lambda (v) (list v (incf i))) vars))
                  ,(let ((sumexpr 0))
                     (dolist (v vars)
                       (setq sumexpr `(+ ,v ,sumexpr)))
                     sumexpr)))
         (val (eval expr)))
    (or (eqlt val (/ (* n (1+ n)) 2)) (list val)))
  t)

;;; Test that all non-variables exported from COMMON-LISP can be bound
;;; in LET forms.
(deftest let.15
  (loop for s in *cl-non-variable-constant-symbols*
        for form = `(ignore-errors (let ((,s 17)) ,s))
        unless (eql (eval form) 17)
        collect s)
  nil)

;;; Check that LET does not have a tagbody
(deftest let.16
  (block done
    (tagbody
     (let () (go 10) 10 (return-from done 'bad))
     10
     (return-from done 'good)))
  good)

;;; Check that free declarations do not apply to the init forms

(deftest let.17
  (let ((x :bad))
    (declare (special x))
    (let ((x :good)) ;; lexical binding
      (let ((y x))
        (declare (special x)) ;; free declaration
        y)))
  :good)

(deftest let.17a
  (funcall
   (compile
    nil
    '(lambda ()
       (let ((x :bad))
         (declare (special x))
         (let ((x :good)) ;; lexical binding
           (let ((y x))
             (declare (special x)) ;; free declaration
             y))))))
  :good)

(deftest let.18
  (let ((foo 'special))
    (declare (special foo))
    (let ((foo 'lexical))
      (locally (declare (special foo)))
      foo))
  lexical)

(deftest let.19
  (loop for k in lambda-list-keywords
        unless (eql (eval `(let ((,k :foo)) ,k)) :foo)
        collect k)
  nil)

;;; Macros are expanded in the appropriate environment

(deftest let.20
  (macrolet ((%m (z) z))
            (let () (expand-in-current-env (%m :good))))
  :good)

(deftest let.21
  (macrolet ((%m (z) z))
            (let ((x (expand-in-current-env (%m 1)))) (+ x x x)))
  3)
