Example #1
0
      public Method methodToInferPostFrom = null;  //if set, Visit tries to infer a postcondition for method. 

      public override StatementList VisitStatementList(StatementList statements) {
        if (statements == null) return null;
        foreach (Statement stat in statements) {
          Return r = stat as Return;
          if (r != null && r.Expression != null) { //Statement return E; found. Test admissibility of E as a witness.
            WitnessFromCodeAdmissibilityVisitor admis = new WitnessFromCodeAdmissibilityVisitor();            
            try {
              admis.VisitExpression(r.Expression);
              this.Witnesses.Add(r.Expression); //witness found, otherwise exception would have been thrown
              
              #region add inferred postconditions if needed
              TypeNode retType = (methodToInferPostFrom == null || methodToInferPostFrom.ReturnType == null) ? null : methodToInferPostFrom.ReturnType;              
              if (r == statements[0] && retType != null && (retType.IsObjectReferenceType || retType.IsPrimitiveComparable)) {
                //the return statement is the first statement of the body.
                //We would like to add ensures result == r.Expression; However, we have to be careful, for 2 reasons.
                //1: == cannot be applied to structs and generic types. Note that we already tested that we do not return a struct.
                //2: result might be the implicitly boxed version of r.Expression (e.g., public object m() {returns 5;}), in which case result == r.Expression does not hold (in particular at runtime)
                //To account for 2, we have to box/unbox result and r.Expression as necessary
                //If r.Expression is a generic type, we have to distinguish cases !(r.Expression is System.ValueType) or (r.Expression is int) and act accordingly                              
                //(We do not (yet) support cases where r.Expression is a valueType other than an int, but they could be handled along the same lines.)

                if (methodToInferPostFrom.Contract == null) {
                  methodToInferPostFrom.Contract = new MethodContract(methodToInferPostFrom);
                }
                SourceContext scInferred = methodToInferPostFrom.Name.SourceContext; //can't set the sourcetext, so error message will be confusing...but then again, the idea is that this can never crash                                                                                
                
                //Duplicate the return expression to avoid sharing problems 
                Duplicator dup = new Duplicator(this.methodToInferPostFrom.DeclaringType.DeclaringModule, this.methodToInferPostFrom.DeclaringType);
                Expression returnInEq = dup.VisitExpression(r.Expression);

                //needed for casting/boxing/unboxing
                MemberBinding intType = new MemberBinding(null, SystemTypes.Int32);
                Literal objectLit = new Literal(SystemTypes.Object, SystemTypes.Type);
                Literal intLit = new Literal(SystemTypes.Int32, SystemTypes.Type);

                Expression resultBinding = new ReturnValue(methodToInferPostFrom.ReturnType, scInferred);
                Expression resultInEq = resultBinding;
                if (r.Expression.NodeType == NodeType.Box) { //if the return expression has been (implicitly) boxed, unbox both it and result
                  returnInEq = (returnInEq as BinaryExpression).Operand1; //as we have the return expression in hand, unboxing is easy
                  //adjust resultInEq to (returnInEq.Type)resultInEq (i.e., add an unbox), using an explicit coercion
                  BinaryExpression resultUnboxed = new BinaryExpression(resultInEq, intType, NodeType.Unbox, SystemTypes.Int32.GetReferenceType(), scInferred); //gets type System.Int32@ 
                  AddressDereference resultDeref = new AddressDereference(resultUnboxed, returnInEq.Type, scInferred);
                  resultInEq = new BinaryExpression(resultDeref, intLit, NodeType.ExplicitCoercion, SystemTypes.Int32, scInferred);
                }
                                
                if (returnInEq.Type != null && (returnInEq.Type.IsObjectReferenceType || returnInEq.Type.IsPrimitiveComparable)) {
                  //returnInEq is not a user-defined struct (to which we can't apply ==)
                  Expression eq = null; //holds the postcondition that is added
                  if (!(isGenericTypeThatCanBeStruct(returnInEq.Type))) {
                    //== can be applied to returnInEq. Therefore, it can also be applied to resultInEq, which is of a supertype (i.e., not generic type, not a struct). 
                    eq = new BinaryExpression(resultInEq, returnInEq, NodeType.Eq, SystemTypes.Boolean, scInferred);
                  } else {
                    #region adjust eq to compensate for generics 
                    //insert  ensures !(returnInEq is System.ValueType) ==> (object)resultInEq == (object)returnInEq;
                    //and     ensures returnInEq is int ==> (int)(object)resultInEq == (int)(object)returnInEq;
                    //the cast to object is needed to allow application of == and the cast to int. 
                    //Note that resultInEq.Type is known to also be a generic type as it is a supertype.
                    //(and note that in this case, there was no unboxing)

                    #region set up the antecedent !(returnInEq is System.ValueType)
                    TypeNode theType = SystemTypes.ValueType;

                    //antecedent: !(r.Expression is System.ValueType)
                    Expression ante = new BinaryExpression(r.Expression, new Literal(theType, SystemTypes.Type), NodeType.Is, SystemTypes.Boolean, scInferred);
                    ante = new UnaryExpression(ante, NodeType.LogicalNot, SystemTypes.Boolean, scInferred);
                    #endregion

                    #region adjust resultInEq and returnInEq to (object)resultInEq and (object)returnInEq
                    //adjust resultInEq to (object)result, so that it can be used in ==
                    //that means Box it to T, then ExplicitCast it to Object.
                    MemberBinding boxType = new MemberBinding(null, returnInEq.Type); //could also use method return type
                    BinaryExpression resultBoxed = new BinaryExpression(resultBinding, boxType, NodeType.Box, returnInEq.Type, scInferred);
                    resultInEq = new BinaryExpression(resultBoxed, objectLit, NodeType.ExplicitCoercion, SystemTypes.Object, scInferred);
                    
                    //adjust returnInEq to (object)returnInEq                    
                    BinaryExpression returnBoxed = new BinaryExpression(returnInEq, boxType, NodeType.Box, returnInEq.Type, scInferred);
                    returnInEq = new BinaryExpression(returnBoxed, objectLit, NodeType.ExplicitCoercion, SystemTypes.Object, scInferred);
                    #endregion

                    //Add first ensures; ensures ante ==> resultInEq == returnInEq;
                    eq = new BinaryExpression(resultInEq, returnInEq, NodeType.Eq, SystemTypes.Boolean, scInferred);
                    Expression impl = new BinaryExpression(ante, eq, NodeType.Implies, scInferred);
                    EnsuresNormal firstPost = new EnsuresNormal(impl);
                    firstPost.SourceContext = scInferred;
                    methodToInferPostFrom.Contract.Ensures.Add(firstPost);

                    //Now add ensures returnInEq is int ==> (int)(object)resultInEq == (int)(object)returnInEq;
                    //antecedent: r.Expression is int                
                    Expression secondAnte = new BinaryExpression(r.Expression, intLit, NodeType.Is, SystemTypes.Boolean, scInferred);

                    #region adjust resultInEq and returnInEq to (int)resultInEq and (int)returnInEq
                    //set resultInSecondEq to (int)resultInEq, i.e., to (int)(object)result
                    //this requires an unbox to int32@, then an adress-deref to int32, then an explicitcast to int32
                    //note that if we unbox to type Int32 directly, we get a "operation could destabilize the runtime" warining from the checkin tests
                    BinaryExpression resultUnboxed = new BinaryExpression(resultInEq, intType, NodeType.Unbox, SystemTypes.Int32.GetReferenceType(), scInferred); //gets type System.Int32@ 
                    AddressDereference resultDeref = new AddressDereference(resultUnboxed, SystemTypes.Int32, scInferred);
                    BinaryExpression resultInSecondEq = new BinaryExpression(resultDeref, intLit, NodeType.ExplicitCoercion, SystemTypes.Int32, scInferred);

                    //adjust returnInEq to (int)returnInEq 
                    BinaryExpression returnUnboxed = new BinaryExpression(returnInEq, intType, NodeType.Unbox, SystemTypes.Int32.GetReferenceType(), scInferred);
                    AddressDereference returnDeref = new AddressDereference(returnUnboxed, SystemTypes.Int32, scInferred);
                    BinaryExpression returnInSecondEq = new BinaryExpression(returnDeref, intLit, NodeType.ExplicitCoercion, SystemTypes.Int32, scInferred);
                    #endregion
                    
                    Expression secondEq = new BinaryExpression(resultInSecondEq, returnInSecondEq, NodeType.Eq, SystemTypes.Boolean, scInferred);
                    eq = new BinaryExpression(secondAnte, secondEq, NodeType.Implies, SystemTypes.Boolean, scInferred); //(Ab)use eq to hold the implication 
                    #endregion
                  }
                  //Add the constructed equality as a postcondition.
                  EnsuresNormal newPost = new EnsuresNormal(eq);
                  newPost.SourceContext = scInferred;
                  methodToInferPostFrom.Contract.Ensures.Add(newPost);  
                } //else don't generate a postcondition, can't apply == to a user-defined struct                 
              }
              #endregion
            }
            catch (ApplicationException e) { ApplicationException dumme = e; } //witness not admissible ('using' e to avoid warning)           
          }
        }
        return statements;
      }