-
Notifications
You must be signed in to change notification settings - Fork 201
/
LevelSpec.tla
2009 lines (1904 loc) · 129 KB
/
LevelSpec.tla
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
----------------------------- MODULE LevelSpec ------------------------------
(***************************************************************************)
(* This module specifies the level-checking for the TLA+ language. See *)
(* Section 17.2 of "Specifying Systems" for a discussion of levels and *)
(* level checking in TLA+. *)
(* *)
(* The semantics of TLA+ requires that, in the following constructs, e *)
(* must have level at most 1 (that is, not contain primed variables) and *)
(* A and B must have level at most 2 (that is, not contain temporal *)
(* operators): *)
(* *)
(* e' [A]_e <<A>>_e ENABLED A UNCHANGED e A \cdot B *)
(* *)
(* Although the semantics doesn't really demand it, we also use level *)
(* checking to rule out some bizarre expressions like *)
(* *)
(* []F + []G *)
(* *)
(* We do this by requiring that an operator argument that can normally be *)
(* a non-Boolean must have level at most 2, so it cannot be a temporal *)
(* formula. Thus, in the expression *)
(* *)
(* {e, f} *)
(* *)
(* we require that e and f have level at most 2. *)
(* *)
(* There is another aspect of level checking that is not described here. *)
(* TLA (as opposed to "raw TLA") does not allow an action like x'=x+1 to *)
(* be used as a temporal formula. Thus, in all the following formulas, F *)
(* and G can have any level other than 2: *)
(* *)
(* []F <<F>> F ~> G F -+-> G \EE x : F \AA x : F *)
(* *)
(* A general algorithm for detecting all violations of this requirement *)
(* is nontrivial. For example, if we have *)
(* *)
(* ----- MODULE M ----- *)
(* CONSTANTS A, Op(_) *)
(* B == Op(A) *)
(* ==================== *)
(* *)
(* ------------- MODULE I ------------- *)
(* VARIABLE x *)
(* C(F) == []F *)
(* INSTANCE M WITH A <- x'=x+1, Op <- C *)
(* ==================================== *)
(* *)
(* then the last instance is illegal because it defines B in module I to *)
(* equal the illegal formula [](x'=x+1). Again, this specification does *)
(* not handle this problem. I presume that the initial version of SANY *)
(* will do a simple check of the level of an expression F or G in the *)
(* expressions listed above, and complain only if the expression has *)
(* explicit level 2. *)
(***************************************************************************)
EXTENDS Integers, Sequences
-----------------------------------------------------------------------------
(****************************)
(* Some Useful Definitions *)
(****************************)
NumMax(i, j) == IF i > j THEN i ELSE j
\* Max is apparently defined in the TLC overridden Naturals module
(*************************************************************************)
(* The maximum of the number i and j. *)
(*************************************************************************)
SetMax(S) == IF S = {} THEN 0
ELSE CHOOSE x \in S : \A y \in S : x \geq y
(*************************************************************************)
(* The maximum of the set S of natural numbers. *)
(*************************************************************************)
RecordCombine(S, T) ==
(*************************************************************************)
(* If S and T are sets of records, then this equals the set of all *)
(* records rc(s,t) with s in S and t in T, where rc(s,t) is the record *)
(* obtained by "merging" s and t--that is, forming the record whose set *)
(* of fields is the union of the sets of fields of the two records. *)
(*************************************************************************)
LET rc(s, t) ==
[i \in (DOMAIN s) \cup (DOMAIN t) |-> IF i \in DOMAIN s THEN s[i]
ELSE t[i]]
IN {rc(s, t) : s \in S, t \in T}
-----------------------------------------------------------------------------
CONSTANT NodeId, Node
(*************************************************************************)
(* We represent a collection of TLA+ modules by a semantic forest, *)
(* composed of nodes that may contain references to other nodes. Each *)
(* module is represented by a tree that may have links to the nodes of *)
(* other modules that it imports. The set NodeId is the set of all node *)
(* identifiers, which can be thought of as the set of references to *)
(* nodes. The constant Node is a function from NodeId to the set (type) *)
(* of all possible semantic nodes. *)
(*************************************************************************)
Null == CHOOSE n : n \notin NodeId
(*************************************************************************)
(* A value that is not a node id. *)
(*************************************************************************)
-----------------------------------------------------------------------------
(***************************************************************************)
(* The Semantic Nodes *)
(* ------------------ *)
(* Here are the kinds of semantic nodes and what they represent *)
(* *)
(* ModuleNode : A module *)
(* InstanceNode : An INSTANCE statement *)
(* OpDefNode : An operator definition. As explained *)
(* below, all built-in TLA+ constructs are *)
(* represented as defined operators. *)
(* ConstantDeclNode : A constant declaration or a formal parameter *)
(* of a definition *)
(* VariableDeclNode : A variable declaration. *)
(* OpDeclNode : A ConstantDeclNode or a VariableDeclNode *)
(* OpApplNode : An application of an operator. *)
(* SubstitutionNode : The sequence of WITH substitutions for an *)
(* INSTANCE statement. *)
(* BoundSymbolNode : A bounded identifier. *)
(* LetInNode : A LET/IN statement. *)
(* ValueNode : A string or number. *)
(* IdentifierNode : An expression consisting of a single symbol. *)
(* Also used to represent an operator argument of *)
(* a second-order operator. *)
(* OpDefOrDeclNode : An OpDefNode or an OpDeclNode. *)
(* ExprNode : An expression, which is an OppApplNode, a *)
(* LetInNode, a ValueNode, an IdentifierNode, *)
(* *)
(* Note: The SANY API apparently includes the following object types and *)
(* kinds not included as semantic nodes in this spec. Here is what they *)
(* correspond to in this spec: *)
(* *)
(* FormalParamNode object : Represented by a ConstantDeclNode. *)
(* *)
(* OpArgNode object : Represented by an IdentifierNode. *)
(* *)
(* For every kind xxxNode of semantic node, we define xxxNodeId to be the *)
(* set of node ids of nodes of kind xxxNode. We use the fact that a *)
(* semantic node has a kind field and an xxxNode object has kind field *)
(* equal to "xxxNode". *)
(***************************************************************************)
Ref(str) == {id \in NodeId : Node[id].kind = str}
ModuleNodeId == Ref("ModuleNode")
InstanceNodeId == Ref("InstanceNodeId")
OpDefNodeId == Ref("OpDefNode")
ConstantDeclNodeId == Ref("ConstantDeclNode")
VariableDeclNodeId == Ref("VariableDeclNode")
OpDeclNodeId == ConstantDeclNodeId \cup VariableDeclNodeId
OpApplNodeId == Ref("OpApplNode")
SubstitutionNodeId == Ref("SubstitutionNode")
BoundSymbolNodeId == Ref("BoundSymbolNode")
LetInNodeId == Ref("LetInNode")
ValueNodeId == Ref("ValueNode")
IdentifierNodeId == Ref("IdentifierNode")
OpDefOrDeclNodeId == OpDefNodeId \cup OpDeclNodeId
ExprNodeId == OpApplNodeId \cup LetInNodeId \cup ValueNodeId
\cup IdentifierNodeId
-----------------------------------------------------------------------------
(**************************)
(* Level Data Structures *)
(**************************)
LevelValue == 0..3
(*************************************************************************)
(* The set of levels, where *)
(* 0 = constant *)
(* 1 = state function *)
(* 2 = action *)
(* 3 = temporal formula *)
(* (See Section 17.2 of "Specifying Systems".) *)
(*************************************************************************)
(***************************************************************************)
(* To understand level checking, consider the following definition. *)
(* *)
(* Foo(a, b, c) == a /\ ENABLED(b'=c) *)
(* *)
(* Since a level-2 expression cannot be primed and the argument of ENABLED *)
(* can have level at most 2, an expression Foo(exp1, exp2, exp3) is legal *)
(* iff the level of exp2 is at most 1 and the level of exp3 is at most 2. *)
(* An ENABLED expression has level equal to 1. (For simplicity, we *)
(* consider ENABLED (1=1), which is equivalent to TRUE, to have level 1.) *)
(* Hence, the expression Foo(exp1, exp2, exp3) has level equal to the *)
(* maximum of 1 and the level of a. *)
(* *)
(* We can describe the level properties of the operator Foo as follows, *)
(* where n is the semantic OpDefNode corresponding to the definition of *)
(* Foo. We define the level constraints on the arguments of Foo by *)
(* n.maxLevels, where n.maxLevels[i] is the maximum level of the i-th *)
(* argument of Foo. Thus, *)
(* *)
(* n.maxLevels[1] = 3 *)
(* n.maxLevels[2] = 1 *)
(* n.maxLevels[3] = 2 *)
(* *)
(* The level of Foo(exp1, exp2, exp3) is the maximum of 1 and the level of *)
(* exp1. We can express that by saying that it is the maximum of 1 and *)
(* the maximum of the set *)
(* *)
(* {1 * level of exp1, 0 * level of exp2, 0 * level of exp3} *)
(* *)
(* The level of an application of Foo is described by *)
(* *)
(* n.level = 1 *)
(* n.weights[1] = 1 *)
(* n.weights[2] = 0 *)
(* n.weights[3] = 0 *)
(* *)
(* This is all simple enough. Things get complicated for 2nd-order *)
(* operators, which are operators that take an operator as an *)
(* argument--for example *)
(* *)
(* Op2(A(_,_,_), b, c) == A(b, x', c) *)
(* *)
(* The expression Op2(Foo, exp2, exp3) is illegal, because it expands to *)
(* *)
(* Foo(exp2, x', exp3) *)
(* *)
(* and the second of argument of Foo can have level at most 1. In other *)
(* words, we cannot substitute Foo for the first argument of Op2 because *)
(* Foo.maxLevels[2] = 1 and the first argument of Op2 must be able to take *)
(* a second argument of level 2. In general, for an OpDefNode op *)
(* representing a definition of an operator Op, we let *)
(* op.minMaxLevel[i][k] be the minimum value of oparg.maxLevels[k] for the *)
(* i-th argument of Op. Thus, op.minMaxLevels[i] is a sequenced whose *)
(* length is the number of arguments taken by the i-th argument of Op. *)
(* (It equals 0 if the i-th argument of Op is not an operator argument.) *)
(* *)
(* An ideal level-checking algorithm would have the property that it *)
(* considers an expression to be level-correct iff expanding all defined *)
(* operators to obtain an expression that contains only built-in operators *)
(* yields a level-correct expression. The following example indicates the *)
(* complexity of an ideal algorithm that doesn't actually do the *)
(* expansion. *)
(* *)
(* Bar(Op1(_,_)) == Op1(x', x)' *)
(* *)
(* The expression Bar(A) is level-correct iff A is an operator whose level *)
(* does not depend on the level of its first argument--that is, iff *)
(* a.weight[1]=0. To simplify the bookkeeping, we make the conservative *)
(* assumption that any operator parameter may be instantiated with an *)
(* operator whose level depends on the levels of all its arguments. Thus, *)
(* we will disallow this definition of Bar. We will do this even if the *)
(* definition occurs within a LET and we could check that all the actual *)
(* instances of Bar result in level-correct expressions. I can't think of *)
(* any reasonable case where this will disallow a level-correct expression *)
(* that a user is likely to write. *)
(* *)
(* The decision to simplify the bookkeeping results in the following, *)
(* somewhat less unlikely problem. With the definitions *)
(* *)
(* ApplyToPrime(Op(_)) == Op(x') *)
(* EqualsNoPrime(a) == x *)
(* *)
(* the expression ApplyToPrime(EqualsNoPrime)' , which equals x', is *)
(* considered to be illegal. This is because the algorithm to compute the *)
(* level makes the assumption that ApplyToPrime will always be applied to *)
(* operators Op for which the level of Op(exp) depends on the level of *)
(* exp. Hence, SANY's algorithm gives ApplyToPrime(Op) a level of at *)
(* least 2 (primed expression) for any operator Op. A slightly more *)
(* realistic example can be constructed by modifying ApplyToPrime a bit *)
(* and applying it to ENABLED. *)
(* TLC warns users about this bug if it reports an invariant to be *)
(* level-incorrect in tlc2.tool.Spec.processConfigInvariants() with error *)
(* code tlc2.output.EC.TLC_INVARIANT_VIOLATED_LEVEL. *)
(* A corresponding test can be found in test52. Its invariant "Invariant" *)
(* covers the ENABLED scenario. However, the invariant remains disabled *)
(* for as long as this bug is open. The invariant Invariant can be *)
(* re-enabled in test52.cfg once this bug is closed. *)
(* *)
(* To compute the values of op.level, op.weights, and op.minMaxLevel for *)
(* an OpDefNode op corresponding to a definition, a level-checking *)
(* algorithm needs to keep track of the constraints on its formal *)
(* parameters implied by the subexpressions of the definition's body, as *)
(* well has how the level of the body depends on the levels of its *)
(* parameters. For our less than ideal level-checking algorithm, this is *)
(* done by keeping track of sets of objects of the following types. *)
(***************************************************************************)
LevelConstraint == [param : ConstantDeclNodeId, level : LevelValue]
(*************************************************************************)
(* A level constraint lc indicates that the parameter with id lc.param *)
(* can be instantiated only with an expression of level at most *)
(* lc.level. *)
(*************************************************************************)
ArgLevelConstraint ==
(*************************************************************************)
(* An arg-level constraint alc indicates that the operator parameter *)
(* with id alc.param can be instantiated with an operator op only if the *)
(* alc.idx-th argument of op can have level at least alc.level. This *)
(* constraint is vacuous iff alc.level = 0. *)
(*************************************************************************)
[param : ConstantDeclNodeId, idx : Nat \ {0}, level : LevelValue]
ArgLevelParam ==
(*************************************************************************)
(* An arg-level parameter alp indicates that the parameter with id *)
(* alp.param appears in the alp.idx-th argument of the operator with id *)
(* alp.op. *)
(*************************************************************************)
[op : NodeId, idx : Nat \ {0}, param : NodeId]
(***************************************************************************)
(* For later use, we define the following two operators on these data *)
(* types. *)
(***************************************************************************)
MinLevelConstraint(id, LC) ==
(*************************************************************************)
(* If LC is a set of level constraints and id a ConstantDeclNodeId, then *)
(* this equals the minimum level constraint on id implied by the *)
(* elements of LC. (This is 3 if there is none.) *)
(*************************************************************************)
IF \E lc \in LC : lc.param = id
THEN LET minLC == CHOOSE lc \in LC :
/\ lc.param = id
/\ \A olc \in LC :
(olc.param = id) => (olc.level \geq lc.level)
IN minLC.level
ELSE 3
MaxArgLevelConstraints(id, ALC) ==
(*************************************************************************)
(* If ALC is a set of arg-level constraints and id a ConstantDeclNodeId, *)
(* then this equals the tuple <<lev_1, ..., lev_n>>, where n is the *)
(* number of arguments taken by the operator parameter op represented by *)
(* node id, such that the arg-level constraints in ALC imply that op *)
(* must be able to take an i-th operator of level at least lev_i, for *)
(* each i. *)
(*************************************************************************)
LET n == Node[id].numberOfArgs
minALC(i) ==
LET isALC(lc) == (lc.param = id) /\ (lc.idx = i)
IN IF \E lc \in ALC : isALC(lc)
THEN LET max == CHOOSE lc \in ALC :
/\ isALC(lc)
/\ \A olc \in ALC :
isALC(olc) => (olc.level \leq lc.level)
IN max.level
ELSE 0
IN [i \in 1..n |-> minALC(i)]
LevelConstraintFields ==
(*************************************************************************)
(* A record whose fields consist of fields used for level computations. *)
(* These fields are common to all semantic nodes of type Expr that *)
(* represent expressions, as well as to all OpDef nodes, which represent *)
(* operator definitions. Some of these fields also occur in other *)
(* nodes--like Instance and Module nodes. *)
(* *)
(* In general, an expression will be in the scope of some formal *)
(* parameters, so its level will depend on the level of the expressions *)
(* substituted for some of those parameters. For example, if p and q *)
(* are formal parameters, and x is a declared variable, then the level *)
(* of the expression *)
(* *)
(* ENABLED(p' = x) /\ q *)
(* *)
(* is the maximum of 1 (the level of the ENABLED expression) and the *)
(* level of q. For the ExprNode e that represents this expression, *)
(* e.level equals 1 and e.levelParams is the set whose single element is *)
(* the (id of the) ConstantDeclNode for q. Here's a description *)
(* of the level fields, where the parameter set of e is the set of all *)
(* parameters (formal definition parameters or declared constants) such *)
(* that e appears in the scope of their declarations. *)
(* *)
(* e.level: A level value. If all the parameters appearing in e *)
(* were instantiated with constants, then e.level would be *)
(* the level of the resulting expression. *)
(* *)
(* e.levelParams : A set of parameters from the parameter set of e. *)
(* You can think of these in two equivalent ways: *)
(* - They are the parameters whose levels contribute to the *)
(* level of e. *)
(* - They are the parameters appearing in e that would get *)
(* primed if expression e were primed. *)
(* An element of e.levelParams is called a LEVEL PARAMETER of e. *)
(* *)
(* e.levelConstraints : A set of level constraints, in which *)
(* all the parameters that appear belong to the parameter set *)
(* of e. *)
(* *)
(* e.argLevelConstraints : A set of arg-level constraints, in which *)
(* all the parameters that appear are (operator) parameters of *)
(* the parameter set of e. *)
(* *)
(* e.argLevelParams : A set of arg-level parameters. *)
(* An element alp indicates that there is a subexpression of e *)
(* (or of its definition, if e is a defined operator) *)
(* of the form alp.op(... ), where alp.param is a *)
(* level parameter of the alp.idx-th argument. *)
(* NOTE: For an OpDefNode op, op.argLevelParams can contain *)
(* elements alp with alp.op and/or alp.param (but not both) *)
(* being formal parameters of the definition. This will *)
(* happen if the definition contains a subexpression Op(arg) *)
(* where either Op is a formal parameter or arg contains a *)
(* formal parameter. (These elements are used for level-checking *)
(* an instantiated version of the definition obtained by an *)
(* INSTANCE statement.) *)
(* *)
(* In the computation, we don't bother eliminating redundant elements *)
(* from these sets. For example, a level constraint lc is redundant if *)
(* there is another level constraint lco such that lco.param = lc.param *)
(* and lco.level < lc.level. A more efficient algorithm would eliminate *)
(* the redundant elements from e.levelConstraints and *)
(* e.argLevelConstraints. *)
(*************************************************************************)
[levelParams : SUBSET ConstantDeclNodeId,
levelConstraints : SUBSET LevelConstraint,
argLevelConstraints : SUBSET ArgLevelConstraint,
argLevelParams : SUBSET ArgLevelParam]
-----------------------------------------------------------------------------
(***************************************************************************)
(* Definitions of the Semantic Nodes *)
(* *)
(* A fair amount of information not relevant to level checking, but *)
(* present in the SANY api, has been eliminated from these definitions of *)
(* the semantic node types. For example, some sequences have been changed *)
(* to sets where their order of occurrence is not relevant to level *)
(* checking (but is relevant to correctness of the module). *)
(***************************************************************************)
ModuleNode ==
(*************************************************************************)
(* A semantic node representing a module. *)
(*************************************************************************)
[kind : {"ModuleNode"},
isConstant : BOOLEAN,
(**********************************************************************)
(* True iff this is a constant module. A constant module is one with *)
(* no VARIABLE declarations and no non-constant operators. We won't *)
(* bother defining this precisely. *)
(* *)
(* Note: In TLA+, the only way to define a constant operator that *)
(* contains a non-constant subexpression is by throwing the *)
(* subexpression away--for example: *)
(* *)
(* Foo(a) == LET Bar(b, c) == b *)
(* IN Bar(a, x') *)
(* *)
(* which is a silly way to write Foo(a) == a. It would thus be safe *)
(* to define a constant module to be one with no declared variables *)
(* and in which all definitions and theorems have level 0. This *)
(* would allow a constant module to have the silly definition above *)
(* of Foo (assuming x is not a declared variable). However, the *)
(* official definition of a constant module prohibits it from having *)
(* definitions like the one above for Foo. *)
(**********************************************************************)
opDecls : SUBSET OpDeclNodeId,
(**********************************************************************)
(* The set declared constants and variables. *)
(**********************************************************************)
opDefs : SUBSET OpDefNodeId,
(***********************************************************************)
(* The top-level operator definitions (ones not defined inside LET's) *)
(* in this module--including definitions incorporated from extended *)
(* and instantiated modules. It includes function definitions *)
(* (definitions of the form f[x \in S] == e) and all definitions *)
(* introduced into the module by module instantiations. (A module *)
(* instantiation creates a new OpDefNode for each OpDefNode in the *)
(* instantiated module.) *)
(***********************************************************************)
instances : SUBSET InstanceNodeId,
(**********************************************************************)
(* The top-level INSTANCEs (ones not defined inside LET's) in this *)
(* module. *)
(**********************************************************************)
innerModules : SUBSET ModuleNodeId,
(**********************************************************************)
(* The top-level submodules that appear in this module. *)
(**********************************************************************)
theorems : SUBSET ExprNodeId,
assumes : SUBSET ExprNodeId,
(**********************************************************************)
(* In this representation, a theorem or assumption node points to an *)
(* ExprNode. *)
(**********************************************************************)
levelConstraints : SUBSET LevelConstraint,
argLevelConstraints : SUBSET ArgLevelConstraint,
argLevelParams : SUBSET ArgLevelParam]
(**********************************************************************)
(* The meanings of these sets of constraints are described with the *)
(* definitions of the constraint data types. The parameters that *)
(* appear in them are the declared constants and variables of the *)
(* module. These constraints are used to check the legality of *)
(* instantiation if this is a constant module. For a non-constant *)
(* module, these fields are not needed, because declared constant *)
(* operators can be instantiated only with constant operators. For a *)
(* constant module, the levelConstraints and argLevelConstraints *)
(* fields reflect only constraints that prevent constants from being *)
(* instantiated with temporal (level 3) formulas. *)
(* *)
(* The MaxLevels method of the api is defined in terms of *)
(* levelConstraints as follows. If id is the NodeId of the i-th *)
(* declared constant, and mod is the ModuleNodeId, then *)
(* *)
(* MaxLevels(i) = *)
(* IF mod.constantModule *)
(* THEN MinLevelConstraint(id, mod.levelConstraints) *)
(* ELSE 0 *)
(**********************************************************************)
OpDefOrDeclNodeFields ==
(*************************************************************************)
(* This defines the fields that are common to the OpDeclNode and *)
(* OpDefNode types. *)
(*************************************************************************)
[name : STRING,
(**********************************************************************)
(* The name of the operator. (This isn't used in the level *)
(* computation, but it's convenient for keeping track of things when *)
(* running tests of the spec with TLC.) *)
(**********************************************************************)
numberOfArgs : Nat,
(**********************************************************************)
(* The number of arguments of the operator. Operators that can take *)
(* an arbitrary number of arguments are represented by an infinite *)
(* sequence of definitions, one for each possible number of *)
(* arguments. For example, we pretend that there is a sequence of *)
(* operators Tuple0, Tuple1, Tuple2, ... such that <<a, b>> is *)
(* represented as Tuple2(a, b). *)
(**********************************************************************)
level : LevelValue]
(**********************************************************************)
(* For an OpDeclNode op, the value of op.level is 0 except for one *)
(* representing a variable declaration, for which op.level equals 1. *)
(* *)
(* The meaning of op.level for an OpDefNode is described above. *)
(**********************************************************************)
OpDeclNode ==
(*************************************************************************)
(* Represents a declared constant or variable. *)
(*************************************************************************)
RecordCombine([kind : {"ConstantDeclNode", "VariableDeclNode"}],
OpDefOrDeclNodeFields)
OpDefNode ==
(*************************************************************************)
(* Represents a definition, for example the definition of the symbol Foo *)
(* in Foo(A, B) == expr. We also assume imaginary definitions of *)
(* built-in symbols. Unlike in the actual SANY api, we represent a *)
(* construction like {exp : x \in S, y \in T} as something like *)
(* SetCons3(exp, S, T), where SetCons3 is an imaginary operator that *)
(* takes three arguments. (As remarked above, we pretend that every *)
(* operator has a fixed number of arguments, so we pretend that there is *)
(* also a separate OpDefNode for the operator SetCons4 used to represent *)
(* the construction {exp : x \in S, y \in T, z \in U}. *)
(* *)
(* As indicated by the formal semantics, a function definition *)
(* *)
(* f[x \in S] == e *)
(* *)
(* is treated like the definition *)
(* *)
(* f == CHOOSE f : f = [x \in S |-> e] *)
(* *)
(* The level-constraint fields of the OpDefNode for an operator Op *)
(* reflects all the constraints implied by the body of the definition of *)
(* Op for the parameters within whose scope the definition appears. *)
(* However, the argLevelParams field may contain arg-level parameters *)
(* whose op or param field (but not both) is a formal parameter of the *)
(* definition. For example, consider *)
(* *)
(* A(Op(_)) == LET B(c) == Op(c) *)
(* IN B(x') *)
(* *)
(* then the fact that the formal parameter c of B appears in the *)
(* definition of B in the argument of Op tells us that the expression *)
(* B(x') in the definition of A implies that A can be used only with a *)
(* first argument that can take an argument of level 2. This is *)
(* recorded by the arg-level parameter *)
(* *)
(* [op |-> Op, idx |-> 1, param |-> c] *)
(* *)
(* in B.argLevelParams. *)
(*************************************************************************)
RecordCombine(
[kind : {"OpDefNode"},
params : Seq(ConstantDeclNodeId),
(********************************************************************)
(* The formal parameters of the definition. *)
(********************************************************************)
maxLevels : Seq(LevelValue),
weights : Seq({0,1}),
minMaxLevel : Seq(Seq(LevelValue)),
opLevelCond : Seq(Seq(Seq(BOOLEAN))),
(********************************************************************)
(* All these fields are described above, except for opLevelCond. *)
(* For an OpDefNode op, op.opLevelCond[i][j][k] is true iff the *)
(* i-th argument of op is an operator argument opArg, and the *)
(* definition of op contains an expression in which the j-th formal *)
(* parameter of the definition of op appears within the k-th *)
(* argument of opArg. As we'll see, this information is needed for *)
(* keeping track of level constraints. *)
(********************************************************************)
body : ExprNodeId \cup {Null},
(********************************************************************)
(* The body of the definition. For a built-in operator, it's Null. *)
(********************************************************************)
substitution : SubstitutionNodeId],
(********************************************************************)
(* Suppose that a module M contains the definition *)
(* *)
(* Op(p, q) == expr *)
(* *)
(* and let mOp be the corresponding OpDef node for operator Op. *)
(* Next suppose that another module N contains *)
(* *)
(* MI(x) == INSTANCE M WITH A <- x+1, B <- x*r *)
(* *)
(* This adds to module N the operator MI!Op such that *)
(* *)
(* MI!Op(x, p, q) == Iexpr *)
(* *)
(* where Iexpr is the expression obtained from expr by substituting *)
(* x+1 for A and x*r for B. (In TLA+ we write *)
(* MI(exp1)!Op(exp2,exp3), but this is just syntax; MI!Op is an *)
(* operator that takes 3 arguments.) The INSTANCE statement adds *)
(* to the semantic tree for module N a UserOpDef node miOp for the *)
(* operator MI!Op such that *)
(* *)
(* miOp.name = "MI!Op", *)
(* miOp.numberOfArgs = 3 *)
(* miOp.params[1] = a ref to a ConstantDeclNode for x *)
(* miOp.params[2] = mOp.params[1] *)
(* miOp.params[3] = mOp.params[2] *)
(* miOp.body = mOp.body *)
(* miOp.substitution = a SubstitutionNode representing *)
(* A <- x+1, B <- x*r *)
(* *)
(* For convenience, if Op does not come from an instantiated *)
(* module, we let the substitution field point to a null *)
(* substitution--that is, one whose subFor and subWith fields are *)
(* the empty sequence. *)
(********************************************************************)
RecordCombine(OpDefOrDeclNodeFields, LevelConstraintFields))
InstanceNode ==
(*************************************************************************)
(* Represents a statement of the form *)
(* *)
(* I(param[1], ... , param[p]) == *)
(* INSTANCE M WITH mparam[1] <- mexp[1], ... , mparam[r] <- mexp[r] *)
(* *)
(* or simply *)
(* *)
(* INSTANCE M WITH mparam[1] <- mexp[1], ... , mparam[r] <- mexp[r] *)
(* *)
(* The mparam[i] are assumed to be all the declared constants and *)
(* variables of M. (That is, implicit substitutions of the form *)
(* param <- param are made explicit.) *)
(*************************************************************************)
[kind : {"InstanceNode"},
module : ModuleNodeId,
(********************************************************************)
(* The instantiated module. *)
(********************************************************************)
params : Seq(ConstantDeclNodeId),
(********************************************************************)
(* The formal parameters of the definition. *)
(********************************************************************)
substitution : SubstitutionNodeId ,
(********************************************************************)
(* The substitution. If M has no parameters, then this is the null *)
(* substitution with subFor and subWith fields equal to the empty *)
(* sequence. *)
(********************************************************************)
numberOfArgs : Nat,
levelConstraints : SUBSET LevelConstraint,
argLevelConstraints : SUBSET ArgLevelConstraint,
argLevelParams : SUBSET ArgLevelParam]
(**********************************************************************)
(* The level constraints obtained from the instantiation. (There are *)
(* no level parameters for the InstanceNode itself.) *)
(**********************************************************************)
OpDefOrDeclNode == OpDefNode \cup OpDeclNode
OpApplNode ==
(*************************************************************************)
(* An OppApplNode represents an operator application. Examples of *)
(* expressions that such a node can represent are: *)
(* *)
(* A \cup B which we think of as \cup(A, B) *)
(* *)
(* (x + y) * (b + c) which we think of as *(+(x,y), +(b,c)) *)
(* *)
(* \E x, y \in S, <<u, v>> \in T : (x+y) > (u+v) which we think of *)
(* here as something like: *)
(* *)
(* \E(S, T, >(+(x,y), +(u,v))) *)
(* *)
(* plus the declarations of x, y, u, and v. (The OpApplNode in the *)
(* actual API maintains the actual structure of the expression, *)
(* Here, we don't bother to distinguish \E x, y, z \in S : P *)
(* from \E <<x, y, z>> \in S : P *)
(*************************************************************************)
RecordCombine(
[kind : {"OpApplNode"},
operator : OpDefOrDeclNodeId,
(********************************************************************)
(* The (id of the) OpDefOrDecl node of the operator. *)
(********************************************************************)
args : Seq(ExprNodeId) \ {<<>>},
(********************************************************************)
(* An OpApplNode has a nonempty sequence of arguments. *)
(********************************************************************)
quantSymbols : SUBSET BoundSymbolNodeId,
(********************************************************************)
(* The bound symbols introduced by the operator application. *)
(********************************************************************)
level : LevelValue],
LevelConstraintFields)
SubstitutionNode ==
(*************************************************************************)
(* The Substitution object s that represents the WITH clause *)
(* *)
(* A <- x+1, B <- x*r *)
(* *)
(* has Len(s.subFor) = Len(s.subWith) = 2 and *)
(* *)
(* s.subFor[1] = the id of the ConstantDecl or VariableDecl *)
(* node for A *)
(* s.subFor[2] = the id of the ConstantDecl or VariableDecl *)
(* node for B *)
(* s.subWith[1] = the id of the ExprNode for x+1 *)
(* s.subWith[2] = the id of the ExprNode for x*r *)
(* *)
(* Note that the nodes referenced in subFor are in the semantic *)
(* tree for the instantiated module, while those referenced in *)
(* subWith are in the semantic tree for the instantiating module. *)
(*************************************************************************)
[kind : {"SubstitutionNode"},
subFor : Seq(OpDeclNodeId),
subWith : Seq(ExprNodeId)]
IdentifierNode ==
(************************************************************************)
(* An IdentifierNode is an ExprNode with a ref field. It represents an *)
(* expression that consists of a single symbol. For example, the *)
(* OppApplNode that represents the expression A * (b + c) will have as *)
(* its list of arguments the subexpressions A and b+c. The *)
(* subexpression A will be an IdentifierNode whose ref field returns *)
(* the OpDefOrDeclNode that declares or defines A. *)
(************************************************************************)
RecordCombine(
[kind : {"IdentifierNode"},
ref : OpDefOrDeclNodeId \cup BoundSymbolNodeId,
level : LevelValue],
LevelConstraintFields)
BoundSymbolNode ==
(*************************************************************************)
(* Represents a bounded identifier, like the x in {x \in S : x > 0}. It *)
(* has level 0 except for the bounded symbols introduced by \EE and \AA, *)
(* which have level 1. *)
(*************************************************************************)
[kind : {"BoundSymbolNode"},
name : STRING,
level : {0,1}]
LetInNode ==
(*************************************************************************)
(* This node represents a LET expression, for example *)
(* *)
(* LET Foo(a) == a + x *)
(* Bar == Foo(a) + a *)
(* IN body *)
(*************************************************************************)
RecordCombine(
[kind : {"LetInNode"},
opDefs : SUBSET OpDefNodeId,
instances : SUBSET InstanceNodeId,
(********************************************************************)
(* The LET definitions and INSTANCE statements. *)
(********************************************************************)
body : ExprNodeId,
level: LevelValue],
LevelConstraintFields)
ValueNode == RecordCombine(
(*************************************************************************)
(* This node type represents the NumeralNode, DecimalNode, and *)
(* StringNode, of the actual api. *)
(*************************************************************************)
[kind : {"ValueNode"},
level : {0}],
LevelConstraintFields)
ExprNode == OpApplNode \cup LetInNode \cup ValueNode \cup IdentifierNode
SemNode ==
(*************************************************************************)
(* The type (set of all possible) semantic nodes. *)
(*************************************************************************)
ModuleNode \cup OpDefOrDeclNode \cup InstanceNode \cup
ExprNode \cup SubstitutionNode \cup BoundSymbolNode
-----------------------------------------------------------------------------
(***************************************************************************)
(* "Type Correctness" *)
(***************************************************************************)
TypeOK ==
(*************************************************************************)
(* This expresses the basic type of Node, and also some relations among *)
(* the various fields of semantic nodes that aren't implied by the *)
(* simple data type definitions. *)
(*************************************************************************)
/\ Node \in [NodeId -> SemNode]
/\ \A id \in NodeId :
LET n == Node[id]
IN /\ (n \in OpDefNode) =>
/\ Len(n.maxLevels) = n.numberOfArgs
/\ Len(n.weights) = n.numberOfArgs
/\ Len(n.params) = n.numberOfArgs
/\ Len(n.minMaxLevel) = n.numberOfArgs
/\ Len(n.opLevelCond) = n.numberOfArgs
/\ \A i \in 1..n.numberOfArgs :
/\ Len(n.minMaxLevel[i]) = Node[n.params[i]].numberOfArgs
/\ Len(n.opLevelCond[i]) = n.numberOfArgs
/\ \A j \in 1..n.numberOfArgs :
Len(n.opLevelCond[i][j]) =
Node[n.params[i]].numberOfArgs
/\ (n \in OpDeclNode) =>
/\ (n.kind = "ConstantDeclNode") => (n.level = 0)
/\ (n.kind = "VariableDeclNode") => /\ n.level = 1
/\ n.numberOfArgs = 0
/\ (n \in OpApplNode) =>
(Len(n.args) = Node[n.operator].numberOfArgs)
/\ (n \in SubstitutionNode) => (Len(n.subFor) = Len(n.subWith))
/\ (n \in InstanceNode) =>
/\ n.numberOfArgs = Len(n.params)
/\ (********************************************************)
(* There is a WITH substitution for every parameter of *)
(* the instantiated module. *)
(********************************************************)
LET mparamid ==
(**************************************************)
(* Defines the mparamid[i] to be the parameter *)
(* ids of the WITH clause. *)
(**************************************************)
[i \in 1..Len(Node[n.substitution].subFor) |->
Node[n.substitution].subFor[i]]
M == Node[n.module]
(*************************************************)
(* The ModuleNode of the instantiated module. *)
(*************************************************)
IN M.opDecls = {mparamid[i] : i \in 1..Len(mparamid)}
-----------------------------------------------------------------------------
(***************************************************************************)
(* Level Correctness Conditions *)
(* *)
(* Level checking is defined by the predicate LevelCorrect, which is a *)
(* correctness condition relating the level fields of a semantic node to *)
(* those of its children. From this condition, it's straightforward to *)
(* design a recursive procedure for computing those fields. The *)
(* conditions for each kind of node are defined separately, where the *)
(* predicate xxxNodeLevelCorrect(n) defines the correctness condition on a *)
(* node n of kind xxxNode. The following operators are used in the *)
(* definition of LevelCorrect. *)
(***************************************************************************)
IsOpArg(op, k) == Node[op.params[k]].numberOfArgs > 0
(*************************************************************************)
(* If op is an OpDefNode and k \in 1..op.numberOfArgs, then this is true *)
(* iff the k-th argument of op is an operator argument. *)
(*************************************************************************)
SubstituteInLevelConstraint(rcd, subst) ==
(*************************************************************************)
(* If rcd is a record containing level-constraint fields and subst is a *)
(* substitution, then this is the record consisting of the *)
(* level-constraint fields inferred from those of rcd by the *)
(* substitution. For example, if rcd is an ExprNode representing an *)
(* expression expr, then SubstituteInLevelConstraint(rcd, subst) is the *)
(* record of level constraints for the expression obtained from expr by *)
(* the substitution subst. *)
(*************************************************************************)
LET paramNums == 1..Len(subst.subFor)
(*******************************************************************)
(* The set of substitution parameter numbers. *)
(*******************************************************************)
ParamSubst(id) ==
(*******************************************************************)
(* The set of "substitute parameters" of the parameter whose *)
(* NodeId is id. If id is one of the parameters being substituted *)
(* for in subst, then this is the set of LevelParams of the *)
(* expression being substituted for it; otherwise, it equals {id}. *)
(*******************************************************************)
IF \E i \in paramNums : subst.subFor[i] = id
THEN LET subExpNum == CHOOSE i \in paramNums : subst.subFor[i] = id
IN Node[subst.subWith[subExpNum]].levelParams
ELSE {id}
IsOpParam(i) == Node[subst.subFor[i]].numberOfArgs > 0
(*******************************************************************)
(* True iff substitution parameter i is an operator parameter. *)
(*******************************************************************)
argNums == 1..Len(subst.subFor)
(*******************************************************************)
(* The set of parameter numbers. *)
(*******************************************************************)
SubOp(opid) ==
(*******************************************************************)
(* If opid is the NodeId of an operator parameter, then this *)
(* equals the NodeId of the operator with which this operator is *)
(* substituted for by subst, which is opid itself if subst does *)
(* not substitute for opid. *)
(*******************************************************************)
IF \E i \in paramNums : subst.subFor[i] = opid
THEN LET subExpNum ==
CHOOSE i \in paramNums : subst.subFor[i] = opid
IN Node[subst.subWith[subExpNum]].ref
ELSE opid
IN [levelParams |->
UNION {ParamSubst(id) : id \in rcd.levelParams},
levelConstraints |->
(******************************************************************)
(* There are two kinds of level constraints obtained after *)
(* substitution: ones that come from rcd.levelConstraints via *)
(* substitution, and ones that come from elements alp in *)
(* rcd.argLevelParams because alp.op is a substitution parameter *)
(* replaced by a defined operator defOp, and *)
(* defOp.maxLevels[alp.idx] implies level constraints on some *)
(* parameter in the expression substituted for alp.param. *)
(******************************************************************)
LET Sub(lc) ==
(************************************************************)
(* If lc is a level constraint on a parameter param, then *)
(* this is the set of level constraints that implies *)
(* because param might be substituted for. *)
(************************************************************)
{[lc EXCEPT !.param = par] :
par \in ParamSubst(lc.param)}
ALP(i) ==
(************************************************************)
(* The set of arg-level parameters alp such that alp.op is *)
(* the substitution parameter i. *)
(************************************************************)
{alp \in rcd.argLevelParams : alp.op = subst.subFor[i]}
SubInALP(alp) ==
(*************************************************************)
(* The set of arg-level parameters obtained from arg-level *)
(* parameter alp by replacing alp.param with each of its *)
(* substitute parameters. *)
(*************************************************************)
{[alp EXCEPT !.param = par] :
par \in ParamSubst(alp.param)}
SubALP(i) == UNION {SubInALP(alp) : alp \in ALP(i)}
(************************************************************)
(* The set of all SubInALP(alp) with alp in ALP(i). *)
(************************************************************)
LC(i, alp) ==
(************************************************************)
(* The level constraint implied by an element alp of *)
(* SubALP(i), if parameter i is an operator parameter *)
(* instantiated by a defined operator. *)
(************************************************************)
[param |-> alp.param,
level |->
Node[Node[subst.subWith[i]].ref].maxLevels[alp.idx]]
OpDefParams ==
{i \in paramNums : /\ IsOpParam(i)
/\ Node[subst.subWith[i]].ref \in
OpDefNodeId}
IN UNION {Sub(lc) : lc \in rcd.levelConstraints}
\cup
UNION { {LC(i, alp) : alp \in SubALP(i)} : i \in OpDefParams },
argLevelConstraints |->
(******************************************************************)
(* There are two kinds of arg-level constraints produced by the *)
(* substitution: ones obtained by substitution from *)
(* rcd.argLevelConstraints, and ones obtained as follows from an *)
(* element alp of rcd.argLevelParams. Since an operator *)
(* parameter can be instantiated only with an operator, *)
(* ParamSubst(alp.op) consists of a single operator op. If op is *)
(* a declared operator, and the subst substitutes expression exp *)
(* for alp.param, then op must be able to accept argument alp.idx *)
(* of level at least that of exp. (If there's no substitution *)