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; }