-
Notifications
You must be signed in to change notification settings - Fork 0
/
apply.lisp
2790 lines (2388 loc) · 117 KB
/
apply.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
; ACL2 Version 8.3 -- A Computational Logic for Applicative Common Lisp
; Copyright (C) 2020, Regents of the University of Texas
; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright
; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0.
; This program is free software; you can redistribute it and/or modify
; it under the terms of the LICENSE file distributed with ACL2.
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; LICENSE for more details.
; Written by: Matt Kaufmann and J Strother Moore
; email: [email protected] and [email protected]
; Department of Computer Science
; University of Texas at Austin
; Austin, TX 78712 U.S.A.
; Many thanks to ForrestHunt, Inc. for supporting the preponderance of this
; work, and for permission to include it here.
(in-package "ACL2")
; See the Essay on the APPLY$ Integration in apply-prim.lisp for an overview.
; Outline
; 1. Badges
; Partially define BADGE, which returns the badge of a symbol. By
; ``partially define'' we mean ``define in terms of a constrained
; function.''
; 2. Tameness
; Partially define tameness: tame lambdas, tame expressions, tame functions,
; lists of tame things, in terms of BADGE.
; 3. Definition of APPLY$ and EV$
; Partially define APPLY$ and EV$.
; 4. Executable Versions of BADGE and TAMEP
; Define :PROGRAM mode functions to recover the badge of a previously
; warranted function and to determine whether expressions, etc., are tame.
; These functions will be used by defwarrant to infer badges which
; maintains a data structure containing previously inferred badges.
; 5. BADGER and the Badge-Table
; Define the :PROGRAM mode function BADGER, which infers the badge (if any)
; of a defined function. The key routine in BADGER is a :PROGRAM mode
; interative inference mechanism called GUESS-ILKS-ALIST.
; 6. Essay on CHECK-ILKS
; For purposes of reassurance only, we locally convert GUESS-ILKS-ALIST to a
; :LOGIC mode function and prove that when it returns non-erroneously the
; badge has certain key properties, (b)-(e), which are enumerated when we
; discuss BADGER. These properties are not immediately obvious from the
; defun of GUESS-ILKS-ALIST because it is iterative and is full of error
; messages. To state this theorem we will define the :LOGIC mode function
; CHECK-ILKS. But the sources and the model differ in when they they do
; this. In the sources, we just leave the essay to remind us that
; guess-ilks-alist is known to imply properties (b)-(e) and we don't
; actually define CHECK-ILKS. That is done during :acl2-devel, during
; certification of books/system/apply. In the model, we define CHECK-ILKS
; and do the proof, but only locally.
; 7. Functional Equivalence
; Define the functional equivalence relation and prove that it is a
; congruence relation for APPLY$. Defwarrant will prove the appropriate
; congruence rules for each :FN formal of newly badged functions.
; 8. DEFWARRANT
; Define DEFWARRANT.
; 9. DEFUN$
; Define DEFUN$.
; Historical Note: Once upon a time we had
; 10. The LAMB Hack
; Define (LAMB vars body) to be `(LAMBDA ,vars ,body) to provide a
; rewrite target for functional-equivalence lemmas. Since ACL2 doesn't
; rewrite constants, we won't even try to simplify a lambda object. We
; are not satisfied with the treatment of functional equivalence yet and
; LAMB is sort of a reminder and placeholder for future work.
; However, we have removed that from the sources because we are not yet
; convinced it is a good way to address the problem of rewriting equivalent
; lambda objects. We plan to experiment with solutions in the user-maintained
; books. As of Version_8.0, our best shot is in books/projects/apply/base.lisp
; (formerly apply-lemmas.lisp), but this may change.
; The next two items are not reflected in the model except as noted below.
; 11. The Defattach
; We attach ``magic'' functions to badge-userfn and apply$-userfn to
; support top-level evaluation of ground apply$ expressions. These magic
; functions are defined in the source file apply-raw.lisp. This is done in
; the ACL2 sources but not in the model of apply$. (However, other files
; in the model work demonstrate the attachments and carry out the requisite
; proofs.)
; 12. Loop$ Scions
; Define the loop$ scions. See the Essay on LOOP$ in translate.lisp.
; (This is not done in the model, just the sources.)
; Note: With the exception of the events immediately below (which are needed by
; the raw Lisp definitions of the *1* function for apply$-lambda), this entire
; file is processed only in pass 2, fundamentally because apply$-primp and
; apply$-prim are only defined in pass 2.
; The definitions of apply$-lambda-guard and apply$-guard were here at one
; time, but have been moved so that they precede the definition of
; ev-fncall-rec-logical.
(when-pass-2
; -----------------------------------------------------------------
; 1. Badges
(defun badge (fn)
(declare (xargs :guard t :mode :logic))
(cond
((apply$-primp fn) (badge-prim fn))
((eq fn 'BADGE) *generic-tame-badge-1*)
((eq fn 'TAMEP) *generic-tame-badge-1*)
((eq fn 'TAMEP-FUNCTIONP) *generic-tame-badge-1*)
((eq fn 'SUITABLY-TAMEP-LISTP) *generic-tame-badge-3*)
((eq fn 'APPLY$) *apply$-badge*)
((eq fn 'EV$) *ev$-badge*)
; Otherwise, badge is undefined unless a warrant tells us what it is.
(t (badge-userfn fn))))
#-acl2-devel
(in-theory (disable apply$-primp badge-prim))
#-acl2-devel
(in-theory (disable badge))
; -----------------------------------------------------------------
; 2. Tameness
; These functions are defined for speed, not clarity. Aside from the obvious
; logical requirements of tameness -- roughly speaking, every function is
; either tame or is tame when supplied with quoted tame functions in the right
; argument positions. We want (tamep x) to imply that x is either a symbol or
; a true-listp and to imply that every function call in x is supplied with the
; right number of arguments (at least with respect to the arities reported by
; badge), and we want tamep guard verified with a guard of t.
(defabbrev tamep-lambdap (fn)
; Fn is allegedly a lambda object. We know it's a consp! We check that it has
; just enough structure to allow guard checking of the tamep clique. This does
; not actually assure us that the lambda object is well formed. We don't
; check, for example, that the lambda formals are distinct or that the
; lambda-body is a termp with no other free vars. The weakness of this
; definition just means that apply$ and ev$ assign meaning to some lambda
; object applications that ACL2 would reject. That's ok as long as we don't
; try to evaluate those applications directly, e.g., by compiling them.
; Finally, we define this as an abbreviation because we use it several times in
; the tamep clique and don't want to introduce another function into the mutual
; recursion.
; See executable-tamep-lambdap for a discussion of an executable version of
; this ``function,'' including an equivalent alternative definition using
; case-match that is perhaps more perspicuous.
; This function is one of the ways of recognizing a lambda object. See the end
; of the Essay on Lambda Objects and Lambda$ for a discussion of the various
; recognizers and their purposes.
(and (lambda-object-shapep fn)
(symbol-listp (lambda-object-formals fn))
(tamep (lambda-object-body fn))))
(mutual-recursion
(defun tamep (x)
(declare (xargs :measure (acl2-count x)
:guard t
:mode :program))
(cond ((atom x) (symbolp x))
((eq (car x) 'quote)
(and (consp (cdr x))
(null (cddr x))))
((symbolp (car x))
(let ((bdg (badge (car x))))
(cond
((null bdg) nil)
((eq (access apply$-badge bdg :ilks) t)
(suitably-tamep-listp (access apply$-badge bdg :arity)
nil
(cdr x)))
(t (suitably-tamep-listp (access apply$-badge bdg :arity)
(access apply$-badge bdg :ilks)
(cdr x))))))
((consp (car x))
(let ((fn (car x)))
(and (tamep-lambdap fn)
(suitably-tamep-listp (length (cadr fn))
; Given (tamep-lambdap fn), (cadr fn) = (lambda-object-formals fn).
nil
(cdr x)))))
(t nil)))
(defun tamep-functionp (fn)
(declare (xargs :measure (acl2-count fn)
:guard t))
(if (symbolp fn)
(let ((bdg (badge fn)))
(and bdg (eq (access apply$-badge bdg :ilks) t)))
(and (consp fn)
(tamep-lambdap fn))))
(defun suitably-tamep-listp (n flags args)
; We take advantage of the fact that (car nil) = (cdr nil) = nil.
(declare (xargs :measure (acl2-count args)
:guard (and (natp n)
(true-listp flags))))
(cond
((zp n) (null args))
((atom args) nil)
(t (and
(let ((arg (car args)))
(case (car flags)
(:FN
(and (consp arg)
(eq (car arg) 'QUOTE)
(consp (cdr arg))
(null (cddr arg))
(tamep-functionp (cadr arg))))
(:EXPR
(and (consp arg)
(eq (car arg) 'QUOTE)
(consp (cdr arg))
(null (cddr arg))
(tamep (cadr arg))))
(otherwise
(tamep arg))))
(suitably-tamep-listp (- n 1) (cdr flags) (cdr args))))))
)
; -----------------------------------------------------------------
; 3. Definition of APPLY$ and EV$
(mutual-recursion
(defun apply$ (fn args)
(declare (xargs :guard (apply$-guard fn args)
:guard-hints (("Goal" :do-not-induct t))
:mode :program))
(cond
((consp fn)
(apply$-lambda fn args))
((apply$-primp fn)
(apply$-prim fn args))
((eq fn 'BADGE)
(badge (car args)))
((eq fn 'TAMEP)
(tamep (car args)))
((eq fn 'TAMEP-FUNCTIONP)
(tamep-functionp (car args)))
((eq fn 'SUITABLY-TAMEP-LISTP)
(ec-call (suitably-tamep-listp (car args) (cadr args) (caddr args))))
((eq fn 'APPLY$)
; The tamep-functionp test below prevents us from APPLY$ing 'APPLY$ except to
; tame functions. In particular, you can't apply$ 'apply$ to 'apply$. We
; discuss some ramifications of this in the Essay on Applying APPLY$ below. A
; cheaper version of this test that works (in the sense that allows both the
; termination and guard proofs) would be (if (symbolp (car args)) (not
; (member-eq (car args) '(apply$ ev$))) (consp (car args))) though that is less
; succinct and might actually ruin the doppelganger construction in the model
; (we haven't tried) because in that construction there are other symbols
; besides APPLY$ and EV$ you can't apply. But the reason we keep the full
; blown tamep-functionp test is more aesthetic: it makes the tameness
; conditions in the ``warrant for apply$'' (which doesn't actually exist but
; which is embodied in the lemma apply$-APPLY$ proved in
; books/projects/apply/base.lisp) exactly analogous to the tameness conditions
; for user-defined mapping functions like COLLECT. There is a similar
; ``warrant for ev$'' embodied in apply$-EV$.
(if (tamep-functionp (car args))
(ec-call (APPLY$ (car args) (cadr args)))
(untame-apply$ fn args)))
((eq fn 'EV$)
(if (tamep (car args))
(EV$ (car args) (cadr args))
(untame-apply$ fn args)))
(t (apply$-userfn fn args))))
(defun apply$-lambda (fn args)
; This is the logical definition of apply$-lambda, which is evaluated under the
; superior call of when-pass-2. Keep this in sync with the raw Lisp
; definition, which is in apply-raw.lisp.
(declare (xargs :guard (apply$-lambda-guard fn args)
:guard-hints (("Goal" :do-not-induct t))))
(apply$-lambda-logical fn args))
(defun ev$ (x a)
(declare (xargs :guard t))
(cond
((not (tamep x))
(untame-ev$ x a))
((variablep x)
(ec-call (cdr (ec-call (assoc-equal x a)))))
((fquotep x)
(cadr x))
((eq (car x) 'if)
(if (ev$ (cadr x) a)
(ev$ (caddr x) a)
(ev$ (cadddr x) a)))
((eq (car x) 'APPLY$)
; Note: the (not (tamep x)) test at the top of this cond is critical to the
; measure of (cadr (cadr x)) being smaller than that of x: we need to know that
; (cdr x) is a consp and it is if x is tamep and starts with 'apply$!.
(apply$ 'APPLY$
(list (cadr (cadr x)) (EV$ (caddr x) a))))
((eq (car x) 'EV$)
(apply$ 'EV$ (list (cadr (cadr x)) (EV$ (caddr x) a))))
(t
(APPLY$ (car x)
(EV$-LIST (cdr x) a)))))
(defun ev$-list (x a)
(declare (xargs :guard t))
(cond
((atom x) nil)
(t (cons (EV$ (car x) a)
(EV$-LIST (cdr x) a)))))
)
; Historical Note:
; We tried to put ``reasonable'' guards on the apply$ clique and failed. For
; example, the reasonable guard on (ev$ x a) is that x is a pseudo-termp and a
; is a symbol-alistp. But consider the recursive call (ev$ (car args) (cadr
; args)) in apply$. The governing test (tamep (car args)) might give us the
; former, but there's nothing that can give us the second because, when ev$
; calls itself as it interprets an 'EV$ call, the second actual is the result
; of a recursive evaluation. So that not only makes the guard proof reflexive
; but puts non-syntactic requirements on the args.
; So we have decided to go with :guard t, except for apply$ where we insist
; (true-listp args) and apply$-lambda where we additionally know that fn and
; args satisfy the pretty weak (apply$-guard fn args).
; Essay on Applying APPLY$
; Suppose collect and collect* are defined as
; (defun$ collect (lst fn)
; (cond ((endp lst) nil)
; (t (cons (apply$ fn (list (car lst)))
; (collect (cdr lst) fn)))))
; (defun$ collect* (lst fn)
; (if (endp lst)
; nil
; (cons (apply$ fn (car lst))
; (collect* (cdr lst) fn))))
; Warning: Don't confuse these symbols with the loop$ scions collect$ and
; collect$+ which take the fn argument first. We define these symbols this way
; here merely for historical reasons: they're defined that way in the original
; apply$ paper.
; (thm ; [1]
; (implies (apply$-warrant-collect)
; (equal (apply$ 'collect '((1 2 3) (lambda (x) (binary-+ '3 x))))
; '(4 5 6))))
; BTW: The warrant below is required because otherwise we don't know
; COLLECT is NOT tame (it is ``almost tame'').
; (thm ; [2]
; (implies (apply$-warrant-collect)
; (equal (apply$ 'apply$
; '(collect ((1 2 3) (lambda (x) (binary-+ '3 x)))))
; (untame-apply$
; 'apply$
; '(collect ((1 2 3) (lambda (x) (binary-+ '3 x)))))))
; :hints
; (("Goal"
; :expand
; ((apply$ 'apply$
; '(collect ((1 2 3) (lambda (x) (binary-+ '3 x)))))))))
; Note that the left-hand sides of the conclusions of [1] and [2] are sort of
; similar but [1] is more direct than [2]. One might wish that if we can
; reduce (apply$ 'collect ...) to a constant we could reduce (apply$ 'apply$
; '(collect ...)) to the same constant but that is not true. In fact [2] tells
; us that (apply$ 'apply$ '(collect ...)) is in some sense undefined since it
; is proved equal to an undefined expression.
; For what it is worth, we can do this:
; (thm ; [3]
; (implies (apply$-warrant-collect)
; (equal (apply$ 'apply$
; '((lambda (lst)
; (collect lst '(lambda (x) (binary-+ '3 x))))
; ((1 2 3))))
; '(4 5 6)))
; :hints
; (("Goal" :expand ((apply$ 'apply$
; '((lambda (lst)
; (collect lst '(lambda (x) (binary-+ '3 x))))
; ((1 2 3))))))))
; One's reaction to [3] could be similar to the scene in "Six Days and Seven
; Nights"... the plane has crashed on the beach of a deserted island...
; Robin: Whoa. What happened?
; Quinn: It crumpled the landing gear when we hit.
; Robin: Well, aren't you gonna fix it? I mean can't we, can't we reattach
; it somehow?
; Quinn: Sure, we'll, like, glue it back on.
; Robin: Aren't you one of those guys?
; Quinn: What guys?
; Robin: Those guy guys, you know, those guys with skills.
; Quinn: Skills?
; Robin: Yeah. You send them into the wilderness with a pocket knife and a
; Q-tip and they build you a shopping mall. You can't do that?
; Quinn: No, I can't do that, but I can do this:
; [Pops finger out of the side of his mouth]
; The reason [3] is relevant is that it's really like [2] except we package the
; collect and the tame lambda object into a tame lambda object and apply it
; successfully. Not exactly a shopping mall, but maybe a convenience store...
; and certainly better than a popping noise!
; Perhaps more interestingly, we can do such things as
; (thm ; [4]
; (implies (apply$-warrant-collect)
; (equal (collect* '(((1 2 3) (lambda (x) (binary-+ '3 x)))
; ((10 20 30) (lambda (x) (binary-+ '3 x))))
; 'collect)
; '((4 5 6) (13 23 33))))
; :hints (("Goal" :in-theory (disable (collect*)))))
; [4] is interesting because we are mapping with a mapping function. One might
; think that since we can't apply$ a mapping function this wouldn't work. But
; it's more subtle. The defun of collect* expands to introduce
; (apply$ 'collect '((1 2 3) (lambda (x) (binary-+ '3 x)))).
; Then the warrant for collect checks that its functional arg is tame,
; so that expands to (collect '(1 2 3) '(lambda (x) (binary-+ '3 x))).
; Now you might think, ``But why can't we force the expansion of the apply$ on
; the untame collect to get an untame-apply error?'' The reason is that
; there's no such clause in the defun of apply$. The clause you're thinking
; about only works for (apply$ 'apply$ ...) not (apply$ 'collect ...). The
; meaning of (apply$ 'collect ...) is, by the defun of apply$, whatever
; apply$-userfn says it is, which is controlled by the warrant for collect.
; About the definition of APPLY$-LAMBDA:
; The only reason we define APPLY$-LAMBDA is so that we can attach a concrete
; executable counterpart to it in the ACL2 source code. We'd prefer not to
; have the function occur in our proofs and so we will always expand it away.
; (See apply$-lambda-opener in books/projects/apply/base.lisp).
; About the definition of EV$:
; In books/projects/apply/base.lisp (and in the model) we prove a simpler
; version of the defun of EV$, conditioned by the hypothesis that x is tamep.
; (This simpler definition, called ev$-def is LOCAL to that book but is used to
; prove ev$-opener which embodies the definition in an effective way.) So why
; do we define EV$ as we do above? In the two clauses dealing with calls of
; APPLY$ and EV$ we apply$ the relevant function symbol rather than just
; calling it, e.g., we write (apply$ 'apply$ ...) instead of (apply$ ...). We
; do it this way so that we can more easily prove that in all cases, ev$
; handles function calls by calling apply$ on the ev$-list of the arguments.
; But note that we don't write it quite that way because we need to prove
; termination. That is, instead of calling ev$-list we actually write an
; explicit list of the two arguments (list (cadr (cadr x)) (EV$ (caddr x) a)).
; Note in particular that we do not ev$ the first argument but just take its
; cadr! This ensures termination and is equivalent to (ev$ (cadr x) a)
; PROVIDED the argument is tame, because tameness guarantees that the first
; argument is quoted! Note also that we could have called (ev$-list (cdr x) a)
; had we known (cdr x) was suitably tame but that would require admitting this
; clique as a reflexive function: the fact that (ev$ (cadr x) a) is smaller
; than (cadr x) when (cadr x) is tame requires reasoning about ev$ before it is
; admitted.
#-acl2-devel
(in-theory (disable badge
(:executable-counterpart badge)))
; -----------------------------------------------------------------
; 4. Executable Versions of BADGE and TAMEP
; In order to infer badges of new functions as will be done in defwarrant we
; must be able to determine the badges of already-badged functions. Similarly,
; we must be able to determine that certain quoted expressions are tame. So we
; define executable versions of badge and tamep that look at data structures
; maintained by defwarrant.
; At one time the definitions were here for executable-badge,
; executable-tamep-lambdap, executable-tamep, executable-tamep-functionp, and
; executable-suitably-tamep-listp. These definitions are now in
; translate.lisp.
; -----------------------------------------------------------------
; 5. BADGER and the Badge-Table
; Recall (from constraints.lisp) that three categories of functions have
; badges: the ~800 apply$ primitives, the 6 apply$ boot functions, and all the
; user-defined functions on which defwarrant succeeded. The last category of
; badges are stored in the badge-table under the key :badge-userfn-structure.
; Given a function fn, (executable-badge fn wrld), defined above in this file,
; returns the badge, or nil. We are here primarily interested in the
; badge-table, which is maintained by defwarrant. defwarrant infers badges
; (and builds warrants with them) by recursively inspecting the body of defun'd
; functions. It uses executable-badge to acquire badges of subroutines.
; Here are some terms we use below:
; ilk: one of the symbols NIL, :FN, or :EXPR; if a variable or argument
; position has ilk NIL we sometimes say it is ``vanilla'' or ``ordinary.'' If
; a variable or argument position has ilk :FN we say it is ``functional'' and
; if it has ilk :EXPR we say it is ``expressional.'' The badge inference
; mechanism uses two more pseudo-ilks, :unknown and :unknown*, which never get
; out of that inference mechanism and should not be confused with ilks.
; ilks: a true list of ilk (or in badge inference, pseudo-ilk) symbols or T
; denoting a list of as many NILs as we'll need. The ilks associated with a
; function symbol fn with formals v1, ..., vn, has length n, and successive
; formals have the corresponding ilk in ilks. For example, if ilks is (NIL
; :FN :EXPR) and the formals are (X Y Z), then X is vanilla, Y is functional
; and Z is expressional.
; badge: a defrec record structure object associated with a function symbol fn.
; The badge has name APPLY$-BADGE and three fields:
; :arity - arity of fn
; :out-arity - number of output values
; :ilks - ilks (of length equal to the arity of fn) corresponding to the
; formals of fn, or else T denoting a list of nils of the appropriate
; length.
; For example, the function COLLECT (mentioned in a comment above) has badge
; (APPLY$-BADGE 2 ; = :arity
; 1 ; = :out-arity
; . (NIL :FN)) ; first formal is ordinary, second formal is
; ; treated like a function
; If a function fn with formals v1, ..., vn, has a badge with :arity n,
; :out-arity k and :ilks T or (c1 ... cn) then we know that fn does not have
; state or stobjs in its signature, that fn takes n arguments and returns k
; values, and
; - if fn is an apply$ primitive, then ilks is T
; - if fn is an apply$ boot function, then the ilks are as given below:
; fn ilks
; BADGE T
; TAMEP T
; TAMEP-FUNCTIONP T
; SUITABLY-TAMEP-LISTP T
; APPLY$ (:FN NIL)
; EV$ (:EXPR NIL)
; - otherwise, fn is a user-defined function successfully processed by
; defwarrant and thus:
; Fn is a defined :logic mode function without state or stobjs in its
; signature, its justification is ``pre-apply$ definable,'' meaning that
; the measure, well-founded relation, and domain are ancestrally
; independent of apply$-userfn, and is either in class (G1) or (G2) as
; described below:
; (G1) fn's body is ancestrally independent of apply$-userfn and
; ancestrally independent of inapplicative functions, like sys-call.
; (This odd extra condition is necessary because we don't actually
; require that fn's body be fully badged. If fn could only call
; badged functions, then we wouldn't have to check that such
; functions as sys-call are not involved.)
; (G2) fn's body is ancestrally dependent on apply$-usefn and
; (a) one of these conditions hold:
; * fn is not recursively defined, or
; * fn is recursively defined with a natural number measure and
; well-founded relation O<, and domain O-P, or
; * fn is recursively defined with a measure that is the
; macroexpansion of an LLIST expression, well-founded relation L<,
; and domain LEXP
; Recall from above that we also know justification is pre-apply$
; definable.
; (b) Every function called in the body of fn, except fn itself, has a
; badge (and thus cannot be one of the inapplicable primitives)
; (c) Every formal of ilk :FN is only passed into :FN slots and every
; :FN slot in the body is either occupied by a formal of ilk :FN or
; by a quoted tame function symbol other than fn itself, or a quoted
; well-formed (fully translated and closed), tame lambda expression
; that does not call fn.
; (d) Every formal of ilk :EXPR is only passed into :EXPR slots and
; every :EXPR slot in the body is either occupied by a formal of ilk
; :EXPR or by a quoted, well-formed (fully translated), tame term
; that does not call fn.
; (e) If formal vi has ilk :FN or :EXPR then vi is passed unchanged into
; the ith slot of every recursive call of fn in the body.
; These conditions are important to our model construction.
; TODO: We cannot analyze mutually recursive defuns yet! We have not yet tried
; to extend the modeling process to accommodate mutually recursive mapping
; functions into the clique with APPLY$ and EV$.
; Note about Inferring Ilks
; We actually beta reduce all ACL2 lambda applications in the body before we
; begin inferring ilks. We discuss this decision further below but for the
; moment just imagine that the body contains no lambda applications.
; If every function symbol mentioned in a (beta-reduced) term has a
; badge then we can tag every occurrence of every subterm in term with an ilk
; of NIL, :FN or :EXPR, as follows: tag the top-level occurrence of the term
; with NIL and then, for each subterm occurring as the nth actual to some call
; of a function, g, tag that occurrence of the subterm with the nth ilk of g.
; We call these the ``occurrence ilks'' of the body of fn, to distinguish them
; from the ilks assigned to the formals of fn by the badge of fn. If a subterm
; occurrence has an occurrence ilk of :ilk, then we say the subterm ``occurs in
; an :ilk slot.''
; For example, consider the top-level term: (apply$ x (cons y (ev$ u v))). The
; occurrence ilks are then:
; occurrence ilk
; (apply$ x (cons y (ev$ u v))) NIL
; x :FN
; (cons y (ev$ u v)) NIL
; y NIL
; (ev$ u v) NIL
; u :EXPR
; v NIL
; For example, the subterm (ev$ u v) occurs in a NIL slot and u occurs in an
; :EXPR slot. The occurrence ilk of each formal variable is its ilk and each
; formal variable has a single ilk. That is, every occurrence of a formal must
; have the same occurrence ilk.
; The basic algorithm for inferring is as follows: in an alist, provisionally
; assign the pseudo-ilk :unknown to each formal, and explore the body keeping
; track of the occurrence ilk of each subterm we encounter, and accumulating
; into the alist the occurrence ilks of each use of each formal or signalling
; an error when a formal with an already known ilk is misused. Of course, we
; do not yet know the ilks of the slots in recursive calls so some uses of a
; formal may not actually tell us anything about its ilk.
; As we explore the body we also enforce the rules (b)-(e) above; rule (a) is
; assured before we start. These checks are sort of scattered since we are
; recursing through a term maintaining occurrence ilks. For example, consider
; (c). The first part of (c) says that every :FN formal is only used in :FN
; slots; the second part says that every :FN slot is occupied either by a :FN
; formal or by a quoted tame object (satisfying still other requirements). The
; first part is checked whenever we encounter a variable: we check that its
; assigned ilk is identical to its occurrence ilk. The second part is checked
; in three places: when we encounter a variable (as just stated), when we
; encounter a quote with occurrence ilk :FN, and when we encounter anything
; else with occurrence ilk :FN.
; Because we may process some recursive calls before we know the ilks of
; everything, we make a second pass confirming that the rules all hold. We
; detect and signal many errors along the way, which obscures the code.
; Finally, because we're checking many things and signalling ``helpful'' error
; messages, the code is pretty obscure! But see the Essay on CHECK-ILKS below.
; Note about Lambda Applications
; To infer the ilks of formals we first beta reduce all ACL2 lambda
; applications in the body. That is, when we say we analyze the body of a
; function we really mean we analyze the body after beta reducing all lambda
; applications in it. [Important distinction: We really do mean we beta reduce
; the ACL2 lambda applications, not ``apply$'s of lambda objects''. Just as we
; distinguish ``ACL2 lambda expressions'' from ``lambda objects,'' -- the
; former being ordinary first class ACL2 terms and the latter being quoted
; constants -- one must distinguish ``ACL2 lambda applications,'' which are
; just ordinary first-class terms, from ``apply$'s of lambda objects.'' Here
; we're talking just about expanding all of the former, i.e., getting rid of
; LET's, LET*'s, etc!]
; To illustrate the problem, consider
; (defun fn (x y)
; ((lambda (u v) (apply$ u v)) x y))
; Note that to determine that x is of ilk :FN we must follow x into the lambda
; via u. This is just the idea of beta reduction. Rather than do it
; repeatedly every time we encounter a given lambda application we just expand
; them all before inferring ilks.
; Alternatively, we could recursively determine that the ilks of the lambda
; formals are :FN and NIL respectively, and then treat the lambda application
; like an ordinary function call with a known badge.
; This idea seems more compositional but is complicated by the fact that we
; might run into recursive calls of fn inside the body of that lambda and they
; will not contribute (initially) to the ilks of the slots. So we would have
; to replace our simple alist, mentioned above and used to track the ilks
; determined so far for the formals of fn, with something more complicated that
; keeps track of local variables within lambdas or ilks for each lambda object
; or something. In any case, the presence of recursive calls both inside the
; lambdas and outside the lambdas complicates the inductive inference. It is
; clearly simpler to just get rid of the lambda applications!
; Warning: One might dismiss the possibility that the body of the lambda
; involves recursion on fn -- mistakenly thinking that recursion is not allowed
; inside lambdas as per restriction (c) above. But one would be confused!
; (One often is!) ACL2 lambda expressions may well involve recursive calls of
; the fn being defined -- it happens whenever a recursively defun'd function
; starts with a LET.
(mutual-recursion
(defun quick-check-for-tamenessp (name term wrld)
; We return t or nil according to whether every function symbol mentioned in
; term (except name) is badged as tame. Note that if every function in the
; body is tame except fn, and fn is defined to be body, then fn is tame. So
; this is really a computational induction argument that says if the recursive
; calls and all subroutines are tame then fn is tame. Note that fn might be
; tame even if some subroutines aren't. For example, the following fn is tame:
; (defun fn (x y) (apply$ 'binary-+ (list x y))).
(declare (xargs :mode :program))
(cond ((variablep term) t)
((fquotep term) t)
(t (let ((fn (ffn-symb term)))
(cond
((flambdap fn)
(and (quick-check-for-tamenessp name (lambda-body fn) wrld)
(quick-check-for-tamenessp-listp name (fargs term) wrld)))
((eq name fn)
(quick-check-for-tamenessp-listp name (fargs term) wrld))
(t (let ((bdg (executable-badge fn wrld)))
(and bdg
(eq (access apply$-badge bdg :ilks) t)
(quick-check-for-tamenessp-listp name (fargs term) wrld)))))))))
(defun quick-check-for-tamenessp-listp (name terms wrld)
(declare (xargs :mode :program))
(cond
((endp terms) t)
(t (and (quick-check-for-tamenessp name (car terms) wrld)
(quick-check-for-tamenessp-listp name (cdr terms) wrld)))))
)
(defun accumulate-ilk (var ilk alist)
; Var has occurred in an ilk slot. If ilk is :UNKNOWN, then this occurrence
; tells us nothing and we return (mv nil alist). Else, ilk is one of NIL, :FN,
; or :EXPR. If var is not bound in alist, then let alist' be alist with the
; new binding (var . ilk) and return (mv nil alist'). If var is bound in alist
; then confirm that the binding is the same as ilk (returning (mv nil alist)),
; or cause an error by returning (mv msg nil).
(declare (xargs :mode :program))
(let ((temp (assoc-eq var alist)))
(cond
((null temp)
(if (eq ilk :unknown)
(mv nil alist)
(mv nil
(cons (cons var ilk)
alist))))
((eq ilk :unknown)
(mv nil alist))
((eq ilk (cdr temp)) (mv nil alist))
(t (mv (msg "The variable ~x0 is used both as ~#1~[a function (ilk = ~
:FN)~/an expression (ilk = :EXPR)~/an ordinary object (ilk ~
= NIL)~] and as ~#2~[a function (ilk = :FN)~/an expression ~
(ilk = :EXPR)~/an ordinary object (ilk = NIL)~]."
var
(cond
((eq (cdr temp) :FN) 0)
((eq (cdr temp) :EXPR) 1)
(t 2))
(cond
((eq ilk :FN) 0)
((eq ilk :EXPR) 1)
(t 2)))
nil)))))
(defun convert-ilk-alist-to-ilks1 (formals alist)
; Return the ilks of the formals, as recorded in alist, defaulting unbound
; formals to ilk NIL. E.g., if given formals (X Y Z) and alist ((X . NIL) (Y
; . :FN)) we return (NIL :FN NIL).
(declare (xargs :mode :program))
(cond ((endp formals) nil)
(t (cons (cdr (assoc-eq (car formals) alist))
(convert-ilk-alist-to-ilks1 (cdr formals) alist)))))
(defun convert-ilk-alist-to-ilks (formals alist)
; Convert an alist, mapping some formals to ilks, to the value of the :ilks
; field of an apply$-badge. This assigns unassigned formals the ilk NIL and
; returns T if the result is all nils.
(declare (xargs :mode :program))
(let ((temp (convert-ilk-alist-to-ilks1 formals alist)))
(if (all-nils temp)
t
temp)))
(defun changed-functional-or-expressional-formalp (formals ilks actuals)
; If there is a formal, vi, of ilk :FN or :EXPR whose corresponding actual is
; not identical to vi, we return the first such (formal ilk actual) triple.
; Else nil. We know formals, ilks, and actuals are all the same length. (Ilks
; = T is handled before calling this function.)
(declare (xargs :mode :program))
(cond
((endp formals) nil)
((and (or (eq (car ilks) :FN)
(eq (car ilks) :EXPR))
(not (eq (car formals) (car actuals))))
(list (car formals) (car ilks) (car actuals)))
(t (changed-functional-or-expressional-formalp (cdr formals)
(cdr ilks)
(cdr actuals)))))
(defun weak-well-formed-lambda-objectp (x wrld)
; Check that x is (lambda vars body) or (lambda vars dcl body) where vars is a
; list of distinct legal variable names, body is a well-formed term wrt wrld,
; and the free vars of body are a subset of vars. We omit the checks involving
; dcl and the tameness check for body.
; This function is one of the ways of recognizing a lambda object. See the end
; of the Essay on Lambda Objects and Lambda$ for a discussion of the various
; recognizers and their purposes.
(declare (xargs :mode :program))
(case-match x
(('LAMBDA formals body)
(and (arglistp formals)
(termp body wrld)
(subsetp-eq (all-vars body) formals)
))
(('LAMBDA formals dcl body)
(declare (ignore dcl))
(and (arglistp formals)
(termp body wrld)
(subsetp-eq (all-vars body) formals)
))
(& nil)))
(mutual-recursion
(defun guess-ilks-alist (fn new-badge term ilk wrld alist)
; Fn is the name of a function being defined. New-badge is either nil or a
; proposed badge for fn and term is a term (initally the body of fn) occuring
; in a slot of ilk ilk. Note: term contains no ACL2 lambda applications! We
; try to determine the ilks of the free vars in term, extending alist to record
; what we find. We return (mv msg alist'), where either msg is an error
; message and alist' is nil, or msg is nil and alist' is extension of alist
; assiging ilks to free vars occurring in term.
; Foreshadowing: We will call this function twice on every name being badged:
; the first time new-badge is nil and the initial alist is nil, and the
; resulting ilks are used to construct a proposed badge and a complete alist
; that binds every formal to the ilk specified by new-badge, then we call the
; function a second time with these new values. The second pass is different
; because there are no unknowns and we have a proposed badge for fn, which
; means we can check recursive calls. Only if the second pass completes
; without error and returns exactly the same alist do we accept the badge.
; We offer an informal but partially mechanically checked proof, below, that
; this rather complicated function is correct. See the Essay on Check-Ilks
; after the defun of badger, which is the function that manages the
; pre-conditions and two passes of this function. The function is more
; complicated than its spec because we try to generate good error messages.
; A few details:
; All ACL2 lambda applications in the body of the function being analyzed
; should be expanded away before this function is called. There may still be
; lambda objects used as arguments to scions, but no ACL2 lambda applications.
; Lambda objects need not be well-formed. All free variables encountered in
; term are formals of the function being analyzed!
; Ilk is the ``occurrence ilk'' of the current occurrence of term; however, it
; is one of the values: NIL, :FN, :EXPR, or :UNKNOWN. The last means the
; occurrence of term is as an actual in a call of fn whose ilks are being
; computed. If term is a variable with :UNKNOWN ilk and the body does not
; definitively assign an ilk to term, we will assign the ilk NIL at the end of
; pass 1. One way this could happen is while trying to determine ilks for:
; (defun fn (x y z) (if (endp x) nil (fn (cdr x) z y))).
; Alist maps the variables we've seen so far to one of the three known ilks,
; NIL, :FN, or :EXPR. If a variable is not bound in alist, it it as though it
; is bound to :UNKNOWN. Thus, the nil alist is the appropriate initial value.
; When we're done with the first pass we complete the alist by assigning nil to
; all variables not otherwise bound. Once the alist binds every formal to a
; known ilk (as is always the case on the second pass) then any conflicting ilk
; will cause an error.
(declare (xargs :mode :program))
(cond
((variablep term)
(accumulate-ilk term ilk alist))
((fquotep term)
; Every exit from this clause is either an error or (mv nil alist). We are
; just very particular about which error message we generate.
(cond
((eq ilk :FN)
; The evg must be a tame function. We could call executable-tamep-functionp
; but we want to check some additional properties and signal appropriate errors,
; so we consider lambda objects separately from quoted symbols...
(cond
((symbolp (cadr term))
(cond
((and (eq fn (cadr term))
(not (executable-badge fn wrld)))
; Note that we avoid the error by testing whether (executable-badge fn wrld) is
; non-nil. This means fn was admitted with :LOOP$-RECURSION. But at the
; moment, a loop like (loop$ for x in lst collect (fn x)) translates to
; (collect$ (lambda$ (x)(fn x)) lst), not (collect$ 'fn lst), so technically
; loop$ recursion has not occured! But the user who knows what is going on
; could well type (collect$ 'fn lst) if she wanted to, since it is logically
; equivalent to the loop$ translation and more succinct! So we allow it.
(mv (msg "~x0 cannot be warranted because a :FN slot in its body is ~
occupied by a quoted reference to ~x0 itself; recursion ~
through APPLY$ is not permitted except when the function ~
was admitted with XARGS :LOOP$-RECURSION T, which only ~
permits such recursion in LOOP$s."
fn)
nil))
((executable-tamep-functionp (cadr term) wrld)
(mv nil alist))
(t (mv (msg "~x0 cannot be warranted because a :FN slot in its ~
body is occupied by a quoted symbol, ~x1, that is not a ~
tame function."
fn
term)
nil))))
((consp (cadr term))
(cond ((weak-well-formed-lambda-objectp (cadr term) wrld)
; See the Essay on Lambda Objects and Lambda$, in particular, item (2) of the
; ``confusing variety of concepts'' for recognizing lambda objects. The check
; above ensures that the formals of the object are legal distinct variables and
; that the body is a term. The dcl, if any, is unchecked and tameness and
; closure are unchecked.
(cond
((and (ffnnamep fn (lambda-object-body (cadr term)))
(not (executable-badge fn wrld)))
(mv (msg "~x0 cannot be warranted because a :FN slot in its ~
body is occupied by a lambda object, ~x1, that ~
recursively calls ~x0; recursion through APPLY$ is ~
not permitted unless the function was admitted ~
with XARGS :LOOP$-RECURSION T, which only permits ~
such recursion in LOOP$s."
fn
term)
nil))
((executable-tamep-lambdap (cadr term) wrld)
; See item (3) of the above mentioned discussion.
(mv nil alist))
(t (mv (msg "~x0 cannot be warranted because a :FN slot in ~
its body is occupied by a lambda object, ~x1, ~
whose body is not tame."
fn
term)
nil))))