1 ;;;; This file contains load-time support for declaration processing.
2 ;;;; In CMU CL it was split off from the compiler so that the compiler
3 ;;;; doesn't have to be in the cold load, but in SBCL the compiler is
4 ;;;; in the cold load again, so this might not be valuable.
6 ;;;; This software is part of the SBCL system. See the README file for
9 ;;;; This software is derived from the CMU CL system, which was
10 ;;;; written at Carnegie Mellon University and released into the
11 ;;;; public domain. The software is in the public domain and is
12 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
13 ;;;; files for more information.
17 ;;; A list of UNDEFINED-WARNING structures representing references to unknown
18 ;;; stuff which came up in a compilation unit.
19 (defvar *undefined-warnings
*)
20 (defvar *argument-mismatch-warnings
*)
21 (declaim (list *undefined-warnings
* *argument-mismatch-warnings
*))
23 ;;; Delete any undefined warnings for NAME and KIND. This is for the
24 ;;; benefit of the compiler, but it's sometimes called from stuff like
25 ;;; type-defining code which isn't logically part of the compiler.
26 (declaim (ftype (function ((or symbol cons
) keyword
) (values))
28 (defun note-name-defined (name kind
)
29 #-sb-xc-host
(atomic-incf sb-kernel
::*type-cache-nonce
*)
30 ;; We do this BOUNDP check because this function can be called when
31 ;; not in a compilation unit (as when loading top level forms).
32 (when (boundp '*undefined-warnings
*)
33 (let ((name (uncross name
)))
34 (setq *undefined-warnings
*
35 (delete-if (lambda (x)
36 (and (equal (undefined-warning-name x
) name
)
37 (eq (undefined-warning-kind x
) kind
)))
38 *undefined-warnings
*))))
41 (defun check-variable-name (name &key
42 (context "local variable")
43 (signal-via #'compiler-error
))
44 (unless (legal-variable-name-p name
)
45 (funcall signal-via
"~@<~S~[~; is a keyword and~; is not a symbol and~
46 ~] cannot be used as a ~A.~@:>"
55 ;;; Check that NAME is a valid function name, returning the name if
56 ;;; OK, and signalling an error if not. In addition to checking for
57 ;;; basic well-formedness, we also check that symbol names are not NIL
58 ;;; or the name of a special form.
59 (defun check-fun-name (name)
62 (unless (legal-fun-name-p name
)
63 (compiler-error "~@<Illegal function name: ~S.~@:>" name
)))
65 (when (eq (info :function
:kind name
) :special-form
)
66 (compiler-error "~@<Special form is an illegal function name: ~S.~@:>"
69 (compiler-error "~@<Illegal function name: ~S.~@:>" name
)))
72 ;;; Check that NAME is a valid class name, returning the name if OK,
73 ;;; and signalling an error if not.
74 (declaim (inline sb-pcl
::check-class-name
))
75 (defun sb-pcl::check-class-name
(name &optional
(allow-nil t
))
76 ;; Apparently, FIND-CLASS and (SETF FIND-CLASS) accept any symbol,
77 ;; but DEFCLASS only accepts non-NIL symbols.
78 (if (or (not (legal-class-name-p name
))
79 (and (null name
) (not allow-nil
)))
80 (error 'sb-kernel
::illegal-class-name-error
:name name
)
83 ;;; Check that NAME is a valid designator for the defining macro
84 ;;; MACRO. This is used mostly to give a consistent message for all
85 ;;; defining forms, except for DEFCLASS, which uses CHECK-CLASS-NAME.
86 (defun check-designator (name macro
&optional
(predicate #'symbolp
)
88 (arg-reference "NAME"))
89 ;; If we decide that the correct behavior is to actually macroexpand
90 ;; and then fail later, well, I suppose we could express all macros
91 ;; such that they perform their LEGAL-FUN-NAME-P/SYMBOLP check as
92 ;; part of the ordinary code, as in: (DEFPARAMETER "foo" 3) ->
93 ;; (%defparameter (the symbol '"foo") ...) which seems at least
94 ;; slightly preferable to failing in the internal function that
95 ;; would store the globaldb info.
96 (unless (funcall predicate name
)
97 (error (format nil
"The ~A argument to ~A, ~~S, is not a ~A."
98 arg-reference macro what
)
101 ;;; This is called to do something about SETF functions that overlap
102 ;;; with SETF macros. Perhaps we should interact with the user to see
103 ;;; whether the macro should be blown away, but for now just give a
104 ;;; warning. Due to the weak semantics of the (SETF FUNCTION) name, we
105 ;;; can't assume that they aren't just naming a function (SETF FOO)
106 ;;; for the heck of it. NAME is already known to be well-formed.
107 (defun warn-if-setf-macro (name)
108 ;; Never warn about this situation when running the cross-compiler.
109 ;; SBCL provides expanders/inverses *and* functions for most SETFable things
110 ;; even when CLHS does not specifically state that #'(SETF x) exists.
111 #+sb-xc-host
(declare (ignore name
))
113 (let ((stem (second name
)))
114 (when (info :setf
:expander stem
)
116 "defining function ~S when ~S already has a SETF macro"
120 ;;; Record a new function definition, and check its legality.
121 (defun proclaim-as-fun-name (name)
124 (check-fun-name name
)
126 ;; KLUDGE: This can happen when eg. compiling a NAMED-LAMBDA, and isn't
127 ;; guarded against elsewhere -- so we want to assert package locks here. The
128 ;; reason we do it only when stomping on existing stuff is because we want
130 ;; (WITHOUT-PACKAGE-LOCKS (DEFUN LOCKED:FOO ...))
131 ;; viable, which requires no compile-time violations in the harmless cases.
132 (with-single-package-locked-error ()
134 (assert-symbol-home-package-unlocked name
"proclaiming ~S as a function")))
136 (let ((kind (info :function
:kind name
)))
137 ;; scrubbing old data: possible collision with a macro
138 ;; There's a silly little problem with fun names that are not ANSI-legal names,
139 ;; e.g. (CAS mumble). We can't ask the host whether that is FBOUNDP,
140 ;; because it would rightly complain. So, just assume that it is not FBOUNDP.
141 (when (and #+sb-xc-host
(symbolp name
)
145 (compiler-style-warn "~S was previously defined as a macro." name
)
146 (setf (info :function
:where-from name
) :assumed
)
147 (clear-info :function
:macro-function name
))
149 (unless (eq :function kind
)
151 ;; There's no reason to store (:FUNCTION :KIND) for names which
152 ;; could only be of kind :FUNCTION if anything.
153 (unless (pcl-methodfn-name-p name
)
154 (setf (info :function
:kind name
) :function
))))))
158 ;;; Make NAME no longer be a function name: clear everything back to
160 (defun undefine-fun-name (name)
162 (macrolet ((frob (&rest types
)
164 name
',(mapcar (lambda (x)
165 (meta-info-number (meta-info :function x
)))
167 ;; Note that this does not clear the :DEFINITION.
168 ;; That's correct, because if we lose the association between a
169 ;; symbol and its #<fdefn> object, it could lead to creation of
170 ;; a non-unique #<fdefn> for a name.
172 :type
; Hmm. What if it was proclaimed- shouldn't it stay?
182 ;;; part of what happens with DEFUN, also with some PCL stuff: Make
183 ;;; NAME known to be a function definition.
184 (defun become-defined-fun-name (name)
185 (proclaim-as-fun-name name
)
186 (when (eq (info :function
:where-from name
) :assumed
)
187 (setf (info :function
:where-from name
) :defined
)
188 (if (info :function
:assumed-type name
)
189 (clear-info :function
:assumed-type name
))))
191 ;;; to be called when a variable is lexically bound
192 (declaim (ftype (function (symbol) (values)) note-lexical-binding
))
193 (defun note-lexical-binding (symbol)
194 ;; This check is intended to protect us from getting silently
195 ;; burned when we define
198 ;; (DEFUN FOO (X) (+ X *FOO*))
203 ;; and then we happen to compile bar.lisp before foo.lisp.
204 (when (looks-like-name-of-special-var-p symbol
)
205 ;; FIXME: should be COMPILER-STYLE-WARNING?
206 (style-warn 'asterisks-around-lexical-variable-name
208 "using the lexical binding of the symbol ~
209 ~/sb-ext:print-symbol-with-prefix/, not the~@
211 :format-arguments
(list symbol
)))
214 ;;; In the target compiler, a lexenv can hold an alist of condition
215 ;;; types (CTYPE . ACTION) such that when signaling condition CTYPE,
216 ;;; we perform ACTION which is usually MUFFLE-CONDITION.
217 ;;; Each CTYPE is a (parsed) CONDITION subtype, which is slightly
218 ;;; more efficient than holding the mapping keys as s-expressions
219 ;;; (type specifier). However, the parsed representation is worse
220 ;;; for in the cross-compiler, actually downright disastrous. Why?
221 ;;; Because to process an entry in the list, we invert the parsed type
222 ;;; back to a sexpr, and then inquire of the host via its CL:TYPEP
223 ;;; whether a condition instance is of that type. (We use host
224 ;;; condition objects). So why parse and unparse? Not only is that
225 ;;; dumb, it's broken. For example, to invert #<classoid CODE-DELETION-NOTE>,
226 ;;; you must already have seen a target definition of that type.
227 ;;; But you haven't necessarily! If you haven't, then there is no
228 ;;; CONDITION-CLASSOID for that, there is only an UNKNOWN-TYPE.
229 ;;; And then you have to signal a PARSE-UNKNOWN-TYPE, and then you must
230 ;;; ask how to handle _that_ condition (the PARSE-UNKNOWN-TYPE)
231 ;;; signaled while trying to signal some other condition. What a mess.
232 (declaim (ftype (function (list list
) list
)
233 process-handle-conditions-decl
))
234 (defun process-handle-conditions-decl (spec list
)
235 (let ((new (copy-alist list
)))
236 (dolist (clause (cdr spec
) new
)
237 (destructuring-bind (typespec restart-name
) clause
238 (let ((ospec (rassoc restart-name new
:test
#'eq
)))
241 (setf (car ospec
) `(or ,typespec
,(car ospec
)))
242 (push (cons typespec restart-name
) new
))
244 (let ((type (compiler-specifier-type typespec
)))
246 ((contains-unknown-type-p type
))
248 (setf (car ospec
) (type-union (car ospec
) type
)))
250 (push (cons type restart-name
) new
)))))))))
252 (declaim (ftype (function (list list
) list
)
253 process-muffle-conditions-decl
))
254 (defun process-muffle-conditions-decl (expr list
)
255 (let ((spec (cond ((not expr
) nil
)
256 ((singleton-p (cdr expr
)) (cadr expr
))
257 (t `(or ,@(cdr expr
))))))
258 (process-handle-conditions-decl `(handle-conditions (,spec muffle-warning
))
261 (declaim (ftype (function (list list
) list
)
262 process-unhandle-conditions-decl
))
263 (defun process-unhandle-conditions-decl (spec list
)
264 (let ((new (copy-alist list
)))
265 (dolist (clause (cdr spec
) new
)
267 (destructuring-bind (typespec restart-name
) clause
268 (let ((ospec (rassoc restart-name new
:test
#'eq
)))
270 (let ((type (type-intersection
272 (or (compiler-specifier-type `(not ,typespec
))
274 (if (type= type
*empty-type
*)
275 (setq new
(delete restart-name new
:test
#'eq
:key
#'cdr
))
276 (setf (car ospec
) type
))))))))))
278 (declaim (ftype (function (list list
) list
)
279 process-unmuffle-conditions-decl
))
280 (defun process-unmuffle-conditions-decl (spec list
)
281 (process-unhandle-conditions-decl
282 `(unhandle-conditions ((or ,@(cdr spec
)) muffle-warning
))
285 (declaim (ftype (function (list list
) list
)
286 process-package-lock-decl
))
287 (defun process-package-lock-decl (spec old
)
288 (destructuring-bind (decl &rest names
) spec
290 (disable-package-locks
291 ;; Why are we using EQUAL here if the only way to disable the
292 ;; lock on (SETF CAR) is to list the name CAR and not (SETF CAR)?
293 (union old names
:test
#'equal
))
294 (enable-package-locks
295 (set-difference old names
:test
#'equal
)))))
297 (defvar *queued-proclaims
* nil
)
299 (defun process-variable-declaration (name kind info-value
)
300 (unless (symbolp name
)
301 (error "Cannot proclaim a non-symbol as ~A: ~S" kind name
))
303 (when (and (eq kind
'always-bound
) (eq info-value
:always-bound
)
305 ;; Allow it to be unbound at compile-time.
306 (not *compile-time-eval
*))
307 (error "Cannot proclaim an unbound symbol as ~A: ~S" kind name
))
309 (multiple-value-bind (allowed test
)
312 (values '(:special
:unknown
) #'eq
))
314 (values '(:global
:unknown
) #'eq
))
315 (always-bound (values '(:constant
) #'neq
)))
316 (let ((old (info :variable
:kind name
)))
317 (unless (member old allowed
:test test
)
318 (error "Cannot proclaim a ~A variable ~A: ~S" old kind name
))))
320 (with-single-package-locked-error
321 (:symbol name
"globally declaring ~A ~A" kind
)
322 (if (eq kind
'always-bound
)
323 (setf (info :variable
:always-bound name
) info-value
)
324 (setf (info :variable
:kind name
) info-value
)))
325 #-sb-xc-host
(sb-impl::unset-symbol-progv-optimize name
))
327 (defun proclaim-type (name type type-specifier where-from
)
328 (unless (symbolp name
)
329 (error "Cannot proclaim TYPE of a non-symbol: ~S" name
))
331 (with-single-package-locked-error
332 (:symbol name
"globally declaring the TYPE of ~A")
334 (when (eq (info :variable
:where-from name
) :declared
)
335 (let ((old-type (info :variable
:type name
)))
336 (when (type/= type old-type
)
338 (warn 'type-proclamation-mismatch-warning
340 :old
(type-specifier old-type
)
341 :new type-specifier
))))
342 (when (and (not warned
)
345 (let ((value (symbol-value name
)))
346 (when (multiple-value-bind (p really
) (ctypep value type
)
349 (warn 'type-proclamation-mismatch-warning
353 :new type-specifier
)))))
354 (setf (info :variable
:type name
) type
355 (info :variable
:where-from name
) where-from
)))
357 (defun proclaim-ftype (name type-oid type-specifier where-from
)
358 (declare (type (or ctype defstruct-description
) type-oid
))
359 (unless (legal-fun-name-p name
)
360 (error "Cannot declare FTYPE of illegal function name ~S" name
))
361 (when (and (ctype-p type-oid
)
362 (not (csubtypep type-oid
(specifier-type 'function
))))
363 (error "Not a function type: ~/sb-impl:print-type/" type-oid
))
364 (with-single-package-locked-error
365 (:symbol name
"globally declaring the FTYPE of ~A")
366 (let ((from (info :function
:where-from name
)))
369 (let ((old-type (global-ftype name
))
370 (type (if (ctype-p type-oid
)
372 (specifier-type type-specifier
))))
374 ((not (type/= type old-type
))) ; not changed
375 ((not (info :function
:info name
)) ; not a known function
376 (warn 'ftype-proclamation-mismatch-warning
378 :old
(type-specifier old-type
)
379 :new type-specifier
))
380 ((csubtypep type old-type
)) ; tighten known function type
383 'ftype-proclamation-mismatch-error
385 :old
(type-specifier old-type
)
386 :new type-specifier
)))))
388 (when (and #+sb-xc-host
(not (sb-cold::make-host-2-parallelism
)))
389 (let* ((old-type (global-ftype name
))
390 (type (if (ctype-p type-oid
)
392 (specifier-type type-specifier
)))
393 (old-return-type (if (fun-type-p old-type
)
394 (fun-type-returns old-type
)
396 (return-type (if (fun-type-p type
)
397 (fun-type-returns type
)
400 ((values-subtypep old-return-type return-type
))
402 (style-warn 'sb-kernel
::ftype-proclamation-derived-mismatch-warning
403 :name name
:old
(type-specifier old-type
)
404 :new type-specifier
))))))))
405 ;; Now references to this function shouldn't be warned about as
406 ;; undefined, since even if we haven't seen a definition yet, we
407 ;; know one is planned.
409 ;; Other consequences of we-know-you're-a-function-now are
410 ;; appropriate too, e.g. any MACRO-FUNCTION goes away.
411 (proclaim-as-fun-name name
)
412 (note-name-defined name
:function
)
414 ;; The actual type declaration.
415 (setf (info :function
:type name
) type-oid
416 (info :function
:where-from name
) where-from
)))
418 (defun seal-class (classoid)
419 (declare (type classoid classoid
))
420 (setf (classoid-state classoid
) :sealed
)
421 (sb-kernel::do-subclassoids
((subclassoid layout
) classoid
)
422 (declare (ignore layout
))
423 (setf (classoid-state subclassoid
) :sealed
)))
425 (defun process-freeze-type-declaration (type-specifier)
426 (let ((class (specifier-type type-specifier
)))
427 (when (typep class
'classoid
)
428 (seal-class class
))))
430 (defun check-deprecation-declaration (state since form
)
431 (unless (typep state
'deprecation-state
)
432 (error 'simple-type-error
434 :expected-type
'deprecation-state
435 :format-control
"~@<In declaration ~S, ~S state is not a ~
436 valid deprecation state. Expected one ~
438 :format-arguments
(list form state
439 (rest (typexpand 'deprecation-state
)))))
440 (multiple-value-call #'values
441 state
(sb-impl::normalize-deprecation-since since
)))
443 (defun process-deprecation-declaration (thing state software version
)
444 (destructuring-bind (namespace name
&key replacement
) thing
445 (let ((info (make-deprecation-info state software version replacement
)))
448 (when (eq state
:final
)
449 (sb-impl::setup-function-in-final-deprecation
450 software version name replacement
))
451 (setf (info :function
:deprecated name
) info
))
454 name
:context
"deprecated variable declaration" :signal-via
#'error
)
455 (when (eq state
:final
)
456 (sb-impl::setup-variable-in-final-deprecation
457 software version name replacement
))
458 (setf (info :variable
:deprecated name
) info
))
460 (when (eq state
:final
)
461 (sb-impl::setup-type-in-final-deprecation
462 software version name replacement
))
463 (setf (info :type
:deprecated name
) info
))))))
465 (defun process-declaration-declaration (name form
)
466 (unless (symbolp name
)
467 (error "In~% ~S~%the declaration to be recognized is not a ~
470 (with-single-package-locked-error
471 (:symbol name
"globally declaring ~A as a declaration proclamation"))
472 (setf (info :declaration
:known name
) t
))
474 ;;; ANSI defines the declaration (FOO X Y) to be equivalent to
475 ;;; (TYPE FOO X Y) when FOO is a type specifier. This function
476 ;;; implements that by converting (FOO X Y) to (TYPE FOO X Y).
477 (defun canonized-decl-spec (decl-spec)
478 (let ((id (first decl-spec
)))
479 (if (cond ((symbolp id
) (info :type
:kind id
))
483 (or (info :type
:expander id
)
484 (info :type
:kind id
)))))
486 ;; FIXME: should be (TYPEP id '(OR CLASS CLASSOID))
487 ;; but that references CLASS too soon.
488 ;; See related hack in DEF!TYPE TYPE-SPECIFIER.
489 (typep id
'instance
)))
490 (cons 'type decl-spec
)
493 ;; These return values are intended for EQ-comparison in
494 ;; STORE-LOCATION in %PROCLAIM.
495 (defun deprecation-location-key (namespace)
497 (function '(deprecated function
))
498 (variable '(deprecated variable
))
499 (type '(deprecated type
))))
501 (defun %proclaim
(raw-form location
)
502 (destructuring-bind (&whole form
&optional kind
&rest args
)
503 (canonized-decl-spec raw-form
)
504 ;; It seems strange to test whether we are currently in
505 ;; compile-time mode this way, but the reason we don't just call
506 ;; %COMPILER-PROCLAIM in a :COMPILE-TOPLEVEL-only situation in the
507 ;; macro-expansion of DECLAIM is that unlike the DEFmumble macros,
508 ;; DECLAIM and PROCLAIM both exist, and it is unclear whether the
509 ;; intent of the ANSI specification is that
510 ;; (EVAL-WHEN (:COMPILE-TOPLEVEL ...)
513 ;; should have the exact same compile time effects as (DECLAIM ...).
514 ;; We make the assumption that yes, they should have the same semantics.
515 (when (boundp '*compilation
*)
516 (%compiler-proclaim kind args
))
517 (labels ((store-location (name &key
(key kind
))
519 (setf (getf (info :source-location
:declaration name
) key
)
521 ;; Without this WHEN, globaldb would accumulate
522 ;; a bunch of explicitly stored empty lists because
523 ;; it does not know that there's no need to store NIL.
524 (when (info :source-location
:declaration name
)
525 (remf (info :source-location
:declaration name
) key
))))
526 (map-names (names function
&rest extra-args
)
528 (store-location name
)
529 (apply function name extra-args
))
531 (map-args (function &rest extra-args
)
532 (apply #'map-names args function extra-args
)))
534 ((special global always-bound
)
535 (map-args #'process-variable-declaration kind
539 (always-bound :always-bound
))))
541 (if *type-system-initialized
*
542 (destructuring-bind (type &rest names
) args
543 (check-deprecated-type type
)
544 (let ((ctype (specifier-type type
)))
545 (map-names names
(ecase kind
546 (type #'proclaim-type
)
547 (ftype #'proclaim-ftype
))
548 ctype type
:declared
)))
550 (push raw-form
*queued-proclaims
*)
552 (error "Type system not yet initialized.")))
554 (map-args #'process-freeze-type-declaration
))
555 ;; This only has compile-time effects.
556 ((start-block end-block
))
558 (multiple-value-bind (new-policy specified-qualities
)
559 (process-optimize-decl form
*policy
*)
560 (when (and (boundp '*compilation
*)
561 ;; Should I also examine *COMPILE-TIME-EVAL* here? I don't think so.
562 (listp (saved-optimize-decls *compilation
*)))
563 (push form
(saved-optimize-decls *compilation
*)))
564 (setq *policy
* new-policy
)
565 (warn-repeated-optimize-qualities new-policy specified-qualities
)))
567 (setq *handled-conditions
*
568 (process-muffle-conditions-decl form
*handled-conditions
*)))
570 ;; When cross-compiling, we're won't perform type algebra on the sexpr
571 ;; representation. There is no need for this kind of ridiculous spec:
572 ;; (and (or this that) (not that)).
573 #+sb-xc-host
(bug "UNMUFFLE: not implemented")
575 (setq *handled-conditions
*
576 (process-unmuffle-conditions-decl form
*handled-conditions
*)))
577 ((disable-package-locks enable-package-locks
)
578 (setq *disabled-package-locks
*
579 (process-package-lock-decl form
*disabled-package-locks
*)))
580 ((inline notinline maybe-inline
)
582 (warn-if-inline-failed/proclaim name kind
)
583 (setf (info :function
:inlinep name
)
584 (the (and inlinep
(not null
)) kind
))))
586 (destructuring-bind (state since
&rest things
) args
587 (multiple-value-bind (state software version
)
588 (check-deprecation-declaration state since form
)
589 (mapc (lambda (thing)
590 (process-deprecation-declaration thing state software version
)
591 (destructuring-bind (namespace name
&rest rest
) thing
592 (declare (ignore rest
))
594 name
:key
(deprecation-location-key namespace
))))
597 (map-args #'process-declaration-declaration form
))
599 (unless (info :declaration
:known kind
)
600 (compiler-warn "unrecognized declaration ~S" raw-form
)))))))
602 (defun proclaim (raw-form)
603 (/noshow
"PROCLAIM" raw-form
)
604 (%proclaim raw-form nil
)
607 ;;; Note that the type NAME has been (re)defined, updating the
608 ;;; undefined warnings and VALUES-SPECIFIER-TYPE cache.
609 (defun %note-type-defined
(name)
610 (declare (symbol name
))
611 (note-name-defined name
:type
)
612 (values-specifier-type-cache-clear)
615 ;; Issue a style warning if there are any repeated OPTIMIZE declarations
616 ;; given the SPECIFIED-QUALITIES, unless there is no ambiguity.
617 (defun warn-repeated-optimize-qualities (new-policy specified-qualities
)
619 (dolist (quality-and-value specified-qualities
)
620 (let* ((quality (car quality-and-value
))
621 (current ; Read the raw quality value, not the adjusted value.
622 (%%policy-quality new-policy
(policy-quality-name-p quality
))))
623 (when (and (not (eql (cdr quality-and-value
) current
))
624 (not (assq quality dups
)))
625 (push `(,quality
,current
) dups
))))
627 ;; If a restriction is in force, this message can be misleading,
628 ;; as the "effective" value isn't always what the message claims.
629 (compiler-style-warn "Repeated OPTIMIZE qualit~@P. Using ~{~S~^ and ~}"
630 (length dups
) dups
))))