-
Notifications
You must be signed in to change notification settings - Fork 313
/
macros.lisp
1963 lines (1802 loc) · 90.5 KB
/
macros.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
;;;; lots of basic macros for the target SBCL
;;;; This software is part of the SBCL system. See the README file for
;;;; more information.
;;;;
;;;; This software is derived from the CMU CL system, which was
;;;; written at Carnegie Mellon University and released into the
;;;; public domain. The software is in the public domain and is
;;;; provided with absolutely no warranty. See the COPYING and CREDITS
;;;; files for more information.
(in-package "SB-IMPL")
;;;; DEFMACRO
;;; Inform the cross-compiler how to expand SB-XC:DEFMACRO (= DEFMACRO)
;;; and supporting macros using the already defined host macros until
;;; this file is itself cross-compiled.
#+sb-xc-host
(flet ((defmacro-using-host-expander (name)
(setf (macro-function name)
(lambda (form env)
(declare (ignore env))
;; Since SB-KERNEL:LEXENV isn't compatible with the host,
;; just pass NIL. The expansion correctly captures a non-null
;; environment, but the expander doesn't need it.
(funcall (cl:macro-function name) form nil)))))
(defmacro-using-host-expander 'sb-xc:defmacro)
(defmacro-using-host-expander 'named-ds-bind)
(defmacro-using-host-expander 'binding*)
(defmacro-using-host-expander 'sb-xc:deftype)
;; FIXME: POLICY doesn't support DEFMACRO, but we need it ASAP.
(defmacro-using-host-expander 'sb-c:policy))
;;;; Destructuring-bind
(sb-xc:defmacro destructuring-bind (lambda-list expression &body body
&environment env)
(declare (ignore env)) ; could be policy-sensitive (but isn't)
"Bind the variables in LAMBDA-LIST to the corresponding values in the
tree structure resulting from the evaluation of EXPRESSION."
`(binding* ,(sb-c::expand-ds-bind lambda-list expression t nil)
,@body))
;;;; DEFUN
;;; Should we save the inline expansion of the function named NAME?
(defun save-inline-expansion-p (name)
(or
;; the normal reason for saving the inline expansion
(let ((inlinep (info :function :inlinep name)))
(member inlinep '(inline maybe-inline)))
;; another reason for saving the inline expansion: If the
;; ANSI-recommended idiom
;; (DECLAIM (INLINE FOO))
;; (DEFUN FOO ..)
;; (DECLAIM (NOTINLINE FOO))
;; has been used, and then we later do another
;; (DEFUN FOO ..)
;; without a preceding
;; (DECLAIM (INLINE FOO))
;; what should we do with the old inline expansion when we see the
;; new DEFUN? Overwriting it with the new definition seems like
;; the only unsurprising choice.
(nth-value 1 (fun-name-inline-expansion name))))
(defun extract-dx-args (lambda-list decl-forms)
(let (dx-decls)
(dolist (form decl-forms)
(dolist (expr (cdr form))
(when (typep expr '(cons (eql dynamic-extent)))
(setf dx-decls (union dx-decls (cdr expr))))))
(unless dx-decls
(return-from extract-dx-args nil))
;; TODO: in addition to ":SILENT T" supressing warnings, PARSE-LAMBDA-LIST
;; needs to allow :CONDITION-CLASS = NIL to ask that no errors be signaled.
;; An indicator can be returned so that at worst the code below does nothing.
(multiple-value-bind (llks required optional rest key aux)
(parse-lambda-list lambda-list :silent t)
(declare (ignore llks rest))
;; We enforce uniqueness of the symbols in the union of REQUIRED,
;; OPTIONAL, REST, KEY (including any supplied-p variables),
;; but there may be an AUX binding shadowing a lambda binding.
;; This affects something like:
;; (LAMBDA (X &AUX (X (MAKE-FOO X))) (DECLARE (DYNAMIC-EXTENT X))
;; in which the decl does not pertain to argument X.
(let ((arg-index 0) caller-dxable)
(labels ((examine (sym dx-note)
(when (and (member sym dx-decls) (not (shadowed-p sym)))
(push dx-note caller-dxable))
(incf arg-index))
(shadowed-p (sym)
(dolist (binding aux)
(when (eq (if (listp binding) (car binding) binding) sym)
(return t)))))
(dolist (spec required)
(examine spec arg-index))
(dolist (spec optional)
(examine (if (listp spec) (car spec) spec) arg-index))
(dolist (spec key)
(multiple-value-bind (keyword var) (parse-key-arg-spec spec)
(examine var keyword))))
(nreverse caller-dxable)))))
(defun block-compilation-non-entry-point (name)
(and (boundp 'sb-c:*compilation*)
(let* ((compilation sb-c:*compilation*)
(entry-points (sb-c::entry-points compilation)))
(and (sb-c::block-compile compilation)
entry-points
(not (member name entry-points :test #'equal))))))
(flet ((defun-expander (env name lambda-list body snippet &optional source-form)
(multiple-value-bind (forms decls doc) (parse-body body t)
;; Maybe kill docstring, but only under the cross-compiler.
#+(and (not sb-doc) sb-xc-host) (setq doc nil)
(let* (;; stuff shared between LAMBDA and INLINE-LAMBDA and NAMED-LAMBDA
(lambda-guts `(,@decls (block ,(fun-name-block-name name) ,@forms)))
(lambda `(lambda ,lambda-list ,@lambda-guts))
(named-lambda `(named-lambda ,name ,lambda-list
,@(when *top-level-form-p* '((declare (sb-c::top-level-form))))
,@(when doc (list doc)) ,@lambda-guts))
;; DXABLE-ARGS and SNIPPET are mutually exclusive, so we can sleazily pass
;; whichever exists (if either does) as one parameter to %DEFUN.
(extra-info (or snippet (extract-dx-args lambda-list decls)))
(inline-thing
(cond ((member snippet '(:predicate :copier :accessor)) nil)
;; If the defstruct snippet is :CONSTRUCTOR, we might have to store
;; a full inline expansion depending on the lexical environment.
((save-inline-expansion-p name)
;; we want to attempt to inline, so complain if we can't
(cond ((sb-c:inline-syntactic-closure-lambda lambda env))
(t
(#+sb-xc-host warn
#-sb-xc-host sb-c:maybe-compiler-notify
"lexical environment too hairy, can't inline DEFUN ~S"
name)
nil))))))
(when (and (eq snippet :constructor)
(not (typep inline-thing '(cons (eql sb-c:lambda-with-lexenv)))))
;; constructor in null lexenv need not save the expansion
(setq inline-thing nil))
(when inline-thing
(setq inline-thing (list 'quote inline-thing)))
(when (and extra-info (not (keywordp extra-info)))
(setq extra-info (list 'quote extra-info)))
(let ((definition
(if (block-compilation-non-entry-point name)
`(progn
(sb-c::%refless-defun ,named-lambda)
',name)
`(%defun ',name ,named-lambda
,@(when (or inline-thing extra-info) `(,inline-thing))
,@(when extra-info `(,extra-info))))))
`(progn
(eval-when (:compile-toplevel)
(sb-c:%compiler-defun ',name t ,inline-thing ,extra-info))
,(if source-form
`(sb-c::with-source-form ,source-form ,definition)
definition)
;; This warning, if produced, comes after the DEFUN happens.
;; When compiling, there's no real difference, but when interpreting,
;; if there is a handler for style-warning that nonlocally exits,
;; it's wrong to have skipped the DEFUN itself, since if there is no
;; function, then the warning ought not to have been issued at all.
,@(when (typep name '(cons (eql setf)))
`((eval-when (:compile-toplevel :execute)
(sb-c::warn-if-setf-macro ',name))
',name))))))))
;;; This is one of the major places where the semantics of block
;;; compilation is handled. Substitution for global names is totally
;;; inhibited if (block-compile *compilation*) is NIL. And if
;;; (block-compile *compilation*) is true and entry points are
;;; specified, then we don't install global definitions for non-entry
;;; functions (effectively turning them into local lexical functions.)
(sb-xc:defmacro defun (&environment env name lambda-list &body body)
"Define a function at top level."
(check-designator name 'defun #'legal-fun-name-p "function name")
#+sb-xc-host
(unless (cl:symbol-package (fun-name-block-name name))
(warn "DEFUN of uninterned function name ~S (tricky for GENESIS)" name))
(defun-expander env name lambda-list body nil))
;; extended defun as used by defstruct
(sb-xc:defmacro sb-c:xdefun (&environment env name snippet source-form lambda-list &body body)
(defun-expander env name lambda-list body snippet source-form)))
;;;; DEFCONSTANT, DEFVAR and DEFPARAMETER
(sb-xc:defmacro defconstant (name value &optional (doc nil docp))
"Define a global constant, saying that the value is constant and may be
compiled into code. If the variable already has a value, and this is not
EQL to the new value, the code is not portable (undefined behavior). The
third argument is an optional documentation string for the variable."
(check-designator name 'defconstant)
`(eval-when (:compile-toplevel :load-toplevel :execute)
(%defconstant ',name ,value (sb-c:source-location)
,@(and docp `(',doc)))))
(declaim (ftype (sfunction (symbol t &optional t t) null)
about-to-modify-symbol-value))
;;; the guts of DEFCONSTANT
(defun %defconstant (name value source-location &optional (doc nil docp))
#+sb-xc-host (declare (ignore doc docp))
(unless (symbolp name)
(error "The constant name is not a symbol: ~S" name))
(with-single-package-locked-error (:symbol name
"defining ~s as a constant")
(when (looks-like-name-of-special-var-p name)
(style-warn 'asterisks-around-constant-variable-name
:format-control "Defining ~S as a constant"
:format-arguments (list name)))
(when source-location
(setf (info :source-location :constant name) source-location))
(let ((kind (info :variable :kind name)))
(case kind
(:constant
;; Note: This behavior (discouraging any non-EQL modification)
;; is unpopular, but it is specified by ANSI (i.e. ANSI says a
;; non-EQL change has undefined consequences). If people really
;; want bindings which are constant in some sense other than
;; EQL, I suggest either just using DEFVAR (which is usually
;; appropriate, despite the un-mnemonic name), or defining
;; something like the DEFCONSTANT-EQX macro used in SBCL (which
;; is occasionally more appropriate). -- WHN 2001-12-21
(if (boundp name)
(if (typep name '(or boolean keyword))
;; Non-continuable error.
(about-to-modify-symbol-value name 'defconstant)
(let ((old (symbol-value name)))
(unless (or (eql value old)
;; SAPs behave like numbers but yet EQL doesn't work on them,
;; special case it.
;; Nobody will notices that the constant
;; is not EQ, since it can be copied at
;; any time anyway.
#-sb-xc-host
(and (system-area-pointer-p old)
(system-area-pointer-p value)
(sap= old value)))
(multiple-value-bind (ignore aborted)
(with-simple-restart (abort "Keep the old value.")
(cerror "Go ahead and change the value."
'defconstant-uneql
:name name
:old-value old
:new-value value))
(declare (ignore ignore))
(when aborted
(return-from %defconstant name))))))
(warn "redefining a MAKUNBOUND constant: ~S" name)))
(:unknown
;; (This is OK -- undefined variables are of this kind. So we
;; don't warn or error or anything, just fall through.)
)
(t (warn "redefining ~(~A~) ~S to be a constant" kind name)))))
(dolist (backpatch (info :variable :forward-references name))
(funcall backpatch value))
(clear-info :variable :forward-references name)
;; We ought to be consistent in treating any change of :VARIABLE :KIND
;; as a continuable error. The above CASE expression pre-dates the
;; existence of symbol-macros (I believe), but at a bare minimum,
;; INFO should return NIL for its second value if requesting the
;; :macro-expansion of something that is getting defined as constant.
(clear-info :variable :macro-expansion name)
(clear-info :source-location :symbol-macro name)
#-sb-xc-host
(progn
(when docp
(setf (documentation name 'variable) doc))
(%set-symbol-value name value))
;; Define the constant in the cross-compilation host, since the
;; value is used when cross-compiling for :COMPILE-TOPLEVEL contexts
;; which reference the constant.
#+sb-xc-host
(eval `(unless (boundp ',name) (defconstant ,name ',value)))
(setf (info :variable :kind name) :constant)
;; Deoptimize after changing it to :CONSTANT, and not before, though tbh
;; if your code cares about the timing of PROGV relative to DEFCONSTANT,
;; well, I can't even.
#-sb-xc-host (sb-c::unset-symbol-progv-optimize name)
name)
(sb-xc:defmacro defvar (var &optional (val nil valp) (doc nil docp))
"Define a special variable at top level. Declare the variable
SPECIAL and, optionally, initialize it. If the variable already has a
value, the old value is not clobbered. The third argument is an optional
documentation string for the variable."
(check-designator var 'defvar)
;; Maybe kill docstring, but only under the cross-compiler.
#+(and (not sb-doc) sb-xc-host) (setq doc nil)
`(progn
(eval-when (:compile-toplevel)
(%compiler-defvar ',var))
(%defvar ',var
(sb-c:source-location)
,@(cond ((not valp)
nil)
((constantp val)
;; No need to avoid evaluation if it's a constant.
`(',(constant-form-value val)))
(val
`((unless (%boundp ',var) ,val))))
,@(and docp
`(',doc)))))
(sb-xc:defmacro defparameter (var val &optional (doc nil docp))
"Define a parameter that is not normally changed by the program,
but that may be changed without causing an error. Declare the
variable special and sets its value to VAL, overwriting any
previous value. The third argument is an optional documentation
string for the parameter."
(check-designator var 'defparameter)
;; Maybe kill docstring, but only under the cross-compiler.
#+(and (not sb-doc) sb-xc-host) (setq doc nil)
`(progn
(eval-when (:compile-toplevel)
(%compiler-defvar ',var))
(%defparameter ',var ,val (sb-c:source-location)
,@(and docp
`(',doc)))))
(defun %compiler-defvar (var)
(proclaim `(special ,var)))
;;;; DEFGLOBAL and DEFINE-LOAD-TIME-GLOBAL
(sb-xc:defmacro defglobal (name value &optional (doc nil docp))
"Defines NAME as a global variable that is always bound. VALUE is evaluated
and assigned to NAME both at compile- and load-time, but only if NAME is not
already bound.
Global variables share their values between all threads, and cannot be
locally bound, declared special, defined as constants, and neither bound
nor defined as symbol macros.
See also the declarations SB-EXT:GLOBAL and SB-EXT:ALWAYS-BOUND."
(check-designator name 'defglobal)
(let ((boundp (make-symbol "BOUNDP")))
`(progn
(eval-when (:compile-toplevel)
(let ((,boundp (boundp ',name)))
(%compiler-defglobal ',name :always-bound
(not ,boundp) (unless ,boundp ,value))))
(%defglobal ',name
(if (%boundp ',name) (make-unbound-marker) ,value)
(sb-c:source-location)
,@(and docp `(',doc))))))
(sb-xc:defmacro define-load-time-global (name value &optional (doc nil docp))
"Defines NAME as a global variable that is always bound. VALUE is evaluated
and assigned to NAME at load-time, but only if NAME is not already bound.
Attempts to read NAME at compile-time will signal an UNBOUND-VARIABLE error
unless it has otherwise been assigned a value.
See also DEFGLOBAL which assigns the VALUE at compile-time too."
(check-designator name 'define-load-time-global)
`(progn
(eval-when (:compile-toplevel)
(%compiler-defglobal ',name :eventually nil nil))
(%defglobal ',name
(if (%boundp ',name) (make-unbound-marker) ,value)
(sb-c:source-location)
,@(and docp `(',doc)))))
(defun %compiler-defglobal (name always-boundp assign-it-p value)
(proclaim `(global ,name))
(when assign-it-p
(set-symbol-global-value name value))
(sb-c::process-variable-declaration
name 'always-bound
;; don't "weaken" the proclamation if it's in fact always bound now
(if (eq (info :variable :always-bound name) :always-bound)
:always-bound
always-boundp)))
;;;; various conditional constructs
(flet ((prognify (forms env)
(cond ((not forms) nil)
((and (singleton-p forms)
(sb-c:policy env (= sb-c:store-coverage-data 0)))
(car forms))
(t `(progn ,@forms)))))
;; COND defined in terms of IF
(sb-xc:defmacro cond (&rest clauses &environment env)
(named-let make-clauses ((clauses clauses))
(if (endp clauses)
nil
(let ((clause (first clauses))
(more (rest clauses)))
(with-current-source-form (clauses)
(if (atom clause)
(error 'simple-type-error
:format-control "COND clause is not a ~S: ~S"
:format-arguments (list 'cons clause)
:expected-type 'cons
:datum clause)
(let ((test (first clause))
(forms (rest clause)))
(if (endp forms)
`(or ,test ,(make-clauses more))
(if (and (eq test t)
(not more))
;; THE to preserve non-toplevelness for FOO in
;; (COND (T (FOO)))
`(the t ,(prognify forms env))
`(if ,test
,(prognify forms env)
,(when more (make-clauses more))))))))))))
(sb-xc:defmacro when (test &body forms &environment env)
"If the first argument is true, the rest of the forms are
evaluated as a PROGN."
`(if ,test ,(prognify forms env)))
(sb-xc:defmacro unless (test &body forms &environment env)
"If the first argument is not true, the rest of the forms are
evaluated as a PROGN."
`(if ,test nil ,(prognify forms env))))
(sb-xc:defmacro return (&optional (value nil))
`(return-from nil ,value))
;;;; various sequencing constructs
(flet ((prog-expansion-from-let (varlist body-decls let)
(multiple-value-bind (body decls) (parse-body body-decls nil)
`(block nil
(,let ,varlist
,@decls
(tagbody ,@body))))))
(sb-xc:defmacro prog (varlist &body body-decls)
(prog-expansion-from-let varlist body-decls 'let))
(sb-xc:defmacro prog* (varlist &body body-decls)
(prog-expansion-from-let varlist body-decls 'let*)))
(sb-xc:defmacro prog1 (result &body body)
(let ((n-result (gensym)))
`(let ((,n-result ,result))
(progn
,@body
,n-result))))
(sb-xc:defmacro prog2 (form1 result &body body)
`(prog1 (progn ,form1 ,result) ,@body))
;; AND and OR are defined in terms of IF.
(sb-xc:defmacro and (&rest forms)
(named-let expand-forms ((nested nil) (forms forms) (ignore-last nil))
(cond ((endp forms) t)
((endp (rest forms))
(let ((car (car forms)))
(cond (nested
car)
(t
;; Preserve non-toplevelness of the form!
`(the t ,car)))))
((and ignore-last
(endp (cddr forms)))
(car forms))
;; Better code that way, since the result will only have two
;; values, NIL or the last form, and the precedeing tests
;; will only be used for jumps
((and (not nested) (cddr forms))
`(if ,(expand-forms t forms t)
,@(last forms)))
(t
`(if ,(first forms)
,(expand-forms t (rest forms) ignore-last))))))
(sb-xc:defmacro or (&rest forms)
(named-let expand-forms ((nested nil) (forms forms))
(cond ((endp forms) nil)
((endp (rest forms))
;; Preserve non-toplevelness of the form!
(let ((car (car forms))) (if nested car `(the t ,car))))
(t
(let ((n-result (gensym)))
`(let ((,n-result ,(first forms)))
(if ,n-result
,n-result
,(expand-forms t (rest forms)))))))))
;;;; Multiple value macros:
;;; All the multiple-value receiving forms are defined in terms of
;;; MULTIPLE-VALUE-CALL.
(flet ((validate-vars (vars)
(with-current-source-form (vars)
(unless (and (listp vars) (every #'symbolp vars))
(error "Vars is not a list of symbols: ~S" vars)))))
(sb-xc:defmacro multiple-value-bind (vars value-form &body body)
(validate-vars vars)
(if (= (length vars) 1)
;; Not only does it look nicer to reduce to LET in this special case,
;; if might produce better code or at least compile quicker.
;; Certainly for the evaluator it's preferable.
`(let ((,(car vars) ,value-form))
,@body)
(flet ((maybe-list (x) (if (member x lambda-list-keywords) (list x) x)))
(let ((ignore '#:ignore))
`(multiple-value-call #'(lambda (&optional ,@(mapcar #'maybe-list vars)
&rest ,ignore)
(declare (ignore ,ignore))
,@body)
,value-form)))))
(sb-xc:defmacro multiple-value-setq (vars value-form)
(validate-vars vars)
;; MULTIPLE-VALUE-SETQ is required to always return just the primary
;; value of the value-from, even if there are no vars. (SETF VALUES)
;; in turn is required to return as many values as there are
;; value-places, hence this:
(if vars
`(values (setf (values ,@vars) ,value-form))
`(values ,value-form))))
(sb-xc:defmacro multiple-value-list (value-form)
`(multiple-value-call #'list ,value-form))
(sb-xc:defmacro nth-value (n form &environment env)
"Evaluate FORM and return the Nth value (zero based)
without consing a temporary list of values."
;; FIXME: The above is true, if slightly misleading. The
;; MULTIPLE-VALUE-BIND idiom [ as opposed to MULTIPLE-VALUE-CALL
;; (LAMBDA (&REST VALUES) (NTH N VALUES)) ] does indeed not cons at
;; runtime. However, for large N (say N = 200), COMPILE on such a
;; form will take longer than can be described as adequate, as the
;; optional dispatch mechanism for the M-V-B gets increasingly
;; hairy.
(let ((val (and (constantp n env) (constant-form-value n env))))
(if (and (integerp val) (<= 0 val (or #+(or x86-64 arm64 riscv) ;; better DEFAULT-UNKNOWN-VALUES
1000
10))) ; Arbitrary limit.
(let ((dummy-list (make-gensym-list val))
(keeper (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.
;; 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))))
;;;; ASSERT and CHECK-TYPE
;;; ASSERT is written this way, to call ASSERT-ERROR, because of how
;;; closures are compiled. RESTART-CASE has forms with closures that
;;; the compiler causes to be generated at the top of any function
;;; using RESTART-CASE, regardless of whether they are needed. Thus if
;;; we just wrapped a RESTART-CASE around the call to ERROR, we'd have
;;; to do a significant amount of work at runtime allocating and
;;; deallocating the closures regardless of whether they were ever
;;; needed.
(sb-xc:defmacro assert (test-form &optional places datum &rest arguments
&environment env)
"Signals an error if the value of TEST-FORM is NIL. Returns NIL.
Optional DATUM and ARGUMENTS can be used to change the signaled
error condition and are interpreted as in (APPLY #'ERROR DATUM
ARGUMENTS).
Continuing from the signaled error using the CONTINUE restart will
allow the user to alter the values of the SETFable locations
specified in PLACES and then start over with TEST-FORM.
If TEST-FORM is of the form
(FUNCTION ARG*)
where FUNCTION is a function (but not a special operator like
CL:OR, CL:AND, etc.) the results of evaluating the ARGs will be
included in the error report if the assertion fails."
(collect ((bindings) (infos))
(let* ((func (if (listp test-form) (car test-form)))
(new-test
(if (and (typep func '(and symbol (not null)))
(not (macro-function func env))
(not (special-operator-p func))
(proper-list-p (cdr test-form)))
;; TEST-FORM is a function call. We do not attempt this
;; if TEST-FORM is a macro invocation or special form.
`(,func ,@(mapcar (lambda (place)
(if (constantp place env)
place
(with-unique-names (temp)
(bindings `(,temp ,place))
(infos `',place)
(infos temp)
temp)))
(rest test-form)))
;; For all other cases, just evaluate TEST-FORM
;; and don't report any details if the assertion fails.
test-form))
(try '#:try)
(done '#:done))
;; If TEST-FORM, potentially using values from BINDINGS, does not
;; hold, enter a loop which reports the assertion error,
;; potentially changes PLACES, and retries TEST-FORM.
`(tagbody
,try
(let ,(bindings)
(when ,new-test
(go ,done))
(assert-error ',test-form
,@(and (infos)
`(,(/ (length (infos)) 2)))
,@(infos)
,@(and (or places datum
arguments)
`(',places))
,@(and (or places datum
arguments)
`(,datum))
,@arguments))
,@(mapcar (lambda (place)
`(setf ,place (assert-prompt ',place ,place)))
places)
(go ,try)
,done))))
(defun assert-prompt (name value)
(cond ((y-or-n-p "The old value of ~S is ~S.~
~%Do you want to supply a new value? "
name value)
(format *query-io* "~&Type a form to be evaluated:~%")
(eval (read *query-io*)))
(t value)))
;;; CHECK-TYPE is written this way, to call CHECK-TYPE-ERROR, because
;;; of how closures are compiled. RESTART-CASE has forms with closures
;;; that the compiler causes to be generated at the top of any
;;; function using RESTART-CASE, regardless of whether they are
;;; needed. Because it would be nice if CHECK-TYPE were cheap to use,
;;; and some things (e.g., READ-CHAR) can't afford this excessive
;;; consing, we bend backwards a little.
(sb-xc:defmacro check-type (place type &optional type-string
&environment env)
"Signal a restartable error of type TYPE-ERROR if the value of PLACE
is not of the specified type. If an error is signalled and the restart
is used to return, this can only return if the STORE-VALUE restart is
invoked. In that case it will store into PLACE and start over."
;; Detect a common user-error.
(when (and (consp type) (eq 'quote (car type)))
(error 'simple-reference-error
:format-control "Quoted type specifier in ~S: ~S"
:format-arguments (list 'check-type type)
:references '((:ansi-cl :macro check-type))))
;; KLUDGE: We use a simpler form of expansion if PLACE is just a
;; variable to work around Python's blind spot in type derivation.
;; For more complex places getting the type derived should not
;; matter so much anyhow.
(let ((expanded (%macroexpand place env))
(type (let ((ctype (sb-c::careful-specifier-type type)))
(if ctype
(type-specifier ctype)
type))))
(if (symbolp expanded)
`(do ()
((typep ,place ',type))
(setf ,place (check-type-error ',place ,place ',type
,@(and type-string
`(,type-string)))))
(let ((value (gensym)))
`(do ((,value ,place ,place))
((typep ,value ',type))
(setf ,place
(check-type-error ',place ,value ',type
,@(and type-string
`(,type-string)))))))))
;;;; DEFINE-SYMBOL-MACRO
(sb-xc:defmacro define-symbol-macro (name expansion)
`(eval-when (:compile-toplevel :load-toplevel :execute)
(sb-c::%define-symbol-macro ',name ',expansion (sb-c:source-location))))
(defun sb-c::%define-symbol-macro (name expansion source-location)
(unless (symbolp name)
(error 'simple-type-error :datum name :expected-type 'symbol
:format-control "Symbol macro name is not a symbol: ~S."
:format-arguments (list name)))
(with-single-package-locked-error
(:symbol name "defining ~A as a symbol-macro"))
(let ((kind (info :variable :kind name)))
(case kind
((:macro :unknown)
(when source-location
(setf (info :source-location :symbol-macro name) source-location))
(setf (info :variable :kind name) :macro)
(setf (info :variable :macro-expansion name) expansion))
(t
(%program-error "Symbol ~S is already defined as ~A."
name (case kind
(:alien "an alien variable")
(:constant "a constant")
(:special "a special variable")
(:global "a global variable")
(t kind))))))
name)
;;;; DEFINE-COMPILER-MACRO
(sb-xc:defmacro define-compiler-macro (name lambda-list &body body)
"Define a compiler-macro for NAME."
(check-designator name 'define-compiler-macro
#'legal-fun-name-p "function name")
(when (and (symbolp name) (special-operator-p name))
(%program-error "cannot define a compiler-macro for a special operator: ~S"
name))
;; DEBUG-NAME is called primarily for its side-effect of asserting
;; that (COMPILER-MACRO-FUNCTION x) is not a legal function name.
(let ((def (make-macro-lambda (sb-c::debug-name 'compiler-macro name)
lambda-list body 'define-compiler-macro name
:accessor 'sb-c::compiler-macro-args)))
;; FIXME: Shouldn't compiler macros also get source locations?
;; Plain DEFMACRO supplies source location information.
`(progn
(eval-when (:compile-toplevel)
(sb-c::%compiler-defmacro :compiler-macro-function ',name))
(eval-when (:compile-toplevel :load-toplevel :execute)
(sb-c::%define-compiler-macro ',name ,def)))))
(eval-when (#-sb-xc :compile-toplevel :load-toplevel :execute)
(defun sb-c::%define-compiler-macro (name definition)
(sb-c::warn-if-compiler-macro-dependency-problem name)
;; FIXME: warn about incompatible lambda list with
;; respect to parent function?
(setf (compiler-macro-function name) definition)
name))
;;;; CASE, TYPECASE, and friends
;;; Make this a full warning during SBCL build.
#+sb-xc ; Don't redefine if recompiling in a warm REPL
(define-condition duplicate-case-key-warning (#-sb-xc-host style-warning #+sb-xc-host warning)
((key :initarg :key
:reader case-warning-key)
(case-kind :initarg :case-kind
:reader case-warning-case-kind)
(occurrences :initarg :occurrences
:type list
:reader duplicate-case-key-warning-occurrences))
(:report
(lambda (condition stream)
(format stream
"Duplicate key ~S in ~S form, ~
occurring in~{~#[~; and~]~{ the ~:R clause:~%~< ~S~:>~}~^,~}."
(case-warning-key condition)
(case-warning-case-kind condition)
(duplicate-case-key-warning-occurrences condition)))))
;;; Return three values:
;;; 1. an array of LAYOUT
;;; 2. an array of (unsigned-byte 16) for the clause index to select
;;; 3. an expression mapping each layout in LAYOUT-LISTS to an integer 0..N-1
(defun build-sealed-struct-typecase-map (layout-lists hashes)
;; The hash-generator emulator wants a cookie identifying the set of objects
;; that were hashed.
(let ((lambda (sb-c:make-perfect-hash-lambda
hashes
#+sb-xc-host
(map 'vector
(lambda (list)
(mapcar (lambda (layout)
(list :type (classoid-name (layout-classoid layout))))
list))
layout-lists))))
(unless lambda
(return-from build-sealed-struct-typecase-map (values nil nil nil)))
(let* ((phashfun (sb-c::compile-perfect-hash lambda hashes))
(n (length hashes))
(domain (make-array n :initial-element nil))
(range (sb-xc:make-array n :element-type '(unsigned-byte 16))))
(loop for clause-index from 1 for list across layout-lists
do (dolist (layout list)
(let* ((hash (ldb (byte 32 0) (layout-clos-hash layout)))
(index (funcall phashfun hash)))
(aver (null (aref domain index)))
(setf (aref domain index) layout
(aref range index) clause-index))))
(values domain range lambda))))
(declaim (ftype function sb-pcl::emit-cache-lookup))
(defun optimize-%typecase-index (layout-lists object sealed)
;; If no new subtypes can be defined, then there is a compiled-time-computable
;; mapping from CLOS-hash to jump table index.
;; Try the hash-based expansion if applicable. It's allowed to fail, as it will
;; when 32-bit hashes are nonunique.
(when sealed
;; TODO: this could eliminate an array lookup when there is only a single layout
;; per clause. So instead of the perfect hash identifying a clause index via lookup,
;; the hash _is_ the clause index; the clauses in the CASE need to be permuted
;; to match the hashes, which doesn't work in the way TYPECASE currently expands.
(let ((seen-layouts)
(expanded-lists (make-array (length layout-lists) :initial-element nil))
(index 0))
(flet ((add-to-clause (layout)
(unless (member layout seen-layouts)
(push layout seen-layouts)
(push layout (aref expanded-lists index)))))
(dovector (layouts layout-lists)
(dolist (layout layouts)
;; unless this layout was in a prior clause
(when (add-to-clause layout)
(sb-kernel::do-subclassoids ((classoid layout) (layout-classoid layout))
(declare (ignore classoid))
(add-to-clause layout))))
(incf index)))
(let ((hashes (map '(array (unsigned-byte 32) (*))
(lambda (x) #+64-bit (ldb (byte 32 0) (layout-clos-hash x))
#-64-bit (layout-clos-hash x))
seen-layouts)))
(multiple-value-bind (layouts indices expr)
(build-sealed-struct-typecase-map expanded-lists hashes)
(when expr
(return-from optimize-%typecase-index
`(truly-the
(integer 0 ,(length layout-lists))
(if (not (%instancep ,object))
0
(let* ((l (%instance-layout ,object)) ; layout
(h (,expr (ldb (byte 32 0) (layout-clos-hash l))))) ; perfect hash
(if (and (< h ,(length layouts)) (eq l (svref ,layouts h)))
(aref ,indices h)
0))))))))))
;; The generated s-expression is too sensitive to the order LOAD-TIME-VALUE fixups are
;; patched in by cold-init. You'll have a bad time if CACHE-CELL is an unbound-marker.
#+sb-xc-host (error "PCL cache won't work for cross-compiled TYPECASE")
;; Use a PCL cache when the sealed logic was inapplicable (or failed due to hash collisions).
;; A cache is usually an improvement over sequential tests but it's impossible to know
;; (the first clause could get taken 99% of the time)
(let ((n (length layout-lists)))
`(truly-the
(integer 0 ,n)
(if (not (%instancep ,object))
0
;; TODO: if we access the cache by layout ID instead of the object,
;; one word can store the layout ID and the clause index
;; (certainly true for 64-bit, maybe not 32).
;; The benefit would be never having to observe a key lacking a value.
;; Also: we don't really need the test for the object layout's hash is 0
;; because it can't be. On the other hand, we might want to utilize
;; this macro on STANDARD-OBJECT.
(prog* ((clause 0)
(cache-cell
(load-time-value
;; Assume the cache will want at least N lines
(cons (sb-pcl::make-cache :key-count 1 :size ,n :value t)
,layout-lists)))
(cache (car (truly-the cons cache-cell)))
(layout (%instance-layout ,object)))
(declare (optimize (safety 0)))
,(sb-pcl::emit-cache-lookup 'cache '(layout) 'miss 'clause)
(return clause)
MISS
(return (sb-pcl::%struct-typecase-miss ,object cache-cell)))))))
;;; Decide whether to bind EXPR to a random gensym or a COPY-SYMBOL, or not at all,
;;; for purposes of CASE/TYPECASE. Lexical vars don't require rebinding because
;;; no SET can occur in dispatching to a clause, and multiple refs are devoid
;;; of side-effects (such as UNBOUND-SYMBOL or undefined-alien trap)
(defun choose-tempvar (bind expr env)
(let ((bind
(cond ((or bind (consp expr)) t)
((not (symbolp expr)) nil)
(t
(let ((found (and (sb-c::lexenv-p env)
(sb-c:lexenv-find expr vars :lexenv env))))
(cond ((or (sb-c::global-var-p found)
(listp found) ; special, macro, or not found
(eq found :bogus)) ; PCL walker shenanigans
t)
((sb-c::lambda-var-specvar found)
(bug "can't happen"))
(t
nil)))))))
(cond ((not bind) expr)
((and (symbolp expr)
;; Some broken 3rd-party code walker is confused by #:_
;; and this hack of forcing a random gensym seems
;; to partially cure whatever the problem is.
(string/= expr "_"))
(copy-symbol expr))
(t
(gensym)))))
;;; Given an arbitrary TYPECASE, see if it is a discriminator over
;;; an assortment of structure-object subtypes. If it is, potentially turn it
;;; into a dispatch based on layout-clos-hash.
;;; The decision to use a hash-based lookup should depend on the number of types
;;; matched, but if there are a lot of types matched all rooted at a common
;;; ancestor, it may not be as beneficial.
;;;
;;; The expansion currently works only with sealed classoids.
;;; Making it work with unsealed classoids isn't too tough.
;;; The dispatch will look something like a PCL cache but simpler.
;;; First of all, there's no reason we can't use stable hashes
;;; for structure layouts, because an incompatibly redefined structure
;;; (which is unportable to begin with), doesn't require a new hash.
;;; In fact as far as I can tell, redefining a standard class doesn't require a new hash
;;; because the obsolete layout always gets clobbered to 0, and cache lookups always check
;;; for a match on both the hash and the layout.
(defun expand-struct-typecase (keyform normal-clauses type-specs default errorp)
(let* ((n (length type-specs))
(n-base-types 0)
(layout-lists (make-array n))
(exhaustive-list) ; of classoids
(temp (choose-tempvar t keyform nil))
(all-sealed t))
(labels
((ok-classoid (classoid)
;; Return T if this is a classoid this expander can work with.
;; Also figure if all classoids accepted by this test were sealed.
(when (or (structure-classoid-p classoid)
(and (sb-kernel::built-in-classoid-p classoid)
(not (memq (classoid-name classoid)
sb-kernel::**non-instance-classoid-types**))))
;; If this classoid is sealed, then its children are sealed too,
;; and we don't need to verify that.
(unless (eq (classoid-state classoid) :sealed)
(setq all-sealed nil))
(sb-kernel::do-subclassoids ((subclassoid layout) classoid)
(declare (ignore layout))
(pushnew subclassoid exhaustive-list))
t))
(get-layouts (ctype)
(let ((list
(cond ((ok-classoid ctype) (list (classoid-layout ctype)))
((and (union-type-p ctype)
(every #'ok-classoid (union-type-types ctype)))
(mapcar 'classoid-layout (union-type-types ctype))))))
(incf n-base-types (length list))
list)))
;; For each clause, if it effectively an OR over acceptable instance types,
;; collect the layouts of those types.
(loop for i from 0 for spec in type-specs
do (let ((parse (specifier-type spec)))
(setf (aref layout-lists i) (or (get-layouts parse)
(return-from expand-struct-typecase nil)))))
;; The number of base types is an upper bound on the number of different TYPEP
;; executions that could occur.
;; Let's say 1 to 4 TYPEP tests isn't to bad. Just do them sequentially.
;; But given something like:
;; (typecase x
;; ((or parent1 parent2 parent3) ...)
;; ((or parent4 parent5 parent6) ...)
;; where eaach parent has dozens of children (directly or indirectly),
;; it may be worse to use a hash-based lookup.
(when (or (< n-base-types 5) ; too few cases
(> (length exhaustive-list) (* 3 n-base-types))) ; too much "bloat"
(return-from expand-struct-typecase))
;; I don't know if these criteria are sane: Use hashing only if either all sealed,
;; or very large? Why is this an additional restriction beyond the above heuristics?
(when (or all-sealed (>= n-base-types 8))
`(let ((,temp ,keyform))
(case (sb-kernel::%typecase-index ,layout-lists ,temp ,all-sealed)
,@(loop for i from 1 for clause in normal-clauses
collect `(,i
;; CLAUSE is ((TYPEP #:G 'a-type) . forms)
(sb-c::%type-constraint ,temp ,(third (car clause)))
,@(cdr clause)))
(0 ,@(if errorp
`((etypecase-failure ,temp ',type-specs))
(cdr default)))))))))
(defun should-attempt-hash-based-case-dispatch (keys)
;; Guess a good minimum table size, with a slight bias against using the xperfecthash files.
;; If there are a mixture of key types, penalize slightly be requiring a larger minimum
;; number of keys. If we don't do that, then the expression can have a ridiculous amount
;; of math in it that would surely outweigh any savings over an IF/ELSE chain.
;; Technically I should generate the perfect hash function and then decide how costly it is
;; using PHASH-CONVERT-TO-2-OPERAND-CODE as the cost model (number of instructions).
(let ((minimum #+sb-xc-host 5 #-sb-xc-host 4))
(when (and (some #'symbolp keys) (or (some #'integerp keys) (some #'characterp keys)))
(incf minimum 2))
(>= (length keys) minimum)))
(defun wrap-if (condition with form)
(if condition
(append with (list form))
form))
;;; CASE-BODY returns code for all the standard "case" macros. NAME is
;;; the macro name, and KEYFORM is the thing to case on.