master: Restore non-simple strings of length 1 as character designators
crhodes via Sbcl-commits <[email protected]> Sat, 02 May 2026 22:07:57 +0000
| Newsgroups | gmane.lisp.steel-bank.cvs |
|---|---|
| Message-ID | <[email protected]> |
The branch "master" has been updated in SBCL:
via 39a97c73287f1f55dae37dc2e351badb5da2536f (commit)
from 072061b567e9bd3d6c2d4b42972d5baeba070172 (commit)
- Log -----------------------------------------------------------------
commit 39a97c73287f1f55dae37dc2e351badb5da2536f
Author: Christophe Rhodes <[email protected]>
Date: Sat May 2 22:45:58 2026 +0100
Restore non-simple strings of length 1 as character designators
The 1 in the (STRING 1) type refers to the size of the underlying
array, not the length of the sequence.
---
NEWS | 4 ++++
src/code/target-char.lisp | 10 ++++++++--
src/compiler/fndb.lisp | 2 +-
src/compiler/typetran.lisp | 2 +-
tests/character.pure.lisp | 22 +++++++++++++++++++++-
5 files changed, 35 insertions(+), 5 deletions(-)
diff --git a/NEWS b/NEWS
index 01a71ff57..343133cdf 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,9 @@
;;;; -*- coding: utf-8; fill-column: 78 -*-
+changes relative to sbcl-2.6.4:
+ * bug fix: strings of arbitrary size with fill-pointer set to 1 are
+ character designators. (reported by _death)
+
changes in sbcl-2.6.4 relative to sbcl-2.6.3:
* minor incompatible change: when DEFSETF is called on a name that was
previously used as a (presumed) call to a function, it issues a single
diff --git a/src/code/target-char.lisp b/src/code/target-char.lisp
index d3ae2c87c..a6d2ce141 100644
--- a/src/code/target-char.lisp
+++ b/src/code/target-char.lisp
@@ -249,14 +249,20 @@ there are no character bits or fonts.)"
"Return the character with the code CODE."
(code-char code))
+(defun length-1-string-p (x)
+ (and (typep x 'string) (= (length x) 1)))
+(defun symbol-with-length-1-name-p (x)
+ (and (typep x 'symbol) (length-1-string-p (symbol-name x))))
+(deftype character-designator ()
+ '(or character (satisfies length-1-string-p) (satisfies symbol-with-length-1-name-p)))
+
(defun character (object)
"Coerce OBJECT into a CHARACTER if possible. Legal inputs are characters,
strings and symbols of length 1."
(flet ((do-error (control args)
(error 'simple-type-error
:datum object
- ;;?? how to express "symbol with name of length 1"?
- :expected-type '(or character (string 1))
+ :expected-type 'character-designator
:format-control control
:format-arguments args)))
(typecase object
diff --git a/src/compiler/fndb.lisp b/src/compiler/fndb.lisp
index 4bee1843f..6ed77011c 100644
--- a/src/compiler/fndb.lisp
+++ b/src/compiler/fndb.lisp
@@ -591,7 +591,7 @@
(character character) boolean
(movable foldable flushable no-verify-arg-count))
-(defknown character ((or character (string 1) symbol))
+(defknown character ((or character (simple-string 1) (and string (not simple-string)) symbol))
character
(movable foldable unsafely-flushable))
(defknown char-code (character) %char-code (movable foldable flushable))
diff --git a/src/compiler/typetran.lisp b/src/compiler/typetran.lisp
index e6adc10fc..b198c1f35 100644
--- a/src/compiler/typetran.lisp
+++ b/src/compiler/typetran.lisp
@@ -1846,7 +1846,7 @@
(fail)))
((eql type-specifier 'character)
(unless (types-equal-or-intersect value-type
- (specifier-type '(or symbol (string 1))))
+ (specifier-type '(or symbol (simple-string 1) (and string (not simple-string)))))
(fail)))
((csubtypep to-type (specifier-type 'complex))
(unless (types-equal-or-intersect value-type
diff --git a/tests/character.pure.lisp b/tests/character.pure.lisp
index cf203c052..722c37e82 100644
--- a/tests/character.pure.lisp
+++ b/tests/character.pure.lisp
@@ -238,4 +238,24 @@
(lambda (x)
(character x)
x)
- (or (string 1) character symbol)))
+ (or (simple-string 1) (and string (not simple-string)) character symbol)))
+
+(with-test (:name (character :input :fill-pointer))
+ (checked-compile-and-assert
+ ()
+ `(lambda (x) (character x))
+ (((make-array 1 :element-type 'character :fill-pointer 0 :initial-element #\x)) (condition 'type-error))
+ (((make-array 1 :element-type 'character :fill-pointer 1 :initial-element #\x)) #\x)
+ (((make-array 2 :element-type 'character :fill-pointer 0 :initial-element #\x)) (condition 'type-error))
+ (((make-array 2 :element-type 'character :fill-pointer 1 :initial-element #\x)) #\x)
+ (((make-array 2 :element-type 'character :fill-pointer 2 :initial-element #\x)) (condition 'type-error))))
+
+(with-test (:name (coerce character :input :fill-pointer))
+ (checked-compile-and-assert
+ ()
+ `(lambda (x y) (coerce x y))
+ (((make-array 1 :element-type 'character :fill-pointer 0 :initial-element #\x) 'character) (condition 'type-error))
+ (((make-array 1 :element-type 'character :fill-pointer 1 :initial-element #\x) 'character) #\x)
+ (((make-array 2 :element-type 'character :fill-pointer 0 :initial-element #\x) 'character) (condition 'type-error))
+ (((make-array 2 :element-type 'character :fill-pointer 1 :initial-element #\x) 'character) #\x)
+ (((make-array 2 :element-type 'character :fill-pointer 2 :initial-element #\x) 'character) (condition 'type-error))))
-----------------------------------------------------------------------
hooks/post-receive
--
SBCL