Repository navigation
Expand file tree
/
Copy pathsubstitute.d
More file actions
1821 lines (1789 loc) · 69.3 KB
/
Copy pathsubstitute.d
File metadata and controls
1821 lines (1789 loc) · 69.3 KB
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 ast.substitute;
import ast.expression, ast.type, ast.declaration, ast.scope_, ast.lastuse;
import astopt;
import std.meta: AliasSeq;
import util: MapX, SetX, MapSX;
private alias genericLhsTypes=AliasSeq!(
IteExp,AssertExp,LiteralExp,LambdaExp,PlaceholderExp,ForgetExp,SliceExp,VectorExp,
UPlusExp,UMinusExp,UNotExp,UBitNotExp,
AddExp,SubExp,NSubExp,MulExp,DivExp,IDivExp,ModExp,PowExp,
BitOrExp,BitXorExp,BitAndExp,AndThenExp,OrElseExp,
OrExp,XorExp,AndExp,LtExp,LeExp,GtExp,GeExp,EqExp,NeqExp,
VectorForExp,ClassicalTy,ProductTy,ArrayTy,TupleTy,VectorTy,
VariadicTy,TypeTy,QNumericTy,BottomTy,NumericTy,StringTy
);
Expression transitionToType(Expression e,Scope target){
assert(e.isSemCompleted());
CompoundExp[] regions;
void collect(Expression x){
if(auto ce=cast(CompoundExp)x)
if(ce.blscope_) regions~=ce;
if(cast(FunctionDef)x||cast(LambdaExp)x||cast(VectorForExp)x) return;
if(auto le=cast(LetExp)x)
if(le.s.blscope_) regions~=le.s;
foreach(c;x.components) collect(c);
}
collect(e);
if(!regions.length) return e;
TypeTransition tt;
tt.target=target;
foreach(ce;regions) tt.codScopes[ce.blscope_]=[];
e.freeVarsImpl((id){ tt.taken[id.id]=[]; return 0; });
foreach(ce;regions) foreach(stmt;ce.s) collectBoundNamesImpl(stmt,tt.taken);
for(auto sc=target;sc;sc=sc.parentScope())
foreach(id,_;sc.rnsymtab) tt.taken[id]=[];
MapSX!(Id,Expression) subst;
foreach(ce;regions){
void attr(Expression stmt){
if(auto de=cast(DefineExp)stmt){
defineLhsBoundVarsImpl(de.e1,(id){
if(id.meaning) tt.declRegion[id.meaning]=ce.blscope_;
return 0;
});
return;
}
if(auto fd=cast(FunctionDef)stmt){ tt.declRegion[fd]=ce.blscope_; return; }
if(auto ce2=cast(CommaExp)stmt){ attr(ce2.e1); attr(ce2.e2); return; }
if(auto ce2=cast(CompoundExp)stmt){
if(!ce2.blscope_) foreach(x;ce2.s) attr(x);
return;
}
}
foreach(stmt;ce.s) attr(stmt);
}
auto r=e.substitute(subst,&tt);
SetX!Declaration twins;
foreach(orig,twin;tt.declMap){
twins[twin]=[];
Scope rs=null;
if(orig.scope_ in tt.codScopes) rs=orig.scope_;
if(auto p=tt.declRegion.getPtr(orig)) rs=*p;
if(rs){
auto ts=tt.mapScope(rs);
twin.scope_=ts;
if(auto prev=ts.peekSymtab(twin.getId,true)){ // (e.g., the twins of a variable and of its redefinition)
if(!cast(DeadDecl)prev){
if(rs.peekSymtab(orig.getId,true) !is orig) continue;
ts.symtabRemove(prev);
}
}
ts.symtabInsert(twin);
if(auto fdt=cast(FunctionDef)twin)
if(fdt.fscope_){ if(!ts.origin) ts.origin=rs; fdt.fscope_.parent=ts; }
}
}
SetX!Scope tsSet;
foreach(_,ts;tt.scopes) tsSet[ts]=[];
void fixUses(Expression x,Scope cur){
if(cast(FunctionDef)x||cast(LambdaExp)x) return;
if(auto le=cast(LetExp)x){
if(le.s.blscope_&&le.s.blscope_ in tsSet) cur=le.s.blscope_;
fixUses(le.s,cur);
fixUses(le.e,cur);
return;
}
if(auto ce=cast(CompoundExp)x){
if(ce.blscope_&&ce.blscope_ in tsSet) cur=ce.blscope_;
foreach(stmt;ce.s) fixUses(stmt,cur);
return;
}
if(auto id=cast(Identifier)x){
if(cur&&(id.scope_&&id.scope_ in tt.codScopes||id.meaning&&id.meaning in twins))
id.scope_=cur;
return;
}
foreach(c;x.components) fixUses(c,cur);
}
fixUses(r,null);
return r;
}
Expression ttTransitionLet(LetExp le,MapSX!(Id,Expression) subst,TypeTransition* tt){
MapSX!(Id,Expression) active;
foreach(k,v;subst) if(le.freeVarsImpl((id)=>id.id==k?1:0)) active[k]=v;
if(le.isSemCompleted()&&le.s.blscope_) return ttRescopeLet(le,active,tt);
MapSX!(Id,Id) forced;
foreach(stmt;le.s.s)
statementBoundVarsImpl(stmt,(id){
forced[id.id]=tt.freshName(id.id);
return 0;
});
auto ctx=BlockSubst(active,forced,&tt.taken,&tt.declMap,false,tt);
auto ns=substituteBlockCompound(le.s,ctx);
auto ne=substituteLValue(le.e,ctx);
auto r=new LetExp(ns,ne);
r.loc=le.loc;
if(le.isSemError()) r.sstate=SemState.error;
else if(le.isSemCompleted()){
r.type=le.type?le.type.substitute(subst,tt):null;
if(r.type&&r.type.isSemEvaluated()) r.sstate=SemState.completed;
}
return r;
}
// substitution into an analyzed `let` expression: the result has its own scopes and declarations (see rescopeImpl)
Expression rescopeLet(LetExp le,MapSX!(Id,Expression) active){
SetX!Id taken;
foreach(k,v;active) taken[k]=[];
le.freeVarsImpl((id){ taken[id.id]=[]; return 0; });
foreach(stmt;le.s.s) collectBoundNamesImpl(stmt,taken);
bool wouldCapture(Id b){
foreach(k,v;active) if(v.hasFreeVar(b)) return true;
return false;
}
MapSX!(Id,Id) fresh;
Id rename(Declaration d){
auto b=d.getId;
if(!wouldCapture(b)) return b;
if(auto p=fresh.getPtr(b)) return *p;
auto nb=b;
do nb=nb.apos; while(nb in taken||wouldCapture(nb));
taken[nb]=[];
return fresh[b]=nb;
}
auto bs=le.s.blscope_;
BlockScope root;
if(auto ts=cast(TypeScope)bs){
auto nts=new TypeScope(ts.origin);
nts.parent=ts.parent;
root=nts;
}else{
root=new BlockScope(null,bs.restriction_,bs.restrictionCause_); // (the copy is not nested in the original's parent)
root.parent=bs.parent;
}
root.isLoopBody=bs.isLoopBody;
MapSX!(Declaration,Declaration) declMap;
auto ctx=BlockSubst(active,MapSX!(Id,Id).init,&taken,&declMap,false);
LetRescope let={tpl:le,root:root,rename:&rename};
rescopeImpl(null,null,&let,ctx);
foreach(k,v;active){ // (e.g., a relabeling of the parameters of a function type does not substitute values)
if(!v.isSemCompleted()) return let.result;
foreach(id;v.freeIdentifiers) if(!id.meaning) return let.result;
}
return reanalyzeLet(let.result,cast(TypeScope)bs?(cast(TypeScope)bs).origin:bs);
}
// the substituted values may have more precise types than the variables they replace: the most precise types
// are determined by analyzing the result again, as for any copy that is analyzed later (e.g., an inferred generic argument)
private Expression reanalyzeLet(LetExp le,Scope origin){
if(!le||!le.isSemCompleted()||!origin) return le;
for(Scope s=origin;s;){ // (e.g., a function is called within its own return type: do not analyze a copy within the analysis of another copy)
if(auto ts=cast(TypeScope)s){
if(ts.reanalyzed&&ts.reanalyzed.loc==le.loc) return le;
s=ts.origin?ts.origin:ts.parent;
}else s=s.parentScope();
}
import ast.semantic_: expressionSemantic, expSemContext, ConstResult, InType;
import std.range: only;
auto sc=new TypeScope(origin); // (free variables are found by name, e.g., such that local functions capture them)
sc.reanalyzed=le;
SetX!Declaration free;
foreach(id;le.freeIdentifiers){
if(!id.meaning) return le;
free[id.meaning]=[];
foreach(name;only(id.id,id.meaning.getId,id.meaning.name?id.meaning.name.id:id.id)){
if(auto p=sc.freeDecls.getPtr(name)){
if(*p !is id.meaning) return le; // (e.g., different variables of the same name)
}else sc.freeDecls[name]=id.meaning;
}
}
bool shadowed=false; // (by a declaration within the `let` expression)
void check(Declaration d){ if(d&&d !in free&&d.name&&(d.getId in sc.freeDecls||d.name.id in sc.freeDecls)) shadowed=true; }
void visit(Expression e){
if(!e||shadowed) return;
if(auto id=cast(Identifier)e) return check(id.meaning);
if(auto fd=cast(FunctionDef)e){
check(fd);
foreach(p;fd.params) check(p);
return visit(fd.body_);
}
if(auto lam=cast(LambdaExp)e) return visit(lam.fd);
if(auto ve=cast(VectorForExp)e){
if(auto r=ve.fe.aggr.isRange){ visit(r.left); visit(r.step); visit(r.right); }
else if(auto c=ve.fe.aggr.isContainer) visit(c.e);
return ve.fd?visit(ve.fd):visit(ve.fe.bdy);
}
foreach(c;e.components) visit(c);
}
visit(le);
if(shadowed) return le;
auto cpy=le.copy();
auto handler=sc.handler;
handler.suppress++;
scope(exit) handler.suppress--;
auto r=expressionSemantic(cpy,expSemContext(sc,ConstResult.yes,InType.yes));
if(!r.isSemCompleted()||r.isSemError()) return le;
return r;
}
private Expression ttRescopeLet(LetExp le,MapSX!(Id,Expression) active,TypeTransition* tt){
auto fn=le.s.blscope_.getFunction();
MapSX!(Id,Id) fresh;
Id rename(Declaration d){
if(d.scope_&&d.scope_.getFunction() !is fn) return d.getId; // (e.g., a local variable of a nested function)
if(auto p=fresh.getPtr(d.getId)) return *p;
return fresh[d.getId]=tt.freshName(d.getId);
}
MapSX!(Declaration,Declaration) declMap;
auto ctx=BlockSubst(active,MapSX!(Id,Id).init,&tt.taken,&declMap,false,tt);
auto root=tt.mapScope(le.s.blscope_);
if(!root.origin) root.origin=le.s.blscope_; // (e.g., no call site is known)
LetRescope let={tpl:le,root:root,rename:&rename};
rescopeImpl(null,null,&let,ctx);
return let.result;
}
Expression ttTransitionIte(IteExp ite,MapSX!(Id,Expression) subst,TypeTransition* tt){
MapSX!(Id,Expression) active;
foreach(k,v;subst) if(ite.freeVarsImpl((id)=>id.id==k?1:0)) active[k]=v;
MapSX!(Id,Id) forced;
foreach(branch;[ite.then,ite.othw])
if(branch) foreach(stmt;branch.s)
statementBoundVarsImpl(stmt,(id){
forced[id.id]=tt.freshName(id.id);
return 0;
});
auto ctx=BlockSubst(active,forced,&tt.taken,&tt.declMap,false,tt);
auto ncond=substituteLValue(ite.cond,ctx);
auto tctx=ctx.nested();
auto nthen=substituteBlockCompound(ite.then,tctx);
CompoundExp nothw=null;
if(ite.othw){
auto fctx=ctx.nested();
nothw=substituteBlockCompound(ite.othw,fctx);
}
auto r=new IteExp(ncond,nthen,nothw);
r.loc=ite.loc;
if(ite.isSemError()) r.sstate=SemState.error;
else if(ite.isSemCompleted()){
r.type=ite.type?ite.type.substitute(subst,tt):null;
if(r.type&&r.type.isSemEvaluated()) r.sstate=SemState.completed;
}
return r;
}
int defineLhsBoundVarsImpl(Expression lhs,scope int delegate(Identifier) dg){
return dispatchExp!(dlBoundVars,dlBoundVarsDefault)(lhs,dg);
}
private int dlBoundVars(Identifier id,scope int delegate(Identifier) dg){
return dg(id);
}
private int dlBoundVars(TypeAnnotationExp tae,scope int delegate(Identifier) dg){
return defineLhsBoundVarsImpl(tae.e,dg);
}
private int dlBoundVars(TupleExp tpl,scope int delegate(Identifier) dg){
foreach(x;tpl.e) if(auto r=defineLhsBoundVarsImpl(x,dg)) return r;
return 0;
}
private int dlBoundVars(CatExp ce,scope int delegate(Identifier) dg){
if(auto r=defineLhsBoundVarsImpl(ce.e1,dg)) return r;
return defineLhsBoundVarsImpl(ce.e2,dg);
}
private int dlBoundVars(CallExp ce,scope int delegate(Identifier) dg){
auto ft=cast(ProductTy)ce.e.type;
auto tpl=cast(TupleExp)ce.arg;
if(ft&&ft.isTuple&&tpl&&ft.nargs==tpl.length){
foreach(i,x;tpl.e){
if(ft.isConstForReverse[i]) continue;
if(auto r=defineLhsBoundVarsImpl(x,dg)) return r;
}
return 0;
}
if(ft&&!ft.isTuple&&ft.nargs==1&&ft.isConstForReverse[0]) return 0;
return defineLhsBoundVarsImpl(ce.arg,dg);
}
private int dlBoundVars(IndexExp ie,scope int delegate(Identifier) dg){
return defineLhsBoundVarsImpl(ie.e,dg);
}
private int dlBoundVars(FieldExp fe,scope int delegate(Identifier) dg){
return defineLhsBoundVarsImpl(fe.e,dg);
}
private int dlBoundVars(LetExp le,scope int delegate(Identifier) dg){
return le.freeVarsImpl((id)=>id.constLookup||id.implicitDup?0:dg(id));
}
private int dlBoundVars(T)(T lhs,scope int delegate(Identifier) dg) if(isOneOf!(T,genericLhsTypes)){
return 0; // no names are bound in these positions
}
private int dlBoundVarsDefault(Expression lhs,scope int delegate(Identifier) dg){
return 0;
}
int defineLhsFreeVarsImpl(Expression lhs,scope int delegate(Identifier) dg){
return dispatchExp!(dlFreeVars,dlFreeVarsDefault)(lhs,dg);
}
private int dlFreeVars(Identifier id,scope int delegate(Identifier) dg){
return 0;
}
private int dlFreeVars(TypeAnnotationExp tae,scope int delegate(Identifier) dg){
if(auto r=defineLhsFreeVarsImpl(tae.e,dg)) return r;
if(tae.t) return tae.t.freeVarsImpl(dg);
return 0;
}
private int dlFreeVars(TupleExp tpl,scope int delegate(Identifier) dg){
foreach(x;tpl.e) if(auto r=defineLhsFreeVarsImpl(x,dg)) return r;
return 0;
}
private int dlFreeVars(CatExp ce,scope int delegate(Identifier) dg){
if(auto r=defineLhsFreeVarsImpl(ce.e1,dg)) return r;
return defineLhsFreeVarsImpl(ce.e2,dg);
}
private int dlFreeVars(CallExp ce,scope int delegate(Identifier) dg){
if(auto r=ce.e.freeVarsImpl(dg)) return r;
auto ft=cast(ProductTy)ce.e.type;
auto tpl=cast(TupleExp)ce.arg;
if(ft&&ft.isTuple&&tpl&&ft.nargs==tpl.length){
foreach(i,x;tpl.e){
if(ft.isConstForReverse[i]){ if(auto r=x.freeVarsImpl(dg)) return r; }
else if(auto r=defineLhsFreeVarsImpl(x,dg)) return r;
}
return 0;
}
if(ft&&!ft.isTuple&&ft.nargs==1&&ft.isConstForReverse[0]) return ce.arg.freeVarsImpl(dg);
return defineLhsFreeVarsImpl(ce.arg,dg);
}
private int dlFreeVars(IndexExp ie,scope int delegate(Identifier) dg){
return ie.freeVarsImpl(dg);
}
private int dlFreeVars(FieldExp fe,scope int delegate(Identifier) dg){
return fe.freeVarsImpl(dg);
}
private int dlFreeVars(LetExp le,scope int delegate(Identifier) dg){
return le.freeVarsImpl((id)=>id.constLookup||id.implicitDup?dg(id):0);
}
private int dlFreeVars(T)(T lhs,scope int delegate(Identifier) dg) if(isOneOf!(T,genericLhsTypes)){
return lhs.freeVarsImpl(dg);
}
private int dlFreeVarsDefault(Expression lhs,scope int delegate(Identifier) dg){
return lhs.freeVarsImpl(dg);
}
int statementBoundVarsImpl(Expression stmt,scope int delegate(Identifier) dg){
return dispatchStm!(stmtBoundVars,stmtBoundVarsDefault)(stmt,dg);
}
private int stmtBoundVars(FunctionDef fd,scope int delegate(Identifier) dg){
auto name=fd.rename?fd.rename:fd.name;
return name?dg(name):0;
}
private int stmtBoundVars(CommaExp ce,scope int delegate(Identifier) dg){
if(auto r=statementBoundVarsImpl(ce.e1,dg)) return r;
return statementBoundVarsImpl(ce.e2,dg);
}
private int stmtBoundVars(IteExp ite,scope int delegate(Identifier) dg){
foreach(x;ite.then.s) if(auto r=statementBoundVarsImpl(x,dg)) return r;
if(ite.othw) foreach(x;ite.othw.s) if(auto r=statementBoundVarsImpl(x,dg)) return r;
return 0;
}
private int stmtBoundVars(CompoundExp ce,scope int delegate(Identifier) dg){
if(!ce.blscope_)
foreach(x;ce.s) if(auto r=statementBoundVarsImpl(x,dg)) return r;
return 0;
}
private int stmtBoundVars(DefineExp de,scope int delegate(Identifier) dg){
return defineLhsBoundVarsImpl(de.e1,dg);
}
private int stmtBoundVars(T)(T stmt,scope int delegate(Identifier) dg)
if(is(T==ForExp)||is(T==WhileExp)||is(T==RepeatExp)||is(T==ReturnExp)||is(T==ForgetExp)||is(T==CallExp)||is(T==TypeAnnotationExp)||is(T==AssertExp)||is(T==ObserveExp)||is(T==CObserveExp)||is(T:AAssignExp))
{
return 0; // these statement kinds bind no names in the enclosing block
}
static if(language==silq)
private int stmtBoundVars(WithExp we,scope int delegate(Identifier) dg){
return 0; // names bound by a `with` block are local to the construct
}
static if(language==silq)
private int stmtBoundVars(DoWhileExp dw,scope int delegate(Identifier) dg){
foreach(x;dw.bdy.s) if(auto r=statementBoundVarsImpl(x,dg)) return r; // (visible after the loop)
return 0;
}
static if(language==psi)
private int stmtBoundVars(DatDecl dd,scope int delegate(Identifier) dg){
auto name=dd.rename?dd.rename:dd.name;
return name?dg(name):0;
}
private int stmtBoundVarsDefault(Expression stmt,scope int delegate(Identifier) dg){
return 0;
}
int statementFreeVarsImpl(Expression stmt,scope int delegate(Identifier) dg){
return dispatchStm!(stmtFreeVars,stmtFreeVarsDefault)(stmt,dg);
}
private int stmtFreeVars(FunctionDef fd,scope int delegate(Identifier) dg){
return functionDefFreeVarsImpl(fd,dg);
}
private int stmtFreeVars(CommaExp ce,scope int delegate(Identifier) dg){
if(auto r=statementFreeVarsImpl(ce.e1,dg)) return r;
return statementFreeVarsImpl(ce.e2,dg);
}
private int stmtFreeVars(CompoundExp ce,scope int delegate(Identifier) dg){
return blockFreeVarsImpl(ce.s,null,dg);
}
private int stmtFreeVars(IteExp ite,scope int delegate(Identifier) dg){
if(auto r=ite.cond.freeVarsImpl(dg)) return r;
if(auto r=blockFreeVarsImpl(ite.then.s,null,dg)) return r;
if(ite.othw) if(auto r=blockFreeVarsImpl(ite.othw.s,null,dg)) return r;
return 0;
}
private int stmtFreeVars(ForExp fe,scope int delegate(Identifier) dg){
if(auto rng=fe.aggr.isRange()){
if(auto r=rng.left.freeVarsImpl(dg)) return r;
if(rng.step) if(auto r=rng.step.freeVarsImpl(dg)) return r;
if(auto r=rng.right.freeVarsImpl(dg)) return r;
}else if(auto cont=fe.aggr.isContainer()){
if(auto r=cont.e.freeVarsImpl(dg)) return r;
}
SetX!Id bound;
if(fe.var) bound[fe.var.id]=[];
if(fe.pattern) defineLhsBoundVarsImpl(fe.pattern,(id){ bound[id.id]=[]; return 0; });
if(fe.pattern) if(auto r=defineLhsFreeVarsImpl(fe.pattern,dg)) return r;
return blockFreeVarsImpl(fe.bdy.s,null,(id)=>id.id in bound?0:dg(id));
}
private int stmtFreeVars(WhileExp we,scope int delegate(Identifier) dg){
if(auto r=we.cond.freeVarsImpl(dg)) return r;
return blockFreeVarsImpl(we.bdy.s,null,dg);
}
private int stmtFreeVars(RepeatExp re,scope int delegate(Identifier) dg){
if(auto r=re.num.freeVarsImpl(dg)) return r;
return blockFreeVarsImpl(re.bdy.s,null,dg);
}
static if(language==silq)
private int stmtFreeVars(DoWhileExp dw,scope int delegate(Identifier) dg){
return blockFreeVarsImpl(dw.bdy.s~dw.cond~(dw.reset?dw.reset.s:[]),null,dg);
}
private int stmtFreeVars(ReturnExp re,scope int delegate(Identifier) dg){
return re.e?re.e.freeVarsImpl(dg):0;
}
private int stmtFreeVars(ForgetExp fe,scope int delegate(Identifier) dg){
if(auto r=fe.var.freeVarsImpl(dg)) return r;
return fe.val?fe.val.freeVarsImpl(dg):0;
}
private int stmtFreeVars(T)(T stmt,scope int delegate(Identifier) dg)
if(is(T==CallExp)||is(T==TypeAnnotationExp)||is(T==AssertExp)||is(T==ObserveExp)||is(T==CObserveExp)||is(T:AAssignExp))
{
return stmt.freeVarsImpl(dg);
}
static if(language==silq)
private int stmtFreeVars(WithExp we,scope int delegate(Identifier) dg){
return blockFreeVarsImpl(withStatements(we),null,dg);
}
private int stmtFreeVars(DefineExp de,scope int delegate(Identifier) dg){
if(auto r=de.e2.freeVarsImpl(dg)) return r;
return defineLhsFreeVarsImpl(de.e1,dg);
}
static if(language==psi)
private int stmtFreeVars(DatDecl dd,scope int delegate(Identifier) dg){
foreach(decl;dd.capturedDecls) // (captures are only known after semantic analysis)
foreach(id;dd.captures[decl])
if(auto r=dg(id)) return r;
return 0;
}
private int stmtFreeVarsDefault(Expression stmt,scope int delegate(Identifier) dg){
return stmt.freeVarsImpl(dg);
}
int blockFreeVarsImpl(Expression[] stmts,Expression trailing,scope int delegate(Identifier) dg){
SetX!Id bound;
int filtered(Identifier id){ return id.id in bound?0:dg(id); }
foreach(stmt;stmts){
if(auto r=statementFreeVarsImpl(stmt,&filtered)) return r;
statementBoundVarsImpl(stmt,(id){ bound[id.id]=[]; return 0; });
}
if(trailing) return trailing.freeVarsImpl(&filtered);
return 0;
}
void updateLambdaDefinition(LambdaExp le){ // (copies of a lambda are made from its original definition: after a change of its definition, e.g., a renaming, they are made from the changed one, also for the lambdas within it, which a copy of it copies from their original definitions in turn)
if(le.fd.body_) updateLambdaDefinitions(le.fd.body_);
le.orig=le.fd.copy();
}
void updateLambdaDefinitions(Expression e){ // (of the lambdas within `e`, also within lambdas and local functions, see `updateLambdaDefinition`)
foreach(x;e.subexpressions){
if(auto le=cast(LambdaExp)x) updateLambdaDefinition(le);
else if(auto nested=cast(FunctionDef)x) if(nested.body_) updateLambdaDefinitions(nested.body_);
}
}
int functionDefFreeVarsImpl(FunctionDef fd,scope int delegate(Identifier) dg){
SetX!Id bound;
if(fd.name){ bound[fd.getId]=[]; bound[fd.name.id]=[]; }
int filtered(Identifier id){ return id.id in bound?0:dg(id); }
foreach(p;fd.params){
if(auto pt=p.vtype?p.vtype:p.dtype) if(auto r=pt.freeVarsImpl(&filtered)) return r;
if(p.name){ bound[p.getId]=[]; bound[p.name.id]=[]; }
}
if(auto ret=fd.ret?fd.ret:fd.rret) if(auto r=ret.freeVarsImpl(&filtered)) return r;
if(fd.body_) if(auto r=blockFreeVarsImpl(fd.body_.s,null,&filtered)) return r;
return 0;
}
void collectBoundNamesImpl(Expression stmt,ref SetX!Id names){
statementBoundVarsImpl(stmt,(id){ names[id.id]=[]; return 0; });
dispatchStm!(collectBoundNames,collectBoundNamesDefault)(stmt,names);
}
private void collectBoundNames(FunctionDef fd,ref SetX!Id names){
collectFunctionBoundNames(fd,names);
}
private void collectBoundNames(CommaExp ce,ref SetX!Id names){
collectBoundNamesImpl(ce.e1,names);
collectBoundNamesImpl(ce.e2,names);
}
private void collectBoundNames(CompoundExp ce,ref SetX!Id names){
foreach(x;ce.s) collectBoundNamesImpl(x,names);
}
private void collectBoundNames(IteExp ite,ref SetX!Id names){
foreach(x;ite.then.s) collectBoundNamesImpl(x,names);
if(ite.othw) foreach(x;ite.othw.s) collectBoundNamesImpl(x,names);
}
private void collectBoundNames(ForExp fe,ref SetX!Id names){
if(fe.var) names[fe.var.id]=[];
if(fe.pattern) defineLhsBoundVarsImpl(fe.pattern,(id){ names[id.id]=[]; return 0; });
foreach(x;fe.bdy.s) collectBoundNamesImpl(x,names);
}
private void collectBoundNames(WhileExp we,ref SetX!Id names){
foreach(x;we.bdy.s) collectBoundNamesImpl(x,names);
}
private void collectBoundNames(RepeatExp re,ref SetX!Id names){
foreach(x;re.bdy.s) collectBoundNamesImpl(x,names);
}
static if(language==silq)
private void collectBoundNames(DoWhileExp dw,ref SetX!Id names){
foreach(x;dw.bdy.s) collectBoundNamesImpl(x,names);
if(dw.reset) foreach(x;dw.reset.s) collectBoundNamesImpl(x,names);
}
private void collectBoundNames(DefineExp de,ref SetX!Id names){
if(auto le=cast(LambdaExp)de.e2) collectFunctionBoundNames(le.fd,names);
}
private void collectBoundNames(T)(T stmt,ref SetX!Id names)
if(is(T==ReturnExp)||is(T==ForgetExp)||is(T==CallExp)||is(T==TypeAnnotationExp)||is(T==AssertExp)||is(T==ObserveExp)||is(T==CObserveExp)||is(T:AAssignExp))
{
}
static if(language==silq)
private void collectBoundNames(WithExp we,ref SetX!Id names){
foreach(x;withStatements(we)) collectBoundNamesImpl(x,names);
}
static if(language==silq)
private Expression[] withStatements(WithExp we){ // (locals of the transformation are visible in the body and in the inverse transformation)
return we.trans.s~we.bdy.s~(we.itrans?we.itrans.s:[]);
}
static if(language==psi)
private void collectBoundNames(DatDecl dd,ref SetX!Id names){
}
private void collectBoundNamesDefault(Expression stmt,ref SetX!Id names){
if(auto le=cast(LetExp)stmt) foreach(x;le.s.s) collectBoundNamesImpl(x,names);
}
void collectFunctionBoundNames(FunctionDef fd,ref SetX!Id names){
if(!fd) return;
if(fd.name){ names[fd.getId]=[]; names[fd.name.id]=[]; }
foreach(p;fd.params) if(p.name){ names[p.getId]=[]; names[p.name.id]=[]; }
if(fd.body_) foreach(x;fd.body_.s) collectBoundNamesImpl(x,names);
}
struct BlockSubst{
MapSX!(Id,Expression) subst;
MapSX!(Id,Id) forced;
SetX!Id* taken;
MapSX!(Declaration,Declaration)* declMap;
bool changed=false;
TypeTransition* tt=null;
MapSX!(Id,Id)* renames=null; // (variables renamed by the twin of a function, see rescopeTwin)
bool wouldCapture(Id b){
foreach(k,v;subst) if(v.hasFreeVar(b)) return true;
return false;
}
Id freshName(Id base){
auto nn=base?base:Id.intern("x");
do nn=nn.apos; while(nn in *taken||wouldCapture(nn));
(*taken)[nn]=[];
return nn;
}
BlockSubst nested(){
auto r=BlockSubst(subst.dup,forced.dup,taken,declMap,false,tt);
r.renames=renames;
return r;
}
Identifier bindVar(Identifier id,Expression vtype=null){
auto b=id.id;
Id nb=b;
if(auto f=forced.getPtr(b)) nb=*f;
else if(wouldCapture(b)) nb=freshName(b);
if(nb==b){
if(b in subst) subst.remove(b);
return id;
}
if(renames&&!cast(FunctionDef)id.meaning){ // (the twin gives all versions of the variable the new name, as their uses keep their meanings)
if(auto p=renames.getPtr(b)) nb=*p;
else (*renames)[b]=nb;
if(b in subst) subst.remove(b);
changed=true;
return id;
}
auto nid=remakeIdentifier(id,nb);
auto use=remakeIdentifier(id,nb);
use.constLookup=true;
use.byRef=false;
use.implicitDup=false;
if(!use.type) use.type=vtype;
if(!use.isSemCompleted()&&!use.isSemError()&&use.type&&use.type.isSemEvaluated()) use.sstate=SemState.completed;
if(auto vd=cast(VarDecl)id.meaning){
auto dvtype=vd.vtype&&vd.vtype.isSemCompleted()&&subst.length?vd.vtype.substitute(subst,tt):vd.vtype;
auto twin=getVarDeclTwin(vd,nid,dvtype);
nid.meaning=twin;
use.meaning=twin;
if(!use.scope_) use.scope_=twin.scope_;
if(declMap) (*declMap)[vd]=twin;
}
subst[b]=use;
changed=true;
return nid;
}
}
private VarDecl getVarDeclTwin(VarDecl orig,Identifier nname,Expression nvtype){
auto twin=new VarDecl(nname);
twin.copyAnalyzedFieldsFrom(orig);
twin.vtype=nvtype;
twin.dtype=orig.dtype;
twin.scope_=orig.scope_;
return twin;
}
private Declaration[] remapDecls(Declaration[] decls,MapSX!(Declaration,Declaration)* declMap,ref bool remapped){
if(!declMap||!(*declMap).length||!decls.length) return decls;
bool c=false;
auto r=decls.dup;
foreach(ref d;r) if(auto p=(*declMap).getPtr(d)){ d=*p; c=true; }
if(!c) return decls;
remapped=true;
return r;
}
private Identifier remakeIdentifier(Identifier id,Id nid){
Expression.CopyArgs cargs={preserveSemantic:true};
auto r=id.copy(cargs);
r.id=nid;
r.loc=id.loc;
return r;
}
private Expression useSubstitute(Expression e,ref BlockSubst ctx){
if(!e) return null;
if(!e.isSemCompleted()) return e;
if(!ctx.subst.length) return e;
auto ne=e.substitute(ctx.subst,ctx.tt);
if(ne !is e) ctx.changed=true;
return ne;
}
private T finishStatement(T)(T r,Expression orig,ref BlockSubst ctx){
r.loc=orig.loc;
ctx.changed=true;
if(orig.isSemError()){ r.sstate=SemState.error; return r; }
if(orig.isSemCompleted()){
if(!r.type) r.type=orig.type&&ctx.subst.length?orig.type.substitute(ctx.subst,ctx.tt):(orig.type?orig.type:unit);
if(r.type&&r.type.isSemEvaluated()) r.sstate=SemState.completed;
}
return r;
}
Expression substituteLValue(Expression lhs,ref BlockSubst ctx){
if(!lhs) return null;
return dispatchExp!(substLhs,substLhsDefault)(lhs,ctx);
}
private Expression substLhs(Identifier id,ref BlockSubst ctx){
if(id.constLookup||id.implicitDup) return useSubstitute(id,ctx);
if(auto p=ctx.subst.getPtr(id.id)){
Expression.CopyArgs cargs={preserveSemantic:true};
auto nv=(*p).copy(cargs);
nv.setConstLookup(id.constLookup);
nv.byRef=id.byRef;
ctx.changed=true;
if(id.classical) if(auto c=nv.eval().getClassical()) return c; // (e.g., the classical version of a type in the result of a `let` expression)
return nv;
}
return id;
}
private Expression substLhs(TypeAnnotationExp tae,ref BlockSubst ctx){
auto ne=substituteLValue(tae.e,ctx);
auto nt=useSubstitute(tae.t,ctx);
if(ne is tae.e&&nt is tae.t) return tae;
auto r=new TypeAnnotationExp(ne,nt,tae.annotationType);
return finishStatement(r,tae,ctx);
}
private Expression substLhs(TupleExp tpl,ref BlockSubst ctx){
auto ne=tpl.e.dup;
bool chg=false;
foreach(ref x;ne){
auto nx=substituteLValue(x,ctx);
if(nx !is x) chg=true;
x=nx;
}
if(!chg) return tpl;
auto r=new TupleExp(ne);
return finishStatement(r,tpl,ctx);
}
private Expression substLhs(IndexExp ie,ref BlockSubst ctx){
auto nagg=substituteLValue(ie.e,ctx);
auto na=useSubstitute(ie.a,ctx);
if(nagg is ie.e&&na is ie.a) return ie;
auto r=new IndexExp(nagg,na);
r.isArraySyntax=ie.isArraySyntax;
static if(language==silq) r.isClassical_=ie.isClassical_;
return finishStatement(r,ie,ctx);
}
private Expression substLhs(FieldExp fe,ref BlockSubst ctx){
auto nagg=substituteLValue(fe.e,ctx);
if(nagg is fe.e) return fe;
auto r=new FieldExp(nagg,fe.f);
return finishStatement(r,fe,ctx);
}
private Expression substLhs(CatExp ce,ref BlockSubst ctx){
auto ne1=substituteLValue(ce.e1,ctx);
auto ne2=substituteLValue(ce.e2,ctx);
if(ne1 is ce.e1&&ne2 is ce.e2) return ce;
auto r=new CatExp(ne1,ne2);
return finishStatement(r,ce,ctx);
}
private Expression substLhs(CallExp ce,ref BlockSubst ctx){
auto ne=useSubstitute(ce.e,ctx);
auto narg=substituteLValue(ce.arg,ctx);
if(ne is ce.e&&narg is ce.arg) return ce;
auto r=new CallExp(ne,narg,ce.isSquare,ce.isClassical_);
return finishStatement(r,ce,ctx);
}
private Expression substLhs(LetExp le,ref BlockSubst ctx){
if(ctx.tt){ // (e.g., the value of a branch of an `if` expression in a type: as for the type itself, the declarations get twins in the scopes of the type)
auto r=ttTransitionLet(le,ctx.subst,ctx.tt);
if(r !is le) ctx.changed=true;
return r;
}
auto nctx=ctx.nested();
foreach(stmt;le.s.s) collectBoundNamesImpl(stmt,*nctx.taken);
auto ns=substituteBlockCompound(le.s,nctx);
auto ne=substituteLValue(le.e,nctx);
if(nctx.changed) ctx.changed=true;
if(ns is le.s&&ne is le.e) return le;
auto r=new LetExp(ns,ne);
return finishStatement(r,le,ctx);
}
private Expression substLhs(T)(T e,ref BlockSubst ctx) if(isOneOf!(T,genericLhsTypes)){
return useSubstitute(e,ctx);
}
private Expression substLhsDefault(Expression e,ref BlockSubst ctx){
return useSubstitute(e,ctx);
}
private Expression substituteDefineLhs(Expression lhs,ref BlockSubst ctx){
if(!lhs) return null;
return dispatchExp!(substDefineLhs,substDefineLhsDefault)(lhs,ctx);
}
private Expression substDefineLhs(Identifier id,ref BlockSubst ctx){
auto nid=ctx.bindVar(id,id.type);
return nid;
}
private Expression substDefineLhs(TypeAnnotationExp tae,ref BlockSubst ctx){
auto nt=useSubstitute(tae.t,ctx);
auto ne=substituteDefineLhs(tae.e,ctx);
if(ne is tae.e&&nt is tae.t) return tae;
auto r=new TypeAnnotationExp(ne,nt,tae.annotationType);
return finishStatement(r,tae,ctx);
}
private Expression substDefineLhs(TupleExp tpl,ref BlockSubst ctx){
auto ne=tpl.e.dup;
bool chg=false;
foreach(ref x;ne){
auto nx=substituteDefineLhs(x,ctx);
if(nx !is x) chg=true;
x=nx;
}
if(!chg) return tpl;
auto r=new TupleExp(ne);
return finishStatement(r,tpl,ctx);
}
private Expression substDefineLhs(CatExp ce,ref BlockSubst ctx){
auto ne1=substituteDefineLhs(ce.e1,ctx);
auto ne2=substituteDefineLhs(ce.e2,ctx);
if(ne1 is ce.e1&&ne2 is ce.e2) return ce;
auto r=new CatExp(ne1,ne2);
return finishStatement(r,ce,ctx);
}
private Expression substDefineLhs(CallExp ce,ref BlockSubst ctx){
auto ne=useSubstitute(ce.e,ctx);
auto ft=cast(ProductTy)ce.e.type;
auto tpl=cast(TupleExp)ce.arg;
Expression narg;
if(ft&&ft.isTuple&&tpl&&ft.nargs==tpl.length){
auto nes=tpl.e.dup;
bool chg=false;
foreach(i,ref x;nes){
auto nx=ft.isConstForReverse[i]?substituteLValue(x,ctx):substituteDefineLhs(x,ctx);
if(nx !is x) chg=true;
x=nx;
}
if(!chg) narg=ce.arg;
else{
auto ntpl=new TupleExp(nes);
narg=finishStatement(ntpl,ce.arg,ctx);
}
}else if(ft&&!ft.isTuple&&ft.nargs==1&&ft.isConstForReverse[0]) narg=substituteLValue(ce.arg,ctx);
else narg=substituteDefineLhs(ce.arg,ctx);
if(ne is ce.e&&narg is ce.arg) return ce;
auto r=new CallExp(ne,narg,ce.isSquare,ce.isClassical_);
return finishStatement(r,ce,ctx);
}
private Expression substDefineLhs(IndexExp ie,ref BlockSubst ctx){
auto nagg=substituteLValue(ie.e,ctx);
auto na=useSubstitute(ie.a,ctx);
defineLhsBoundVarsImpl(ie.e,(id){ ctx.subst.remove(id.id); return 0; });
if(nagg is ie.e&&na is ie.a) return ie;
auto r=new IndexExp(nagg,na);
r.isArraySyntax=ie.isArraySyntax;
static if(language==silq) r.isClassical_=ie.isClassical_;
return finishStatement(r,ie,ctx);
}
private Expression substDefineLhs(FieldExp fe,ref BlockSubst ctx){
auto nagg=substituteLValue(fe.e,ctx);
defineLhsBoundVarsImpl(fe.e,(id){ ctx.subst.remove(id.id); return 0; });
if(nagg is fe.e) return fe;
auto r=new FieldExp(nagg,fe.f);
return finishStatement(r,fe,ctx);
}
private Expression substDefineLhs(LetExp le,ref BlockSubst ctx){
defineLhsBoundVarsImpl(le,(id){ ctx.subst.remove(id.id); return 0; });
return substituteLValue(le,ctx);
}
private Expression substDefineLhs(T)(T e,ref BlockSubst ctx) if(isOneOf!(T,genericLhsTypes)){
return substituteLValue(e,ctx);
}
private Expression substDefineLhsDefault(Expression e,ref BlockSubst ctx){
return substituteLValue(e,ctx);
}
private Expression substituteStatement(Expression stmt,ref BlockSubst ctx){
return dispatchStm!(substStm,substStmDefault)(stmt,ctx);
}
private Expression substStm(DefineExp de,ref BlockSubst ctx){
auto ne2=substituteLValue(de.e2,ctx);
auto ne1=substituteDefineLhs(de.e1,ctx);
if(ne1 is de.e1&&ne2 is de.e2) return de;
auto r=new DefineExp(ne1,ne2);
return finishStatement(r,de,ctx);
}
private Expression substStm(AAssignExp ae,ref BlockSubst ctx){
auto ne1=substituteLValue(ae.e1,ctx);
auto ne2=useSubstitute(ae.e2,ctx);
if(ne1 is ae.e1&&ne2 is ae.e2) return ae;
auto r=cast(AAssignExp)(cast(Expression)ae).copy();
assert(!!r);
r.e1=ne1;
r.e2=ne2;
r.type=null;
r.sstate=SemState.initial;
auto base=ae.e1;
for(;;){
if(auto idx=cast(IndexExp)base) base=idx.e;
else if(auto fe=cast(FieldExp)base) base=fe.e;
else break;
}
if(auto id=cast(Identifier)base) if(id.id !in ctx.subst){ // (the updated variable keeps its versions, e.g., in a twin of a function in a type)
r.replacements=ae.replacements;
import std.algorithm: endsWith;
import ast.parser, ast.lexer;
static foreach(op;binaryOps){
static if(op.endsWith("←")&&op!="←"){
if(auto se=cast(BinaryExp!(Tok!op))ae)
if(se.operation) (cast(BinaryExp!(Tok!op))r).operation=useSubstitute(se.operation,ctx);
}
}
}
return finishStatement(r,ae,ctx);
}
static if(language==psi)
private Expression substStm(DatDecl dd,ref BlockSubst ctx){
return dd; // TODO: substitute into data type declarations?
}
private Expression substStmDefault(Expression stmt,ref BlockSubst ctx){
// expression-statements are substituted as lvalues
return substituteLValue(stmt,ctx);
}
private Expression substStm(FunctionDef fd,ref BlockSubst ctx){
auto nfd=substituteFunctionDefImpl(fd,ctx,true);
if(nfd !is fd){
if(ctx.declMap) (*ctx.declMap)[fd]=nfd;
auto fname=fd.rename?fd.rename:fd.name;
if(fname) if(auto p=ctx.subst.getPtr(fname.id))
if(auto uid=cast(Identifier)(*p)){
if(uid.meaning is fd||uid.meaning is null) uid.meaning=nfd;
if(uid.meaning is nfd&&nfd.ftype&&nfd.ftype.isSemEvaluated()) uid.type=nfd.ftype;
}
}
return nfd;
}
private Expression substStm(CommaExp ce,ref BlockSubst ctx){
auto ne1=substituteStatement(ce.e1,ctx);
auto ne2=substituteStatement(ce.e2,ctx);
if(ne1 is ce.e1&&ne2 is ce.e2) return ce;
auto r=new CommaExp(ne1,ne2);
return finishStatement(r,ce,ctx);
}
private Expression substStm(IteExp ite,ref BlockSubst ctx){
auto ncond=useSubstitute(ite.cond,ctx);
int decide(Identifier id){
auto b=id.id;
if(b in ctx.forced) return 0;
if(ctx.wouldCapture(b)) ctx.forced[b]=ctx.freshName(b);
return 0;
}
foreach(x;ite.then.s) statementBoundVarsImpl(x,&decide);
if(ite.othw) foreach(x;ite.othw.s) statementBoundVarsImpl(x,&decide);
auto tctx=ctx.nested();
auto nthen=substituteBlockCompound(ite.then,tctx);
auto octx=ctx.nested();
CompoundExp nothw=null;
if(ite.othw) nothw=substituteBlockCompound(ite.othw,octx);
void applyEscaping(Expression[] ss,ref BlockSubst src){
foreach(x;ss) statementBoundVarsImpl(x,(id){
auto b=id.id;
if(b in ctx.forced){
if(auto v=src.subst.getPtr(b)) ctx.subst[b]=*v;
else ctx.subst.remove(b);
}else ctx.subst.remove(b);
return 0;
});
}
applyEscaping(ite.then.s,tctx);
if(ite.othw) applyEscaping(ite.othw.s,octx);
if(tctx.changed||octx.changed) ctx.changed=true;
if(ncond is ite.cond&&nthen is ite.then&¬hw is ite.othw) return ite;
auto r=new IteExp(ncond,nthen,nothw);
return finishStatement(r,ite,ctx);
}
private Expression substStm(CompoundExp ce,ref BlockSubst ctx){
if(!ce.blscope_) return substituteBlockCompound(ce,ctx);
auto nctx=ctx.nested();
auto r=substituteBlockCompound(ce,nctx);
if(nctx.changed) ctx.changed=true;
return r;
}
private Expression substStm(ForExp fe,ref BlockSubst ctx){
ForAggregate naggr=fe.aggr;
bool aggrChanged=false;
if(auto rng=fe.aggr.isRange()){
auto nleft=useSubstitute(rng.left,ctx);
auto nstep=rng.step?useSubstitute(rng.step,ctx):null;
auto nright=useSubstitute(rng.right,ctx);
if(nleft !is rng.left||nstep !is rng.step||nright !is rng.right){
naggr=ForAggregate(ForRange(rng.leftExclusive,nleft,nstep,rng.rightExclusive,nright));
aggrChanged=true;
}
}else if(auto cont=fe.aggr.isContainer()){
auto nce=useSubstitute(cont.e,ctx);
if(nce !is cont.e){
naggr=ForAggregate(ForContainer(nce));
aggrChanged=true;
}
}
auto bctx=ctx.nested();
Identifier nvar=fe.var;
if(fe.var) nvar=bctx.bindVar(fe.var,fe.var.type?fe.var.type:fe.loopVar?fe.loopVar.vtype:null); // (the binding occurrence may have no type)
Expression npattern=fe.pattern?substituteDefineLhs(fe.pattern,bctx):null;
auto nbdy=substituteBlockCompound(fe.bdy,bctx);
if(bctx.changed) ctx.changed=true;
if(!aggrChanged&&nvar is fe.var&&npattern is fe.pattern&&nbdy is fe.bdy) return fe;
auto r=new ForExp(nvar,npattern,naggr,nbdy);
r.fescope_=fe.fescope_;
r.loopVar=fe.loopVar;
if(fe.loopVar&&bctx.declMap)
if(auto p=(*bctx.declMap).getPtr(cast(Declaration)fe.loopVar))
if(auto nvd=cast(VarDecl)*p) r.loopVar=nvd;
return finishStatement(r,fe,ctx);