/// <param name="e">the expression for which we are trying to find possible witnesses of consistency</param> /// <param name="modelfield">the modelfield for which we are trying to find a witness, or null if we are looking for result in a method postcondition</param> /// <param name="witnessType">the type of the witness we are looking for</param> protected static WUCs GetWUCs(Expression e, Member modelfield, TypeNode witnessType) { WUCs result = new WUCs(); BinaryExpression be = e as BinaryExpression; TernaryExpression te = e as TernaryExpression; NodeType nt = e.NodeType; if (nt == NodeType.Eq || nt == NodeType.Iff || nt == NodeType.Ge || nt == NodeType.Gt || nt == NodeType.Le || nt == NodeType.Lt || nt == NodeType.Ne) { #region don't want to go into either operand, but check if either operand is a witness candidate. Expression inferredWitness = Checker.GetWitnessHelper(e, modelfield); //theModelField == null means we are looking for result in method postcondition if (inferredWitness != null) { WitnessUnderConstruction wuc = new WitnessUnderConstruction(inferredWitness, null, 0); if (nt == NodeType.Eq || nt == NodeType.Iff) result.Exact.Add(wuc); //found result <==> P. Then P is the only potential witness. else { //When we find one of the numeric operators, we can assume that witnessType is numeric, //as otherwise the type checker will have produced an error. if (nt == NodeType.Ge) result.Lowerbounds.Add(wuc); else if (nt == NodeType.Gt) { wuc.Witness = new BinaryExpression(inferredWitness, new Literal(1, witnessType), NodeType.Add, witnessType, inferredWitness.SourceContext); //witness = witness + 1 result.Lowerbounds.Add(wuc); } else if (nt == NodeType.Le) result.Upperbounds.Add(wuc); else if (nt == NodeType.Lt) { wuc.Witness = new BinaryExpression(inferredWitness, new Literal(1, witnessType), NodeType.Sub, witnessType, inferredWitness.SourceContext); //witness = witness - 1 result.Upperbounds.Add(wuc); } else if (nt == NodeType.Ne) result.Neqs.Add(wuc); } } #endregion } else if (nt == NodeType.LogicalAnd || nt == NodeType.LogicalOr || nt == NodeType.And || nt == NodeType.Or) { #region go into both operands and combine results based on nt WUCs lhsWUCs = GetWUCs(be.Operand1, modelfield, witnessType); WUCs rhsWUCs = GetWUCs(be.Operand2, modelfield, witnessType); if (nt == NodeType.And || nt == NodeType.LogicalAnd) { //Todo: Optimization - if lhsWUCs only contains exact witnesses, we can ignore whatever witnesses came from rhsWUCs (but deal with rhsNeq's). //we can also take max of all lowerbounds and discard others //Reconsider these optimizations: do they work for !(P)? //Increase the nr of duplications in non-exact lhsWUCs elements by rhsWUCs.Neqs.Length (and vice versa). lhsWUCs.Duplicate(rhsWUCs.Neqs.Count); rhsWUCs.Duplicate(lhsWUCs.Neqs.Count); //It might be that the right-hand side witnesses are total only given the validity of the be.Operand1. //Therefore, we want to guard these witnesses by be.Operand1. However, if be.Operand1 contains the modelfield/result value, //that trick is not going to work: it needs to be evaluated with the witness as its value. //i.e., if the witness is only total when be.operand1 holds, then it is not a good witness when be.operand1 requires its evaluation. if (nt == NodeType.LogicalAnd && lhsWUCs.IsEmpty) rhsWUCs.Guard(be.Operand1); } else if (nt == NodeType.LogicalOr) { //No need to increase duplicates for || (or |). But we can guard the rhs by !(lhs) if lhs doesn't give witnesses. if (lhsWUCs.IsEmpty) rhsWUCs.Guard(new UnaryExpression(be.Operand1, NodeType.Neg, SystemTypes.Boolean)); } result.Add(lhsWUCs); result.Add(rhsWUCs); #endregion } else if (nt == NodeType.Implies) { #region Treat A ==> B as !A || B Expression notA = new UnaryExpression(be.Operand1, NodeType.Neg, SystemTypes.Boolean); Expression notAorB = new BinaryExpression(notA, be.Operand2, NodeType.Or, SystemTypes.Boolean); result = GetWUCs(notAorB, modelfield, witnessType); #endregion } else if (te != null) { #region Treat P ? A : B as P ==> A && !P ==> B //Need to look into P for witnesses to: for instance, consider the consistent contract ensures result == 5 ? true : false; //But also want to pick up witnesses from !P: for instance, consider the consistent contract ensures !(result == 5 ? false : true); //In the second case it is essential to have 5 in result.neq so that ! can turn it into an exact witness. Expression PimplA = new BinaryExpression(te.Operand1, te.Operand2, NodeType.Implies); Expression notP = new UnaryExpression(te.Operand1, NodeType.Neg, SystemTypes.Boolean); Expression notPimplB = new BinaryExpression(notP, te.Operand3, NodeType.Implies); Expression and = new BinaryExpression(PimplA, notPimplB, NodeType.And); return GetWUCs(and, modelfield, witnessType); #endregion } else if (nt == NodeType.Neg) { #region Treat !(A) by flipping all the results we get from A WUCs wucs = GetWUCs((e as UnaryExpression).Operand, modelfield, witnessType); result.Exact = wucs.Neqs; result.Upperbounds = wucs.Lowerbounds; //Example: !(result > 4). Then we get a lowerbound of 5 in wucs. We need to turn that into an upperbound of 4. AddOrSub(result.Upperbounds, 1, NodeType.Add); result.Lowerbounds = wucs.Upperbounds; AddOrSub(result.Lowerbounds, 1, NodeType.Sub); result.Neqs = wucs.Exact; #endregion } return result; }