package newTypeSystem;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import ast.ExpCore;
import ast.Ast.Doc;
import ast.Ast.Mdf;
import ast.Ast.NormType;
import ast.Ast.Path;
import ast.Ast.SignalKind;
import ast.ExpCore.Block;
import ast.ExpCore.Block.Dec;
import ast.ExpCore.Block.On;
import coreVisitors.FreeVariables;
import programReduction.Norm;
import tools.Assertions;
import tools.Map;
public interface TsBlock extends TypeSystem{
default TOut tsBlock(TIn in, Block s) {
TOut res1=tsBlockBase(in,s);
if (res1.isOk()){return res1;}
if (!promotionMakesSense(in,res1.toError())){return res1;}//promotionMakesSense: mut that need capsule
TOut res2=tsBlockPromotion(in,s);//calls tsBlock internally,
//thanks to promotionMakesSense does not goes in loop
if (res2.isOk()){return res2;}
//if we are here, both res1,res2 not ok
return combine(res1.toError(),res2.toError());
}
default int splitDs(TIn in,List<Block.Dec> ds){
int i=0;
final int n=ds.size()-1;
if (ds.isEmpty()){return 0;}
List<String> xs=new ArrayList<>();
while(true){
xs.addAll(coreVisitors.FreeVariables.of(ds.get(i).getInner()));//xs U FV(ei)
if (xsNotInDomi(xs,ds,i+1)){return i;}
//cut will be from 0 to i included
if (i==n){return i;} //ds.size-1
i+=1;
}
}
default boolean xsNotInDomi(List<String> xs,List<Dec> ds,int ip1){
//ds=ds0..dsn;
//domi=dom(dsi+1..dsn)
//domi\xs=domi == no x in xs in dom(dsi+1..dsn)
if(ip1>=ds.size()){return true;}
String xDsip1=ds.get(ip1).getX();
if(xs.contains(xDsip1)){return false;}
return xsNotInDomi(xs,ds,ip1+1);
}
default TOut tsBlockBase(TIn in,Block s){
//Phase| p| G |- (ds ks e0 [_]) ~>(ds' ks' e'0 [T])
// : T <= T' | Tr'.capture(p,ks') U Tr U Tr0
List<Block.Dec> ds=s.getDecs();
List<Block.On> ks=s.getOns();
TIn in1=in.removeGDs(ds);//G'=G/dom(ds)
TIn in2=in1.gKs(ks);//G'[ks]
//Phase| p| G'[ks] |- ds ~> ds' |Tr' | G0
TOutDs _dsOut=dsType(in2,ds);
if(!_dsOut.isOk()){
TErr err=_dsOut.toError();
if(!err.kind.needContext){return err;}
//TODO: here we have the info to capture a failure about ds and discover if
//extant name (fwd[%]* x) was hidden by error safety or modifiable name (capsule/mut/lent x)
//was locked by error safety[cite the line number of the catch]
return err;
}
TOkDs dsOk=_dsOut.toOkDs();
//Phase| p| G'| Tr' |- ks~> ks' : Ts <= T' | Tr
TOutKs _ksOut=ksType(in1,dsOk.trAcc,ks);
if(!_ksOut.isOk()){return _ksOut.toError();}
TOkKs ksOk=_ksOut.toOkKs();
//Phase| p| G'[G0\dom(G')] |- e0~>e'0:T0 <=T' | Tr0
G G0LessG1=dsOk.g.removeGXs(in1.g.keySet());
TOut _e0Out=type(in1.addGG(G0LessG1).withE(s.getE(), in.expected));
if(!_e0Out.isOk()){return _e0Out.toError();}
TOk e0Ok=_e0Out.toOk();
//T= mostGeneralMdf({T0.mdf,Ts.mdfs}) T'.P //set of Mdfs admits no single most general mdf
Set<Mdf>mdfs=new HashSet<>();
for(NormType nt:ksOk.ts){
mdfs.add(nt.getMdf());
}
mdfs.add(e0Ok.computed.getMdf());
Mdf tMdf=TypeManipulation._mostGeneralMdf(mdfs);
if(tMdf==null){
return new TErr(in,"",e0Ok.computed,ErrorKind.NoMostGeneralMdf);
}
NormType t=new NormType(tMdf,in.expected.getPath(),Doc.empty());
assert null==TypeSystem.subtype(in.p,t, in.expected);
Block annotated=new Block(s.getDoc(),dsOk.ds,e0Ok.annotated,ksOk.ks,s.getP());
TOk res=new TOk(in,annotated,t);
// result Tr: Tr'.capture(p,ks') U Tr U Tr0
Tr trCaptured=dsOk.trAcc;
for(On k : ks){
trCaptured=trCaptured.trCapture(in.p,k);
}
Tr trUnion = trCaptured.trUnion(ksOk.trAcc).trUnion(e0Ok);
res=res.trUnion(trUnion);
return res;
}
default TOutDs dsType(TIn in,List<Dec> _ds){
//Phase| p| G |- empty ~> empty| empty;empty | G
if(_ds.isEmpty()){return new TOkDs(Tr.instance,_ds,G.instance.addGG(in));}
//Phase| p| G |- T0 x0=e0 ..Tn xn=en, ds ~>
// T'0 x0=e'0 ..T'n xn=e'n, ds'|Tr U Tr' | G2
int i=splitDs(in,_ds);
assert i+1<=_ds.size();
List<Dec> ds=_ds.subList(i+1,_ds.size());
List<Dec> ds0n=new ArrayList<>();
List<String> fve0n=new ArrayList<>();
for(Dec di:_ds.subList(0,i+1)){
ds0n.add(di.withT(Norm.resolve(in.p,di.getT())));
fve0n.addAll(FreeVariables.of(di.getInner()));
}
assert !fve0n.stream()
.anyMatch(x->ds.stream()
.anyMatch(d->d.getX().equals(x)));
//assert dom(ds) disjoint FV(e0..en)
// for i in 0..n T'i=resolve(p,Ti)
// G'=x0:T'0..xn:T'n //is just ds for the way I handle TIn
// G1= G[fwd(onlyMutOrImm(G'))] //capturing error for next line if not onlyMutOrImm(G') is used and is errored by next line
List<Dec> dsFiltered = ds0n.stream().filter(
d->{Mdf m=d.getT().getNT().getMdf(); return m==Mdf.Immutable||m==Mdf.Mutable;})
.map(d->d.withT(TypeManipulation.fwd(d.getT().getNT())))
.collect(Collectors.toList());
TIn in1=in.addGds(in.p,dsFiltered);
// for i in 0..n Phase| p| G1|-ei~>e'i: _ <= fwd% T'i | Tri
// Tr=Tr0 U .. U Trn
Tr trAcc=Tr.instance;
List<Dec>ds1=new ArrayList<>();
List<Dec>ds1FwdP=new ArrayList<>();
for(Dec di:ds0n){
NormType nt=di.getT().getNT();
NormType ntFwdP=TypeManipulation.fwdP(nt);
TOut _out=type(in1.withE(di.getInner(),ntFwdP));
if(!_out.isOk()){return _out.toError();}
TOk ok=_out.toOk();
trAcc=trAcc.trUnion(ok);
Dec di1=di.withInner(ok.annotated);
ds1.add(di1.withT(nt));
ds1FwdP.add(di1.withT(ntFwdP));
}
// if fwd_or_fwd%_in Tr.Ts
// then x0..xn disjoint FV(e0..en)//returning unresolved items from cycles is prohibited
if(TypeManipulation.fwd_or_fwdP_in(trAcc.returns)){
boolean xInCommon=fve0n.stream().anyMatch(x->ds0n.stream().anyMatch(d->d.getX().equals(x)));
if(xInCommon){return new TErr(in,"",null,ErrorKind.AttemptReturnFwd);}
}
// if fwd_or_fwd%_in { G(x) | x in FV(e0..en) } // x0..xn already excluded
// then G0=G[fwd%(G')]
// otherwise G0=G[G']//capturing error for next line, see if the difference between fwd%(G') ad G' would fix it. Still, then we need to check for the fwd x in FV(e0..en)..
List<NormType> _nts=new ArrayList<>();
for(String x: fve0n){
NormType t=in._g(x);
if(t!=null){_nts.add(t);}
}
TIn inG0;
if(TypeManipulation.fwd_or_fwdP_in(_nts)){inG0=in.addGds(in.p,ds1FwdP);}
else{inG0=in.addGds(in.p,ds1);}
// Phase| p| G0|- ds ~> ds'|Tr' | G2
TOutDs _res= dsType(inG0,ds);
if(!_res.isOk()){return _res.toError();}
TOkDs res=_res.toOkDs();
ds1.addAll(res.ds);//safe? locally created, not leaked yet.
if(res.trAcc!=null){trAcc=trAcc.trUnion(res.trAcc);}
return new TOkDs(trAcc,ds1,res.g);
}
default TOutKs ksType(TIn in,Tr trAcc,List<On> ks){
// D| Tr |-k1..kn ~> k'1..k'n:T1..Tn <= T | Tr1 U .. U Trn
// forall i in 1..n D| Tr.capture(D.p,k1..ki-1)|-ki ~> k'i:Ti <= T |Tri
assert trAcc!=null;
Tr tr=trAcc;
Tr newTrAcc=Tr.instance;
List<On>ks1=new ArrayList<>();
List<NormType>ts=new ArrayList<>();
for(On k:ks){
TOutK out=kType(in,tr,k);
if(!out.isOk()){return out.toError();}
TOkK ok=out.toOkK();
ks1.add(ok.k);
ts.add(ok.t);
newTrAcc=newTrAcc.trUnion(ok.tr);
//Removed, according to fix to type system in 5/4/2017
//tr=tr.trCapture(in.p,ok.k);
}
TOkKs res=new TOkKs(newTrAcc,ks,ts);
return res;
}
default TOutK kType(TIn in,Tr tr,On k){
if(TypeManipulation.catchRethrow(k)){return kTypeCatchAny(in,tr,k);}
return kTypeCatch(in,tr,k);
}
// T0 is the declared caught type, which contributes only a path
// T1 is the actual caught type, based on the types which can be thrown in context
// T2 is the type of the expression, based on x being bound T1
default TOutK kTypeCatch(TIn in,Tr tr1,On k){
if(k.getKind()==SignalKind.Return && tr1.returns.isEmpty()){
return new TErr(in,"No returns in scope",null,ErrorKind.NoMostGeneralMdf);
}
Mdf mdf1=TypeManipulation._mostGeneralMdf(k.getKind(),tr1);
if(mdf1==null){
return new TErr(in,"Contrasting mdf expected for return",null,ErrorKind.NoMostGeneralMdf);
}
NormType T1 = Norm.resolve(in.p, k.getT()).withMdf(mdf1);
TOut _out=type(in.addG(k.getX(),T1).withE(k.getE(), in.expected));
if(!_out.isOk()){return _out.toError();}
TOk out=_out.toOk();
TOkK res=new TOkK(Tr.instance.trUnion(out),k.withE(out.annotated).withT(T1),out.computed);
return res;
/* Phase| p| G| Tr' |- catch throw T0 x e ~> catch throw T1.P x e' :T2 <= T | Tr
mdf1 = mostGeneralMdf(throw,Tr') //set of Mdfs admits no single most general mdf, or mdfs is empty
//inconsistent set of thrown things, which do not share a most
//general modifier [list of line numbers of the throws]
T1 = mdf1 resolve (p, T0).P //resolve can fail
not catchRethrow(catch throw T1 x e)
Phase| p| G[x:T1]|- e ~> e' : T2 <= T | Tr
*/
}
default TOutK kTypeCatchAny(TIn in,Tr tr,On k){
Block e=(Block) k.getE();
ExpCore e0=e.getDecs().get(0).getInner();
TIn in1=in.removeG(k.getX());
TOut _out=type(in1.withE(e0,Path.Void().toImmNT()));
if(!_out.isOk()){return _out.toError();}
TOk ok=_out.toOk();
if(!ok.exceptions.isEmpty() ||!ok.returns.isEmpty()){return new TErr(in,"",null,ErrorKind.UnsafeCatchAny);}
return new TOkK(tr,k.withE(e.withDeci(0,e.getDecs().get(0).withInner(ok.annotated))),in.expected);
/*
(catch and rethrow any)// could be sugared as "on throw doAndPropagate e"
Phase |p |G |Tr|-catch throw Any x (e0 throw x) ~> catch throw Any x (e0' throw x): T<=T | Tr
where //Note: e0, e, e0',e' are using the sugar imm Void x=e == e
e0=(e catch error Any z void void)
e0'=(e' catch error Any z void void)
Phase |p |G\x |- e ~> e':_ <=imm Void | empty
catchRethrow(catch throw Any x(e0 throw x))
*/
}
//we are discussing if some blocks may not be promotable:
//for example blocks with empty ds and ks.
default TOut tsBlockPromotion(TIn in,Block s){
//Phase |p |G |- (ds ks e)~>(ds' ks' e'):capsule P <=capsule P | Tr
// Phase |p |toLent(G) |-(ds ks e)~>(ds' ks' e'):mut P <=mut P | Tr
Mdf eM=in.expected.getMdf();
assert eM==Mdf.Capsule || eM==Mdf.Immutable ||eM==Mdf.ImmutableFwd || eM==Mdf.ImmutablePFwd:
eM;
TIn in2=in.toLent();
TOut out=type(in2.withE(in.e,in.expected.withMdf(Mdf.Mutable)));
if(!out.isOk()){return out;}
TOk ok=out.toOk();
return new TOk(in,ok.annotated,ok.computed.withMdf(Mdf.Capsule));
//this rule is now "deterministic" in the sense that if typing the block give us a capsule directly,
//this rule can not be applied, since we require mut P <=mut P in the premise.
}
default TErr combine(TErr res,TErr promFail){
return new TErr(res.in,res.msg
+"\n(Block promotion attempted but\n"+promFail.in+"\n failed)",res._computed,res.kind);
}
default boolean promotionMakesSense(TIn in,TErr tErr){
NormType expected=in.expected;
NormType obtained=tErr._computed;
if(expected==null || obtained==null){return false;}
if (null!=TypeSystem.subtype(in.p,obtained.getPath(), expected.getPath())){return false;}
Mdf eM=expected.getMdf();
Mdf oM=obtained.getMdf();
boolean acceptableEM=eM==Mdf.Capsule || eM==Mdf.Immutable ||eM==Mdf.ImmutableFwd || eM==Mdf.ImmutablePFwd;
return acceptableEM && oM==Mdf.Mutable;
}
}