package newTypeSystem;
import ast.ExpCore.MCall;
import auxiliaryGrammar.Functions;
import tools.Assertions;
import tools.Map;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import ast.Ast.Doc;
import ast.Ast.Mdf;
import ast.Ast.MethodType;
import ast.Ast.NormType;
import ast.Ast.Path;
import ast.ExpCore;
import ast.Ast.Type;
public interface TsMCall extends TypeSystem{
default TOut innerMVPRetype(TOk ri,NormType ti){
if(Functions.isSubtype(ri.computed.getMdf(),ti.getMdf())){
assert ri.computed.getMdf()!=Mdf.Mutable;
return ri;
}
else{
assert ri.computed.getMdf() == Mdf.Mutable;
assert ti.getMdf() == Mdf.Capsule;
TIn ini=ri.in.withE(ri.in.e,ti);
TOut resi=type(ini);
return resi;
}
}
default TOut tsMCall(TIn in, MCall s) {
NormType _rec=GuessTypeCore.of(in, s.getInner());
Path rec=_rec.getPath();
if(rec.isPrimitive()){
return new TErr(in,"MethodCall on primitive type",null,ErrorKind.SelectorNotFound);
}
MethodType mDec=AlternativeMethodTypes._mtDeclared(in.p,rec,s.getS());
if (mDec==null){
return new TErr(in,"",null,ErrorKind.SelectorNotFound);
}
NormType ret=mDec.getReturnType().getNT();
ErrorKind kind = TypeSystem.subtype(in.p, ret.getPath(),in.expected.getPath());
if(kind!=null){return new TErr(in,"",ret,kind);}
List<MethodType> mTypes = AlternativeMethodTypes.types(mDec);
MethodType mType=AlternativeMethodTypes._firstMatchReturn(in.p,in.expected,mTypes);
if (mType==null){
return new TErr(in,"",ret,ErrorKind.NotSubtypeMdf);
}
//unachievable return type (T) for method (P.ms) [line numbers of expression and declaration]
//2 type all the parameters with mutOnlyToLent(Ts) //we may include mutOnlyToLent in the computation of the MTypes, instead of in the loop below
List<TOk> resp=new ArrayList<>();
List<Type> computed=new ArrayList<>();
List<ExpCore> annotated=new ArrayList<>();
ExpCore e0=s.getInner();
NormType t0=new NormType(mType.getMdf(),rec,Doc.empty());
TOut _res0=type(in.withE(e0,TypeManipulation.mutOnlyToLent(t0)));
if(!_res0.isOk()){return improveReceiverError(in,t0, _res0.toError());}
TOk res0=_res0.toOk();
Mdf recMdf=_res0.toOk().computed.getMdf();
{int i=-1;for( ExpCore ei:s.getEs()){i+=1;
NormType ti=mType.getTs().get(i).getNT();
TOut _resi=type(in.withE(ei,TypeManipulation.mutOnlyToLent(ti)));
if(!_resi.isOk()){return _resi.toError();}
resp.add(_resi.toOk());
computed.add(_resi.toOk().computed);
annotated.add(_resi.toOk().annotated);
}}
MethodType computedMt=new MethodType(false,recMdf,computed,in.expected,Collections.emptyList());
MethodType mTypeRev=AlternativeMethodTypes._bestMatchMtype(in.p,computedMt,mTypes);
if (mTypeRev!=null){
MCall resM=new MCall(res0.annotated,s.getS(),s.getDoc(),annotated,s.getP());
TOk res=new TOk(in,resM,mTypeRev.getReturnType().getNT());
// Trs[with r in resp (use[r.Tr])].collapse())
res=res.trUnion(res0);
for(TOk oki:resp){res=res.trUnion(oki);}
return res;
}
//3 if there is no matching method, we may need to retype some mut
//in capsule caused by mvp:
//it is not over if there is a mathing method type with mutToCapsule(result param types)
//tsToCaps=Ts[with r in resp (use[mutToCapsule(r.T)])]
//mTypeMVP=_bestMatchMtype(tsToCaps,TSIn.T,mTypes)
List<Type>tsToCaps=new ArrayList<>();
for(TOk r: resp){
Mdf m=r.computed.getMdf();
if(m==Mdf.MutableFwd || m==Mdf.MutablePFwd){
return new TErr(in,"impossible to search for mvp since mdf "+m,mTypes.get(0).getReturnType().getNT(),ErrorKind.NotSubtypeClass);
}
NormType nt=TypeManipulation.mutToCapsule(r.computed);
tsToCaps.add(nt);
}
computedMt=computedMt.withTs(tsToCaps).withMdf(TypeManipulation.mutToCapsule(computedMt.getMdf()));
MethodType mTypeMVP=AlternativeMethodTypes._bestMatchMtype(in.p, computedMt, mTypes);
if (mTypeMVP==null){
return new TErr(in,"mvp candidate notfound",mTypes.get(0).getReturnType().getNT(),ErrorKind.NotSubtypeClass);
}
//To be happy, we can retype the obtained mut parameters into expected capsule
TOut _newRes0=innerMVPRetype(res0,t0.withMdf(mTypeMVP.getMdf()));
if(!_newRes0.isOk()){return _newRes0;}
TOk newRes0=_newRes0.toOk();
List<TOk> newResp=new ArrayList<>();
{int i=-1;for(TOk ri :resp){i+=1;NormType ti=mTypeMVP.getTs().get(i).getNT();
TOut outi=innerMVPRetype(ri,ti);
if(!outi.isOk()){return outi.toError();}
newResp.add(outi.toOk());
}}
//return res=makeMCallOK(TSIn,respMVP,mTypeMVP)
MCall resM=new MCall(newRes0.annotated,s.getS(),s.getDoc(),
Map.of(r->r.annotated,newResp),s.getP());
TOk res=new TOk(in,resM,mTypeMVP.getReturnType().getNT());
// Trs[with r in resp (use[r.Tr])].collapse())
res=res.trUnion(newRes0);
for(TOk oki:newResp){res=res.trUnion(oki);}
return res;
}
default TErr improveReceiverError(TIn in,NormType t0, TErr err) {
if(err.kind!=ErrorKind.NotSubtypeMdf){return err;}
//if receiver must class, and is not, give better error
//if receiver must not class, but is class, give better error
if(t0.getMdf()==Mdf.Class){
return new TErr(in,"",err._computed,ErrorKind.ClassMethCalledOnNonClass);
}
if(err._computed!=null && err._computed.getMdf()==Mdf.Class){
return new TErr(in,"",err._computed,ErrorKind.NonClassMethCalledOnClass);
}
return new TErr(in,"",err._computed,ErrorKind.ReceiverInvalidMdfForMs);
}
}