module CodeGen open Ast open Getters open AstUtils open Utils open Resolver open TypeChecker open PrintUtils open DafnyPrinter open DafnyModelUtils open Options let validFuncName = "Valid()" let validSelfFuncName = "Valid_self()" let validReprFuncName = "Valid_repr()" /// requires: numUnrols >= 0 /// requires: |fldExprs| = |fldNames| let rec GetUnrolledFieldValidExpr fldExprs fldNames validFuncToUse numUnrolls = let rec __Combine exprLst strLst = match exprLst with | e :: rest -> let resLst1 = strLst |> List.map (fun s -> Dot(e, s)) List.concat [resLst1; __Combine rest strLst] | [] -> [] let rec __NotNull e = match e with | IdLiteral(_) | ObjLiteral(_) -> BinaryNeq e (ObjLiteral("null")) | Dot(sub, str) -> BinaryAnd (__NotNull sub) (BinaryNeq e (ObjLiteral("null"))) | _ -> failwith "not supposed to happen" (* --- function body starts here --- *) assert (numUnrolls >= 0) if numUnrolls = 0 then [TrueLiteral] else let exprList = fldExprs |> List.map (fun e -> BinaryImplies (__NotNull e) (Dot(e, validFuncToUse))) if numUnrolls = 1 then exprList else let fldExprs = __Combine fldExprs fldNames List.append exprList (GetUnrolledFieldValidExpr fldExprs fldNames validFuncToUse (numUnrolls - 1)) let GetFieldValidExpr flds validFunName numUnrolls = let fldExprs = flds |> List.map (fun var -> IdLiteral(GetExtVarName var)) let fldNames = flds |> List.map GetExtVarName let unrolledExprs = GetUnrolledFieldValidExpr fldExprs fldNames validFunName numUnrolls // add the recursive definition as well let recExprs = if not (validFunName = validFuncName) && Options.CONFIG.recursiveValid then flds //|> List.filter (fun var -> not ((GetExtVarName var).StartsWith("_back_"))) //don't use back pointers |> List.map (fun var -> let name = GetExtVarName var BinaryImplies (BinaryNeq (IdLiteral(name)) NullLiteral) (Dot(IdLiteral(name), validFuncName))) else [] recExprs @ unrolledExprs let GetFieldsForValidExpr allFields prog comp : VarDecl list = let frameVars = GetFrameFields comp allFields |> List.filter (fun var -> IsUserType prog (GetVarType var)) |> List.filter (fun var -> Utils.ListContains var frameVars) let GetFieldsValidExprList clsName allFields prog : Expr list = let fields = GetFieldsForValidExpr allFields prog (FindComponent prog clsName |> ExtractOption) let fieldsByType = GroupFieldsByType fields fieldsByType |> Map.fold (fun acc t varSet -> let validFunName, numUnrolls = match t with | Some(ty) when clsName = (GetTypeShortName ty) -> validSelfFuncName, Options.CONFIG.numLoopUnrolls | _ -> validFuncName, 1 acc |> List.append (GetFieldValidExpr (Set.toList varSet) validFunName numUnrolls) ) [] let PrintValidFunctionCode comp prog vars allInvs genRepr nameSuffix: string = let validFuncName = "Valid" + nameSuffix + "()" let validReprFuncName = "Valid_repr" + nameSuffix + "()" let validSelfFuncName = "Valid_self" + nameSuffix + "()" let idt = " " let __PrintInvs invs = invs |> List.fold (fun acc e -> List.concat [acc ; SplitIntoConjunts e]) [] |> PrintSep (" &&" + newline) (fun e -> sprintf "%s(%s)" idt (PrintExpr 0 e)) |> fun s -> if s = "" then (idt + "true") else s let clsName = GetClassName comp let compTypeName = GetClassType comp |> PrintType let hasLoop = vars |> List.exists (fun var -> match GetVarType var with Some(ty) when compTypeName = PrintType ty -> true | _ -> false) let fieldsValid = GetFieldsValidExprList clsName vars prog let frameFldNames = GetFrameFields comp |> List.map GetExtVarName let validReprBody = " this in Repr &&" + newline + " null !in Repr" + (PrintSep "" (fun x -> " &&" + newline + " ($x != null ==> $x in Repr && $x.Repr <= Repr && this !in $x.Repr)".Replace("$x", x)) frameFldNames) let vr = if genRepr then " function " + validReprFuncName + ": bool" + newline + " reads *;" + newline + " {" + newline + validReprBody + newline + " }" + newline + newline else "" let decreasesStr = if Options.CONFIG.recursiveValid then if hasLoop then if genRepr then " decreases Repr;" + newline else // TODO: Dafny currently doesn't accept "decreases *" on methods " decreases *;" + newline else "" else "" vr + " function " + validSelfFuncName + ": bool" + newline + " reads *;" + newline + " {" + newline + (if genRepr then " " + validReprFuncName + " &&" + newline else "") + (__PrintInvs allInvs) + newline + " }" + newline + newline + " function " + validFuncName + ": bool" + newline + " reads *;" + newline + decreasesStr + " {" + newline + " this." + validSelfFuncName + " &&" + newline + (__PrintInvs fieldsValid) + newline + " }" + newline let PrintDafnyCodeSkeleton prog methodPrinterFunc genRepr genOld = match prog with | Program(components) -> components |> List.fold (fun acc comp -> match comp with | Component(Interface(name,typeParams,members), DataModel(_,_,cVars,frame,inv), code) as comp -> let aVars = FilterFieldMembers members let aOldVars = MakeOldVars aVars let cOldVars = MakeOldVars cVars let allInvs = GetInvariantsAsList comp |> DesugarLst let allOldInvs = MakeOld (allInvs |> List.fold BinaryAnd TrueLiteral) |> SplitIntoConjunts let aVarsAndRepr = aVars |> List.append (Utils.Ite genRepr [Var("Repr", Some(SetType(NamedType("object", []))), false)] []) let compMethods = FilterConstructorMembers members // Now print it as a Dafny program acc + (sprintf "class %s%s {" name (PrintTypeParams typeParams)) + newline + // the fields: original abstract fields plus concrete fields (sprintf "%s" (PrintFields aVarsAndRepr 2 true)) + newline + (sprintf "%s" (PrintFields cVars 2 false)) + newline + (if genOld then (sprintf "%s" (PrintFields aOldVars 2 true)) + newline + (sprintf "%s" (PrintFields cOldVars 2 false)) + newline else "") + // generate the Valid function (sprintf "%s" (PrintValidFunctionCode comp prog (aVars @ cVars) allInvs genRepr "")) + newline + (if genOld then (sprintf "%s" (PrintValidFunctionCode comp prog (aOldVars @ cOldVars) allOldInvs genRepr "_old")) + newline else "") + // call the method printer function on all methods of this component (methodPrinterFunc comp) + // the end of the class "}" + newline + newline | _ -> assert false; "") "" let PrintPrePost pfix expr = SplitIntoConjunts expr |> PrintSep "" (fun e -> pfix + (PrintExpr 0 e) + ";") let GetPreconditionForMethod m = let validExpr = IdLiteral(validFuncName); if IsConstructor m then GetMethodPrePost m |> fst else BinaryAnd validExpr (GetMethodPrePost m |> fst) let GetPostconditionForMethod prog m genRepr = let validExpr = IdLiteral(validFuncName); match m with | Method(_,_,_,post,isConstr) -> // this.Valid() and user-defined post-condition let postExpr = BinaryAnd validExpr post // method out args are valid let postExpr = (GetMethodOutArgs m) |> List.fold (fun acc var -> if IsUserType prog (GetVarType var) then let varExpr = VarLiteral(GetExtVarName var) let argValidExpr = BinaryImplies (BinaryNeq varExpr NullLiteral) (Dot(varExpr, validFuncName)) BinaryAnd acc argValidExpr else acc ) postExpr // fresh Repr if genRepr then let freshExpr = if isConstr then "fresh(Repr - {this})" else "fresh(Repr - old(Repr))"; BinaryAnd (IdLiteral(freshExpr)) postExpr else postExpr | _ -> failwithf "expected a method, got %O" m let PrintAssumePostcondition prog m genRepr prefix = PrintPrePost prefix (GetPostconditionForMethod prog m genRepr |> Desugar) + newline let GetAllocObjects heapInst = heapInst.assignments |> List.fold (fun acc a -> match a with | FieldAssignment((obj,fld),_) when not (obj.name = "this") -> acc |> Set.add obj | FieldAssignment(_, ObjLiteral(name)) when not (name = "this" || name = "null") -> acc |> Set.add (heapInst.objs |> Map.find name) | _ -> acc ) Set.empty let PrintAllocNewObjects heapInst indent = let idt = Indent indent GetAllocObjects heapInst |> Set.fold (fun acc obj -> acc + (sprintf "%svar %s := new %s;%s" idt obj.name (PrintType obj.objType) newline)) "" let PrintVarAssignments heapInst indent = let idt = Indent indent let stmts = ConvertToStatements heapInst true let str = stmts |> PrintSep (newline) (fun s -> (PrintStmt s indent false)) str + newline /// let PrintReprAssignments prog heapInst indent = let __FollowsFunc o1 o2 = heapInst.assignments |> List.fold (fun acc assgn -> match assgn with | FieldAssignment ((srcObj,fld),value) -> acc || (srcObj = o1 && value = ObjLiteral(o2.name)) | _ -> false ) false let idt = Indent indent let objs = heapInst.assignments |> List.fold (fun acc assgn -> match assgn with | FieldAssignment((obj,var),_) -> if GetVarName var = "" then acc else acc |> Set.add obj | _ -> acc ) Set.empty |> Set.toList |> Utils.TopSort __FollowsFunc |> List.rev let rec __GetReprConcrete obj = let expr = SetExpr([ObjLiteral(obj.name)]) let builder = CascadingBuilder<_>(expr) builder { let typeName = GetTypeShortName obj.objType let! comp = FindComponent prog typeName let vars = GetFrameFields comp let nonNullVars = vars |> List.choose (fun v -> let lst = heapInst.assignments |> List.choose (function FieldAssignment(x,y) -> Some(x,y) | _ -> None) match Utils.ListMapTryFind (obj,v) lst with | Some(ObjLiteral(n)) when not (n = "null" || n = obj.name) -> Some(v,n) | _ -> None) return nonNullVars |> List.map (fun (var,objName) -> var,(Map.find objName heapInst.objs)) |> List.fold (fun acc (var,varValObj) -> if Options.CONFIG.genMod then BinaryAdd acc (Dot(Dot(ObjLiteral(obj.name), (GetVarName var)), "Repr")) else BinaryAdd acc (__GetReprConcrete varValObj) ) expr } let reprGetsList = objs |> List.fold (fun acc obj -> let objStmt = BinaryGets (Dot(ObjLiteral(obj.name), "Repr")) (__GetReprConcrete obj) objStmt :: acc // let expr = SetExpr([ObjLiteral(obj.name)]) // let builder = CascadingBuilder<_>(expr) // let fullRhs = builder { // let typeName = GetTypeShortName obj.objType // let! comp = FindComponent prog typeName // let vars = GetFrameFields comp // let nonNullVars = vars |> List.filter (fun v -> // let lst = heapInst.assignments |> List.choose (function FieldAssignment(x,y) -> Some(x,y) | _ -> None) // match Utils.ListMapTryFind (obj,v) lst with // | Some(ObjLiteral(n)) when not (n = "null") -> true // | _ -> false) // return nonNullVars |> List.fold (fun a v -> // BinaryAdd a (Dot(Dot(ObjLiteral(obj.name), (GetVarName v)), "Repr")) // ) expr // } // let fullReprExpr = BinaryGets (Dot(ObjLiteral(obj.name), "Repr")) fullRhs // fullReprExpr :: acc ) [] let reprStr = if not (reprGetsList = []) then idt + "// repr stuff" + newline + (PrintStmtList reprGetsList indent true) else "" let reprValidExpr = GetAllocObjects heapInst |> Set.fold (fun acc obj -> BinaryAnd acc (Dot(ObjLiteral(obj.name), validFuncName))) TrueLiteral let assertValidStr = if not (reprValidExpr = TrueLiteral) then idt + "// assert repr objects are valid (helps verification)" + newline + (PrintStmt (ExprStmt(AssertExpr(reprValidExpr))) indent true) else "" let outStr = reprStr + assertValidStr if outStr = "" then outStr else newline + outStr let rec PrintHeapCreationCodeOld prog (comp,meth) sol indent genRepr = let rec __RewriteOldStmt stmt = match stmt with | Assign(l, r) -> Assign(l, BringToPost r) | ExprStmt(e) -> ExprStmt(BringToPost e) | Block(slist) -> Block(slist |> List.map __RewriteOldStmt) let __RewriteOldAsgn a = match a with | FieldAssignment((o,f),e) -> FieldAssignment((o,f), BringToPost e) | ArbitraryStatement(stmt) -> ArbitraryStatement(__RewriteOldStmt stmt) /// inserts an assignments into a list of assignments such that the list remains /// topologically sorted wrt field dependencies between different assignments let rec __InsertSorted asgsLst asg = let ___DependsOn dependentAsg asg = match asg, dependentAsg with | FieldAssignment((o,f),_), FieldAssignment(_,e) -> let mf = fun e acc -> match e with | IdLiteral(name) when name = GetVarName f && o.name = "this" -> true | Dot(discr, name) -> let t1 = InferType prog comp (fun s -> None) discr let t2 = FindComponentForType prog o.objType acc || (name = GetVarName f && t1 = t2) | _ -> acc DescendExpr2 (mf ) e false | _ -> false match asgsLst with | [] -> [asg] | a :: rest -> if ___DependsOn a asg then asg :: a :: rest else a :: __InsertSorted rest asg /// - removes all FieldAssignments to unmodifiable objects and old variables /// - rewrites expressions not to use old fields let __RemoveUnmodifiableStuff heapInst = let newAsgs = heapInst.assignments |> List.fold (fun acc a -> match a with | FieldAssignment((obj,_),_) when not (Set.contains obj heapInst.modifiableObjs) -> acc | FieldAssignment((_,var),_) when IsOldVar var -> acc | _ -> __InsertSorted acc (__RewriteOldAsgn a) ) [] { heapInst with assignments = newAsgs } let idt = Indent indent match sol with | (c, hi) :: rest -> let heapInstMod = __RemoveUnmodifiableStuff hi let __ReprAssignments ind = if genRepr then (PrintReprAssignments prog heapInstMod ind) else "" if c = TrueLiteral then (PrintAllocNewObjects heapInstMod indent) + (PrintVarAssignments heapInstMod indent) + (__ReprAssignments indent) + (PrintHeapCreationCodeOld prog (comp,meth) rest indent genRepr) else if List.length rest > 0 then idt + "if (" + (PrintExpr 0 c) + ") {" + newline + (PrintAllocNewObjects heapInstMod (indent+2)) + (PrintVarAssignments heapInstMod (indent+2)) + (__ReprAssignments (indent+2)) + idt + "} else {" + newline + (PrintHeapCreationCodeOld prog (comp,meth) rest (indent+2) genRepr) + idt + "}" + newline else (PrintAllocNewObjects heapInstMod indent) + (PrintVarAssignments heapInstMod indent) + (__ReprAssignments indent) | [] -> "" let PrintHeapCreationCode prog (comp,meth) sol indent genRepr = let idt = Indent indent let ghostPre = GetMethodGhostPrecondition meth if ghostPre = TrueLiteral then PrintHeapCreationCodeOld prog (comp,meth) sol indent genRepr else (ghostPre |> SplitIntoConjunts |> PrintSep newline (fun e -> idt + "assume " + (PrintExpr 0 e) + ";")) + newline + (PrintHeapCreationCodeOld prog (comp,meth) sol indent genRepr) let GenConstructorCode prog comp mthd decreasesClause body genRepr = let validExpr = IdLiteral(validFuncName); match mthd with | Method(methodName,sign,_,_,isConstr) -> let preExpr = GetPreconditionForMethod mthd |> Desugar let postExpr = GetPostconditionForMethod prog mthd genRepr |> Desugar let thisObj = ThisObj comp " method " + methodName + (PrintSig sign) + (if IsModifiableObj thisObj (comp,mthd) then newline + " modifies this;" else "") + (PrintPrePost (newline + " requires ") preExpr) + (PrintPrePost (newline + " ensures ") postExpr) + newline + decreasesClause + " {" + newline + body + " }" + newline | _ -> "" let GetDecreasesClause (c,m) sol = if IsRecursiveSol (c,m) sol then " decreases Repr;" + newline else "" // solutions: (comp, constructor) |--> condition * heapInst let PrintImplCode prog solutions genRepr = PrintDafnyCodeSkeleton prog (fun comp -> let cname = GetComponentName comp solutions |> Map.fold (fun acc (c,m) sol -> if (GetComponentName c) = cname then let mthdBody,decr = match sol with | [] -> let body = " //unable to synthesize" + (PrintAssumePostcondition prog m genRepr (newline + " assume ")) let decr = "" body,decr | _ -> let body = PrintHeapCreationCode prog (c,m) sol 4 genRepr let decr = GetDecreasesClause (c,m) sol body,decr acc + newline + (GenConstructorCode prog comp m decr mthdBody genRepr) + newline else acc) "") genRepr