List lengths are always fixnums now

Without this patch, it was theoretically possible for a list
length to be a bignum, which means that safe-length could
signal an error (due to generating a too-large bignum) contrary
to its documentation.  Fix things to remove the theoretical
possibility, so that list lengths are always fixnums (and so
that list lenghts are always ptrdiff_t values too, since that
is assumed internally anyway).
* src/alloc.c (Fcons): Do not allocate so many conses that
a list length won’t fit into ptrdiff_t or into fixnum.
This matters only on weird platforms; on typical platforms,
list lengths always fit anyway.
* src/fns.c (list_length, Fsafe_length, proper-list-p):
Remove integer overflow checks that are no longer needed.
This commit is contained in:
Paul Eggert 2019-01-10 21:35:31 -08:00
parent 9609db9d98
commit 470082de55
2 changed files with 16 additions and 8 deletions

View file

@ -2774,6 +2774,19 @@ DEFUN ("cons", Fcons, Scons, 2, 2, 0,
{
if (cons_block_index == CONS_BLOCK_SIZE)
{
/* Maximum number of conses that should be active at any
given time, so that list lengths fit into a ptrdiff_t and
into a fixnum. */
ptrdiff_t max_conses = min (PTRDIFF_MAX, MOST_POSITIVE_FIXNUM);
/* This check is typically optimized away, as a runtime
check is needed only on weird platforms where a count of
distinct conses might not fit. */
if (max_conses < INTPTR_MAX / sizeof (struct Lisp_Cons)
&& (max_conses - CONS_BLOCK_SIZE
< total_free_conses + total_conses))
memory_full (sizeof (struct cons_block));
struct cons_block *new
= lisp_align_malloc (sizeof *new, MEM_TYPE_CONS);
memset (new->gcmarkbits, 0, sizeof new->gcmarkbits);

View file

@ -101,9 +101,7 @@ list_length (Lisp_Object list)
FOR_EACH_TAIL (list)
i++;
CHECK_LIST_END (list, list);
if (i <= min (PTRDIFF_MAX, MOST_POSITIVE_FIXNUM))
return i;
overflow_error ();
return i;
}
@ -141,14 +139,13 @@ DEFUN ("safe-length", Fsafe_length, Ssafe_length, 1, 1, 0,
doc: /* Return the length of a list, but avoid error or infinite loop.
This function never gets an error. If LIST is not really a list,
it returns 0. If LIST is circular, it returns an integer that is at
least the number of distinct elements.
Value is a fixnum, if it's small enough, otherwise a bignum. */)
least the number of distinct elements. */)
(Lisp_Object list)
{
intptr_t len = 0;
FOR_EACH_TAIL_SAFE (list)
len++;
return INT_TO_INTEGER (len);
return make_fixnum (len);
}
DEFUN ("proper-list-p", Fproper_list_p, Sproper_list_p, 1, 1, 0,
@ -168,8 +165,6 @@ A proper list is neither circular nor dotted (i.e., its last cdr is nil). */
}
if (!NILP (last_tail))
return Qnil;
if (MOST_POSITIVE_FIXNUM < len)
overflow_error ();
return make_fixnum (len);
}