From 9c2fc61fe8c2208bb46118df074358e8d9bc7f2a Mon Sep 17 00:00:00 2001 From: Douglas Katzman Date: Sat, 31 Oct 2015 19:25:54 -0400 Subject: [PATCH] Fix NTH-VALUE for huge constant N. --- NEWS | 3 +++ src/code/macros.lisp | 22 ++++++++++++---------- tests/compiler-2.pure.lisp | 6 ++++++ 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/NEWS b/NEWS index 9d2480431..abae6ce3f 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,7 @@ ;;;; -*- coding: utf-8; fill-column: 78 -*- +changes relative to sbcl-1.3.0: + * bug fix: NTH-VALUE does not cause stack overflow. (lp#1511419) + changes in sbcl-1.3.0 relative to sbcl-1.2.16: * minor incompatible change: the environment passed to a macro/setf/deftype expander is not always an object of type SB-KERNEL:LEXENV. diff --git a/src/code/macros.lisp b/src/code/macros.lisp index a62fa3fab..5f9cda3af 100644 --- a/src/code/macros.lisp +++ b/src/code/macros.lisp @@ -480,7 +480,7 @@ invoked. In that case it will store into PLACE and start over." ;;;; miscellaneous macros -(defmacro-mundanely nth-value (n form) +(defmacro-mundanely nth-value (n form &environment env) #!+sb-doc "Evaluate FORM and return the Nth value (zero based) without consing a temporary list of values." @@ -491,18 +491,20 @@ invoked. In that case it will store into PLACE and start over." ;; form will take longer than can be described as adequate, as the ;; optional dispatch mechanism for the M-V-B gets increasingly ;; hairy. - (if (integerp n) - (let ((dummy-list (make-gensym-list n)) - (keeper (sb!xc:gensym "KEEPER"))) - `(multiple-value-bind (,@dummy-list ,keeper) ,form - (declare (ignore ,@dummy-list)) - ,keeper)) + (let ((val (and (sb!xc:constantp n env) (constant-form-value n env)))) + (if (and (integerp val) (<= 0 val 10)) ; Arbitrary limit. + (let ((dummy-list (make-gensym-list val)) + (keeper (sb!xc:gensym "KEEPER"))) + `(multiple-value-bind (,@dummy-list ,keeper) ,form + (declare (ignore ,@dummy-list)) + ,keeper)) ;; &MORE conversion handily deals with non-constant N, ;; avoiding the unstylish practice of inserting FORM into the ;; expansion more than once to pick off a few small values. - `(multiple-value-call - (lambda (n &rest list) (nth (truly-the index n) list)) - (the index ,n) ,form))) + ;; This is not as good as above, because it uses TAIL-CALL-VARIABLE. + `(multiple-value-call + (lambda (n &rest list) (nth (truly-the index n) list)) + (the index ,n) ,form)))) (defmacro-mundanely declaim (&rest specs) #!+sb-doc diff --git a/tests/compiler-2.pure.lisp b/tests/compiler-2.pure.lisp index 9b0d38975..79d5d87ff 100644 --- a/tests/compiler-2.pure.lisp +++ b/tests/compiler-2.pure.lisp @@ -86,3 +86,9 @@ (with-test (:name :make-array-nil-no-warning) (assert-no-signal (compile nil '(lambda () (make-array '(2 2) :element-type nil))))) + +(with-test (:name :nth-value-huge-n-works) + (flet ((return-a-ton-of-values () + (values-list (loop for i below 5000 collect i)))) + (assert (= (nth-value 1 (return-a-ton-of-values)) 1)) + (assert (= (nth-value 4000 (return-a-ton-of-values)) 4000)))) -- 2.11.4.GIT