LET: signal an error when multiple bindings of the same name occur
authorDaniel Kochmański <daniel@turtleware.eu>
Mon, 17 Aug 2015 08:07:31 +0000 (10:07 +0200)
committerDaniel Kochmański <daniel@turtleware.eu>
Mon, 17 Aug 2015 08:07:31 +0000 (10:07 +0200)
This situation is unspecified by ANSI spec – also hard to find
situation, when such construct wouldn't be an error.

Signed-off-by: Daniel Kochmański <daniel@turtleware.eu>
src/c/compiler.d
src/cmp/cmplet.lsp
src/new-cmp/cmplet.lsp

index 31c3d91..ee27855 100644 (file)
@@ -1572,6 +1572,10 @@ c_let_leta(cl_env_ptr env, int op, cl_object args, int flags) {
                         FEillegal_variable_name(var);
                 if (op == OP_PBIND) {
                         compile_form(env, value, FLAG_PUSH);
+                        if (ecl_member_eq(var, vars))
+                                FEprogram_error_noreturn
+                                        ("LET: The variable ~s occurs more than "
+                                         "once in the LET.", 1, var);
                         vars = CONS(var, vars);
                 } else {
                         compile_form(env, value, FLAG_REG0);
index 3b6b3d4..279bb3d 100644 (file)
           ((null (rest bindings))
            (c1let/let* 'let* bindings args))
           (t
-           (loop with temp
-              for b in bindings
-              if (atom b)
-              collect b into real-bindings
-              else collect (setf temp (gensym "LET")) into temp-names and
-              collect (cons temp (cdr b)) into temp-bindings and
-              collect (list (car b) temp) into real-bindings
-              finally
+           (loop :with temp
+              :for b :in bindings
+                :if (atom b)
+                  :collect b :into real-bindings :and
+                  :collect b :into names
+                :else
+                  :collect (setf temp (gensym "LET")) :into temp-names    :and
+                  :collect (cons temp (cdr b))        :into temp-bindings :and
+                  :collect (list (car b) temp)        :into real-bindings :and
+                  :collect (car b)                    :into names
+                :do
+                  (cmpck (member (car names) (cdr names) :test #'eq)
+                         "LET: The variable ~s occurs more than once in the LET."
+                         (car names))
+              :finally
                 (return (c1let/let* 'let*
                                     (nconc temp-bindings real-bindings)
                                     `((declare (ignorable ,@temp-names)
index 21b5fa0..9cc8023 100644 (file)
@@ -28,7 +28,7 @@
            ;; If there is only one variable binding, we use LET* instead of LET
            (let-bindings (first args))
            (psetq-p (and psetq-p (rest let-bindings)))
-           (var-form-pairs (parse-let let-bindings ss is ts other-decls))
+           (var-form-pairs (parse-let let-bindings psetq-p ss is ts other-decls))
            (body (create-temps-for-specials var-form-pairs body psetq-p))
            (compiled-pairs (compile-let-forms var-form-pairs psetq-p
                                               ss is ts other-decls))
                                 (c1unbind (nconc specials locals)))
                          )))))
 
-(defun parse-let (var-assignment-pairs ss is ts other-decls)
+(defun parse-let (var-assignment-pairs psetq-p ss is ts other-decls)
   (flet ((in-read-only-decl-p (v other-decls)
            (dolist (i other-decls nil)
              (when (and (eq (car i) :READ-ONLY)
                         (member v (rest i)))
                (return t)))))
-    (loop for x in var-assignment-pairs
-       collect (let (name form)
-                 (cond ((symbolp x)
-                        (setf name x
-                              form nil
-                              x nil))
-                       ((not (and (consp x) (or (endp (cdr x)) (endp (cddr x)))))
-                        (cmperr "Syntax error in LET/LET* variable binding~&~8T~S" x))
-                       (t
-                        (setf name (first x)
-                              form (rest x))))
-                 (let ((v (c1make-var name ss is ts)))
-                   (when (in-read-only-decl-p name other-decls)
-                     (setf (var-read-only-p v) t))
-                   (cons v (if form (first form) (default-init v))))))))
+    (loop
+       :with names = '()
+       :for x :in var-assignment-pairs
+       :collect (let (name form)
+                  (cond ((symbolp x)
+                         (setf name x
+                               form nil
+                               x nil))
+                        ((not (and (consp x) (or (endp (cdr x)) (endp (cddr x)))))
+                         (cmperr "Syntax error in LET/LET* variable binding~&~8T~S" x))
+                        (t
+                         (setf name (first x)
+                               form (rest x))))
+                  (when psetq-p
+                    (push name names)
+                    (cmpck (member (car names) (cdr names) :test #'eq)
+                           "LET: The variable ~s occurs more than once in the LET."
+                           name))
+                  (let ((v (c1make-var name ss is ts)))
+                    (when (in-read-only-decl-p name other-decls)
+                      (setf (var-read-only-p v) t))
+                    (cons v (if form (first form) (default-init v))))))))
 
 (defun create-temps-for-specials (var-form-pairs body psetq-p)
   ;; In a LET form, when special variables are bound they cause a side