Beispiel #1
0
    /// <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;
    }