1 ;;;; implementation of CONSTANTP, needs both INFO and IR1-ATTRIBUTES
3 ;;;; This software is part of the SBCL system. See the README file for
6 ;;;; This software is derived from the CMU CL system, which was
7 ;;;; written at Carnegie Mellon University and released into the
8 ;;;; public domain. The software is in the public domain and is
9 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
10 ;;;; files for more information.
14 (!defvar
*special-constant-variables
* nil
)
16 (defun %constantp
(form environment envp
)
18 (%macroexpand form environment
)
21 ;; This INFO test catches KEYWORDs as well as explicitly
22 ;; DEFCONSTANT symbols.
24 (or (eq (info :variable
:kind form
) :constant
)
25 (constant-special-variable-p form
)))
27 (let ((answer (constant-special-form-p form environment envp
)))
28 (if (eq answer
:maybe
)
29 (values (constant-function-call-p form environment envp
))
33 (defun %constant-form-value
(form environment envp
)
35 (%macroexpand form environment
)
39 ;; KLUDGE: superficially, this might look good enough: we grab
40 ;; the value from FORM's property list, and if it isn't there (or
41 ;; is NIL, but hey) we use the host's value. This works for
42 ;; MOST-POSITIVE-FIXNUM and friends, but still fails for
43 ;; float-related constants, where there is in fact no guarantee
44 ;; that we can represent our target value at all in the host,
45 ;; so we don't try. We should rework all uses of floating
46 ;; point so that we never try to use a host's value, and then
47 ;; make some kind of assertion that we never attempt to take
48 ;; a host value of a constant in the CL package.
49 (or #+sb-xc-host
(xc-constant-value form
) (symbol-value form
)))
51 (multiple-value-bind (specialp value
)
52 (constant-special-form-value form environment envp
)
53 (if specialp value
(constant-function-call-value
54 form environment envp
))))
58 (defun constant-special-variable-p (name)
59 (and (member name
*special-constant-variables
*) t
))
61 ;;; FIXME: It would be nice to deal with inline functions
63 (defun constant-function-call-p (form environment envp
)
64 (let ((name (car form
)))
65 (if (and (legal-fun-name-p name
)
66 (eq :function
(info :function
:kind name
))
67 (let ((info (info :function
:info name
)))
68 (and info
(ir1-attributep (fun-info-attributes info
)
70 (and (every (lambda (arg)
71 (%constantp arg environment envp
))
73 ;; Even though the function may be marked as foldable
74 ;; the call may still signal an error -- eg: (CAR 1).
76 (values t
(constant-function-call-value form environment envp
))
81 (defun constant-function-call-value (form environment envp
)
82 (apply (fdefinition (car form
))
84 (%constant-form-value arg environment envp
))
89 ;;;; If you add new special forms, check that they do not
90 ;;;; alter the logic of existing ones: eg, currently
91 ;;;; CONSTANT-FORM-VALUE directly evaluates the last expression
92 ;;;; of a PROGN, as no assignment is allowed. If you extend
93 ;;;; analysis to assignments then other forms must take this
96 (eval-when (:compile-toplevel
:execute
)
97 (defparameter *special-form-constantp-defs
* (make-array 20 :fill-pointer
0)))
99 (defmacro !defconstantp
(operator lambda-list
&key test eval
)
100 (let ((args (make-symbol "ARGS")))
102 ;; FIXME: DESTRUCTURING-BIND should have the option to expand this way.
103 ;; It would be useful for DEFINE-SOURCE-TRANSFORM as well.
104 ((binding-maker (input on-error
)
105 (multiple-value-bind (llks req opt rest key aux env whole
)
108 :accept
(lambda-list-keyword-mask '(&whole
&optional
&rest
&body
)))
109 (declare (ignore llks key aux env
))
110 (aver (every (lambda (x) (and (symbolp x
) x
)) (append req opt rest
)))
111 (flet ((bind (var pred enforce-end
)
114 `(if (and (,pred
,args
) (not (cdr ,args
)))
117 `(if (,pred
,args
) (pop ,args
) ,on-error
)))))
120 ;; If both &WHOLE and &REST are present, the &WHOLE var
121 ;; must be a list, although we don't know that just yet.
122 ;; It will be verified when the &REST arg is bound.
123 `((,(car whole
) ,(if rest
`(truly-the list
,args
) args
))))
124 ,@(maplist (lambda (x)
125 (bind x
(if (cdr x
) 'listp
'consp
)
126 (and (not (cdr x
)) (not opt
) (not rest
))))
128 ,@(maplist (lambda (x) (bind x
'listp
(and (not (cdr x
)) (not rest
))))
132 (if (proper-list-p ,args
)
133 (truly-the list
,args
) ; to open-code EVERY #'P on &REST arg
135 `(eval-when (:compile-toplevel
:execute
)
136 (vector-push-extend ',(list* operator test eval
137 (binding-maker 'args
'(go fail
)))
138 *special-form-constantp-defs
*)))))
140 ;;; NOTE: special forms are tested in the order as written,
141 ;;; so there is some benefit to listing important ones earliest.
143 (!defconstantp quote
(value)
147 (!defconstantp if
(test then
&optional else
)
149 (and (constantp* test
)
150 (constantp* (if (constant-form-value* test
)
153 :eval
(if (constant-form-value* test
)
154 (constant-form-value* then
)
155 (constant-form-value* else
)))
157 ;; FIXME: isn't it sufficient for non-final forms to be flushable and/or
158 ;; maybe satisfy some other conditions? e.g. (PROGN (LIST 1) 'FOO) is constant.
159 (!defconstantp progn
(&body forms
)
160 :test
(every #'constantp
* forms
)
161 :eval
(constant-form-value* (car (last forms
))))
163 (!defconstantp the
(type form
)
164 ;; We can't call TYPEP because the form might be (THE (FUNCTION (t) t) #<fn>)
165 ;; which is valid for declaration but not for discrimination.
166 ;; Instead use %%TYPEP in non-strict mode. FIXME:
167 ;; (1) CAREFUL-SPECIFIER-TYPE should never fail. See lp#1395910.
168 ;; (2) CONTAINS-UNKNOWN-TYPE-P should grovel into ARRAY-TYPE-ELEMENT-TYPE
169 ;; so that (C-U-T-P (SPECIFIER-TYPE '(OR (VECTOR BAD) FUNCTION))) => T
170 ;; and then we can parse, check for unknowns, and get rid of HANDLER-CASE.
171 :test
(and (constantp* form
)
173 ;; in case the type-spec is malformed!
174 (let ((parsed (careful-specifier-type type
)))
175 ;; xc can't rely on a "non-strict" mode of TYPEP.
178 (typep (constant-form-value* form
)
179 (let ((*unparse-fun-type-simplify
* t
))
180 (declare (special *unparse-fun-type-simplify
*))
181 (type-specifier parsed
)))
183 (%%typep
(constant-form-value* form
) parsed nil
)))
185 :eval
(constant-form-value* form
))
187 (!defconstantp unwind-protect
(&whole subforms protected-form
&body cleanup-forms
)
188 :test
(every #'constantp
* subforms
)
189 :eval
(constant-form-value* protected-form
))
191 (!defconstantp block
(name &body forms
)
192 ;; We currently fail to detect cases like
195 ;; ...CONSTANT-FORMS...
196 ;; (RETURN-FROM FOO CONSTANT-VALUE)
199 ;; Right now RETURN-FROM kills the constantness unequivocally.
200 :test
(every #'constantp
* forms
)
201 :eval
(constant-form-value* (car (last forms
))))
203 (!defconstantp multiple-value-prog1
(&whole subforms first-form
&body forms
)
204 :test
(every #'constantp
* subforms
)
205 :eval
(constant-form-value* first-form
))
207 (!defconstantp progv
(symbols values
&body forms
)
208 :test
(and (constantp* symbols
)
210 (let* ((symbol-values (constant-form-value* symbols
))
211 (*special-constant-variables
*
212 (append symbol-values
*special-constant-variables
*)))
215 (constant-form-value* values
)
216 (every #'constantp
* forms
))))
218 (constant-form-value* symbols
)
219 (constant-form-value* values
)
220 (constant-form-value* (car (last forms
)))))
225 ((expand-cases (expr-selector default-clause
)
226 `(flet ((constantp* (x) (%constantp x environment envp
))
227 (constant-form-value* (x) (%constant-form-value x environment envp
)))
228 (declare (optimize speed
) (ignorable #'constantp
*)
229 (ftype (function (t) (values t
&optional
)) ; avoid "unknown values"
230 constantp
* constant-form-value
*))
231 (let ((args (cdr (truly-the list form
))))
234 (lambda (spec &aux
(bindings (cdddr spec
)))
237 (declare (ignorable ,@(mapcar #'car bindings
)))
238 ,(nth expr-selector spec
))))
239 *special-form-constantp-defs
*)
241 ,default-clause
))))))
243 (defun constant-special-form-p (form environment envp
)
245 (tagbody (setq result
(expand-cases 1 :maybe
)) fail
)
248 (defun constant-special-form-value (form environment envp
)
251 (setq result
(expand-cases 2 (return-from constant-special-form-value
253 (return-from constant-special-form-value
(values t result
))
255 ;; Mutatation of FORM could cause failure. It's user error, not a bug.
256 (error "CONSTANT-FORM-VALUE called with invalid expression ~S" form
)))