public void CheckModelfieldAdmissibility(ModelfieldContract mfC) { this.DeclaringMfC = mfC; this.DeclaringMember = mfC.Modelfield; if (this.DeclaringMember is Property) this.DeclaringMember = (this.DeclaringMember as Property).Getter; //references to the modelfield have been resolved as bindings to this getter foreach (Expression satExpr in mfC.SatisfiesList) { StateStack = new System.Collections.Stack(); ResetCurrentState(); this.VisitExpression(satExpr); } }
public virtual ModelfieldContract VisitModelfieldContract(ModelfieldContract mfC) { if (mfC == null) return null; mfC.Witness = this.VisitExpression(mfC.Witness); for (int i = 0, n = mfC.SatisfiesList.Count; i < n; i++) mfC.SatisfiesList[i] = this.VisitExpression(mfC.SatisfiesList[i]); return mfC; }
/// <summary> /// if mfC does not have a witness, infer one and return it, else return mfC.Witness. /// </summary> /// <param name="mfC"></param> /// <returns></returns> private Expression GetInferredWitness(ModelfieldContract/*!*/ mfC) { //At some point, this method should be replaced with the better inference scheme used for pure method if (mfC.ModelfieldType == null) return mfC.Witness; //something's wrong if (mfC.HasExplicitWitness) return mfC.Witness; //don't change user-supplied witness. //Let P be the conjunction of the expressions in eList. //if result != null, then either result witnesses the consistency of P, or P is inconsistent (either not total or P => false) //No, we're not gonna get quite that far, for instance on this.mf < i && this.mf != this.i - 1; (atually, we can do that one now) //for now, let's only worry about modelfields as far as this method is concerned. return GetInferredWitness(mfC.SatisfiesList, mfC.ModelfieldType, mfC.Modelfield); }
public override TypeNode VisitTypeNode(TypeNode typeNode) { if (typeNode == null) return null; TypeNode savedCurrentType = this.currentType; if (typeNode.IsNormalized) { this.currentType = this.typeSystem.currentType = typeNode; this.VisitMemberList(typeNode.Members); this.currentType = this.typeSystem.currentType = savedCurrentType; return typeNode; } if (typeNode.Template == this.currentType && typeNode.IsNotFullySpecialized) return typeNode; if (typeNode.PartiallyDefines != null) { if (this.visitedCompleteTypes == null) this.visitedCompleteTypes = new TrivialHashtable(); if (this.visitedCompleteTypes[typeNode.PartiallyDefines.UniqueKey] == null) { this.VisitTypeNode(typeNode.PartiallyDefines); this.visitedCompleteTypes[typeNode.PartiallyDefines.UniqueKey] = typeNode; } return typeNode; } typeNode.Attributes = this.VisitAttributeList(typeNode.Attributes); //Flatten interface list InterfaceList interfaces = this.GetTypeView(typeNode).Interfaces; for (int i = 0, n = interfaces == null ? 0 : interfaces.Count; i < n; i++) { Interface iface = interfaces[i]; if (iface == null || iface is TypeParameter) continue; if (this.GetTypeView(iface).IsAssignableTo(typeNode)) { this.HandleError(typeNode.Name, Error.CycleInInterfaceInheritance, this.GetTypeName(iface), this.GetTypeName(typeNode)); if (iface != typeNode) this.HandleRelatedError(iface); for (int j = i; j < n-1; j++) interfaces[j] = interfaces[j+1]; interfaces.Count = n-1; continue; } if (typeNode.NodeType == NodeType.Interface) { if (this.IsLessAccessible(iface, typeNode)) { this.HandleError(typeNode.Name, Error.BaseInterfaceLessAccessible, this.GetTypeName(iface), this.GetTypeName(typeNode)); this.HandleRelatedError(iface); } } InterfaceList inheritedInterfaces = this.GetTypeView(iface).Interfaces; int m = inheritedInterfaces == null ? 0 : inheritedInterfaces.Count; for (int j = 0; j < m; j++) { Interface iiface = inheritedInterfaces[j]; if (iiface == null) continue; bool mustAddInterface = true; for (int k = 0; k < n; k++) { if (interfaces[k] == iiface) { mustAddInterface = false; break; } } if (mustAddInterface) { interfaces.Add(iiface); n++; } } } typeNode.Attributes = this.VisitAttributeList(typeNode.Attributes, typeNode); this.currentType = this.typeSystem.currentType = typeNode; this.CheckHidingAndOverriding(typeNode); #region Deal with modelfields that are inherited from implemented interfaces. if (typeNode is Class) { StringCollection implementedModelfields = new StringCollection(); //contains the names of modelfields implemented so far foreach (Interface implInterface in typeNode.Interfaces) { if (implInterface == null) continue; //Why is Interfaces initialized to a List with a single null element? Means this check is essential. if (implInterface.Contract != null) { foreach (ModelfieldContract mfCToImplement in implInterface.Contract.ModelfieldContracts) { #region implement mfCToImplement in typeNode String fieldnameToImplement = mfCToImplement.Modelfield.Name.Name; if (implementedModelfields.Contains(fieldnameToImplement)) { this.HandleError(typeNode, Error.GenericError, "Class " + typeNode.Name.Name + " cannot implement two interfaces that both define a model field " + fieldnameToImplement); continue; //ignore this contract //Disallowed to prevent the unexpected modification of a modelfield in one interface by changing a modelfield in another interface. } else { implementedModelfields.Add(fieldnameToImplement); ModelfieldContract mfCThatImplements = null; //represents the contract that will implement mfCToImplement Member implementingMember = null; #region if typeNode or a superclass already defines a member named fieldNameToImplement, store it in implementingMember. for (TypeNode classWithField = typeNode; classWithField != null && implementingMember == null; classWithField = classWithField.BaseType) { MemberList members = this.GetTypeView(classWithField).GetMembersNamed(mfCToImplement.Modelfield.Name); foreach (Member m in members) { if (m.Name.Name == fieldnameToImplement) { implementingMember = m; break; //implementing member found; stop looking } } } #endregion #region if there is an implentingMember: if it is a modelfield in typeNode, then store its contract in mfCThatImplements, else complain if (implementingMember != null && implementingMember.DeclaringType != typeNode) { this.HandleError(typeNode, Error.GenericError, "Class " + typeNode.Name.Name + " does not define a model field " + fieldnameToImplement + " that implements " + mfCToImplement.Modelfield.FullName + " and hides " + implementingMember.FullName); this.HandleRelatedError(mfCToImplement.Modelfield); this.HandleRelatedError(implementingMember); //TODO: suppress error typeNode does not implement implInterface.fieldnameToImplement.get continue; //ignore this contract //Disallowed to prevent the unexpected modification of a superclass member by changing a modelfield in an interface/the unexpected hiding of a superclass member by a modelfield in an interface. } if (implementingMember != null && !(implementingMember is Field && (implementingMember as Field).IsModelfield)) { this.HandleError(typeNode, Error.GenericError, "Class " + typeNode.Name.Name + " cannot implement " + mfCToImplement.Modelfield.FullName + " as it contains a non-modelfield member of that name"); this.HandleRelatedError(mfCToImplement.Modelfield); this.HandleRelatedError(implementingMember); //TODO: suppress error typeNode does not implement implInterface.fieldnameToImplement.get continue; //ignore this contract } if (implementingMember != null) { //typeNode defines a modelfield (i.e., implementingMember) that can implement mfCToImplement Debug.Assert(typeNode.Contract != null); //a class that defines a modelfield must have a modelfieldcontract that applies to it. foreach (ModelfieldContract mfC in typeNode.Contract.ModelfieldContracts) if (mfC.Modelfield == implementingMember) { mfCThatImplements = mfC; break; } Debug.Assert(mfCThatImplements != null); } #endregion #region if there is no implementingMember: add a new modelfield + contract to typeNode and store contract in mfCThatImplements //TODO: Unfortunately, qualified identifiers have already been resolved: currently references to the modelfield will produce an error. if (implementingMember == null) { Identifier mfIdent = new Identifier(mfCToImplement.Modelfield.Name.Name); mfCThatImplements = new ModelfieldContract(typeNode, new AttributeList(), mfCToImplement.ModelfieldType, mfIdent, typeNode.SourceContext); Field mf = (mfCThatImplements.Modelfield as Field); mf.SourceContext = mfCToImplement.SourceContext; //the modelfield does not appear in the code but implements mfCToImplement. typeNode.Members.Add(mf); if (typeNode.Contract == null) typeNode.Contract = new TypeContract(typeNode); typeNode.Contract.ModelfieldContracts.Add(mfCThatImplements); } #endregion #region Implement the property and property getter that represent mfCToImplement, let getter return mfCThatImplements.Modelfield //assert typeNode.Contract.ModelfieldContracts.Contains(mfCThatImplements); //create Property: // public <mfCThatImplements.ModelfieldType> <mfCThatImplements.Modelfield.Name> // ensures result == <mfCThatImplements.Modelfield>; // { [Confined] get { return <mfCThatImplements.Modelfield>; } } // Note that getter needs to be confined because it inherits NoDefaultContract MemberBinding thisMf = new MemberBinding(new This(typeNode), mfCThatImplements.Modelfield); Statement ret = new Return(thisMf); Method getter = new Method(typeNode, new AttributeList(), (mfCToImplement.Modelfield as Property).Getter.Name, new ParameterList(), mfCThatImplements.ModelfieldType, new Block(new StatementList(ret))); getter.Flags = MethodFlags.Public; getter.CallingConvention = CallingConventionFlags.HasThis; if (getter.Contract == null) { getter.Contract = new MethodContract(getter); } Expression resultOfGet = new ReturnValue(getter.ReturnType, mfCToImplement.SourceContext); BinaryExpression b = new BinaryExpression(resultOfGet, thisMf, NodeType.Eq, mfCToImplement.SourceContext); b.Type = SystemTypes.Boolean; //Give getter Confined (as it has NoDefaultContract) // That means make it [Pure][Reads(Reads.Owned)] InstanceInitializer pCtor = SystemTypes.PureAttribute.GetConstructor(); if (pCtor != null) getter.Attributes.Add(new AttributeNode(new MemberBinding(null, pCtor), null, AttributeTargets.Method)); InstanceInitializer rCtor = SystemTypes.ReadsAttribute.GetConstructor(); // can use nullary ctor since default is confined if (rCtor != null) getter.Attributes.Add(new AttributeNode(new MemberBinding(null, rCtor), null, AttributeTargets.Method)); getter.Contract.Ensures.Add(new EnsuresNormal(b)); Identifier implPropName = new Identifier(mfCToImplement.Modelfield.FullName, mfCToImplement.SourceContext); //use full name as typeNode might define modelfield with this name itself. Property implementingProperty = new Property(typeNode, new AttributeList(), PropertyFlags.None, implPropName, getter, null); typeNode.Members.Add(implementingProperty); typeNode.Members.Add(getter); #endregion #region Copy the info from mfCToImplement to typeNode's mfCThatImplements foreach (Expression satClause in mfCToImplement.SatisfiesList) mfCThatImplements.SatisfiesList.Add(satClause); //Don't copy the explicit witness from the implemented contract: can likely infer a better one. #endregion } #endregion } } } } #endregion //needs to happen after CheckHidingAndOverriding, but before CheckAbstractMethods. this.CheckAbstractMethods(typeNode); //TODO: suppress duplicate errors generated by template instances this.CheckCircularDependency(typeNode); this.CheckForDuplicateDeclarations(typeNode); // must do this *after* CheckForDuplicateDeclarations this.CheckForInterfaceImplementationsOfOutOfBandContractedMethods(typeNode); this.CheckOperatorOverloads(typeNode); if (typeNode is Class && typeNode.IsSealed) this.CheckForNewFamilyOrVirtualMembers(typeNode); this.VisitTemplateInstanceTypes(typeNode); this.CheckContractInheritance(typeNode); TypeNode result = base.VisitTypeNode(typeNode); #region infer and serialize witnesses where needed (for modelfields and pure methods). ALSO infers postconditions. if (this.currentOptions != null && !this.currentOptions.DisablePublicContractsMetadata && !this.currentOptions.DisableInternalContractsMetadata) { //Note that this code could move to the boogie end, except that we need a runtime witness for modelfields. //We need to show that the contract of a modelfield mf is consistent in order to use the associated axioms. //An inferred witness is a guess at an expression e that will satisfy the contract, i.e., //an expression e such that for each satisfies clause p, p[e/mf] holds. Checking this witness is left to Boogie. if (typeNode.Contract != null) { foreach (ModelfieldContract mfC in typeNode.Contract.ModelfieldContracts) { if (mfC.ModelfieldType == null) continue; //signals error, but will be reported elsewhere Expression satisfies = null; foreach (Expression sat in mfC.SatisfiesList) { //construct a single expression to take advantage of the fact that multiple clauses act as an && if (sat == null) continue; if (satisfies == null) satisfies = sat; else satisfies = new BinaryExpression(sat, satisfies, NodeType.LogicalAnd, SystemTypes.Boolean); } WUCs witnesses = Checker.GetWitnesses(satisfies, mfC.Modelfield, mfC.ModelfieldType); this.SerializeWUCs(witnesses, mfC); //also serializes explicitly specified witnesses. if (mfC.Witness == null) { //we need to set a witness as runtime witness (do this afterwards as it will be serialized otherwise) //But as we have only one runtime witness, we can't guarantuee that we pick the right one. For now, just hope for the best. if (witnesses.RuntimeWitness != null) mfC.Witness = witnesses.RuntimeWitness; else mfC.Witness = this.GetInferredWitness(mfC); //this calculates a runtime witness } } } foreach (Member m in typeNode.Members) { Method method = m as Method; if (method == null || method.ReturnType == null) continue; if (!method.IsPure && !method.IsConfined && !method.IsStateIndependent) continue; if (method.CciKind != CciMemberKind.Regular) continue; //no need to check consistency of methodology method contracts if (method.ReturnType == SystemTypes.Void) continue; //no witness needed for void #region infer potential method contract witnesses and add them as WitnessAttributes //A pure method can be used in specifications and assert statements. //Therefore, we need to show that the contract of a pure method is consistent in order to use purity axioms. //An inferred witness is a guess at an expression e that will satisfy all postconditions, i.e., //an expression e such that for each postcondition p, p[e/result] holds. Checking this witness is left to Boogie. Expression postcondition = null; if (method.Contract != null) { foreach (Ensures ens in method.Contract.Ensures) { if (ens.PostCondition != null) { if (postcondition == null) postcondition = ens.PostCondition; else postcondition = new BinaryExpression(ens.PostCondition, postcondition, NodeType.LogicalAnd, SystemTypes.Boolean); } } } WUCs witnesses = Checker.GetWitnesses(postcondition, null, method.ReturnType); #region find witnesses in method code and infer postconditions if (method.Body != null && method.Body.Statements != null) { WitnessFromCodeFinderVisitor codeWitnessFinder = new WitnessFromCodeFinderVisitor(); if (!method.IsVirtual && //don't infer postcondition: the absence might be intentional, to give overriding method more freedom (method.Contract == null || method.Contract.Ensures.Count == 0)) //don't infer post if user specified a postcondition { //look for inferred postconditions codeWitnessFinder.methodToInferPostFrom = method; } codeWitnessFinder.VisitStatementList(method.Body.Statements); if (method.ReturnType != SystemTypes.Boolean && method.ReturnType.NodeType != NodeType.EnumNode) //check if all possible witnesses have already been added, finder was only run to infer postconditions foreach (Expression witness in codeWitnessFinder.Witnesses) witnesses.Exact.Add(new WitnessUnderConstruction(witness, null, 0)); } #endregion this.SerializeWUCs(witnesses, method); #endregion } } #endregion // do this here so the serialized contracts reflect any modifications made during Checker // serialized contracts should be pre-Normalized ASTs, *but* they should not reflect // any contract inheritance that has happened. // Note that this (possibly) adds things to the Attributes of the typeNode // Those attribute nodes will not be checked as part of Checker this.SerializeContracts(typeNode); this.currentType = this.typeSystem.currentType = savedCurrentType; return result; }
public override ModelfieldContract VisitModelfieldContract(ModelfieldContract mfC) { if (mfC == null || mfC.SatisfiesList == null) return mfC; bool savedInsideModelfield = this.insideModelfield; this.insideModelfield = true; bool savedMayRef = this.MayReferenceThisAndBase; this.MayReferenceThisAndBase = true; //Allow this and base to be mentioned in witness and satisifes clauses. //If there is a(n explicitly specified) witness, then check that the type of the witness is a subtype of the type of the modelfield if (mfC.Witness != null) { this.typeSystem.ImplicitCoercion(mfC.Witness, mfC.ModelfieldType, this.TypeViewer); //perhaps we want a different error? now shows "cannot implicitly convert type A to type B". mfC.Witness = this.VisitExpression(mfC.Witness); } mfC.SatisfiesList = this.VisitBooleanExpressionList(mfC.SatisfiesList); //satisfies expressions must have type boolean. this.MayReferenceThisAndBase = savedMayRef; this.insideModelfield = savedInsideModelfield; return mfC; }
public virtual void VisitModelfieldContract(ModelfieldContract mfC) { if (mfC == null) return; this.VisitExpression(mfC.Witness); for (int i = 0, n = mfC.SatisfiesList.Count; i < n; i++) this.VisitExpression(mfC.SatisfiesList[i]); }
/// <summary> /// Parse a modelfield. Requires that the model keyword has been swallowed already. /// Adds a new ModelfieldContract to parentType.Contract and, if the modelfield is not an override, adds a Member to parentType that represents the modelfield. /// </summary> private void ParseModelField(TypeNode parentType, AttributeList attributes, TokenList modifierTokens, SourceContextList modifierContexts, object sctx, TokenSet followers) { //A model field has the shape [new | override | sealed] model T name {ConstraintsAndWitness}. //override is not allowed if parentype is Interface. //model T name; is accepted as a shorthand for model T name {witness 0;}. #region parse type T //Expecting type T as currentToken. if (this.currentToken == Token.EndOfFile) { this.HandleError(Error.ExpectedExpression); return; } TypeNode type = this.ParseTypeExpression(parentType.ConstructorName, followers | Parser.IdentifierOrNonReservedKeyword); #endregion #region parse identifier name //Expecting identifier f if (!Parser.IdentifierOrNonReservedKeyword[this.currentToken]) { this.HandleError(Error.ExpectedIdentifier); if (this.currentToken != Token.EndOfFile) this.SkipTo(followers); return; } Identifier name = this.scanner.GetIdentifier(); #endregion ModelfieldContract mfC = new ModelfieldContract(parentType, attributes, type, name, name.SourceContext); #region handle modifiers bool isNew = false; for (int i = 0, n = modifierTokens.Length; i < n; i++) { switch (modifierTokens[i]) { case Token.New: if (isNew || mfC.IsOverride || parentType is Interface) this.HandleError(modifierContexts[i], Error.InvalidModifier, modifierContexts[i].SourceText); else { isNew = true; (mfC.Modelfield as Field).HidesBaseClassMember = true; } break; case Token.Override: if (isNew || mfC.IsOverride || parentType is Interface) this.HandleError(modifierContexts[i], Error.InvalidModifier, modifierContexts[i].SourceText); else { mfC.IsOverride = true; } break; case Token.Sealed: if (mfC.IsSealed) this.HandleError(modifierContexts[i], Error.DuplicateModifier, modifierContexts[i].SourceText); else mfC.IsSealed = true; break; default: this.HandleError(modifierContexts[i], Error.InvalidModifier, modifierContexts[i].SourceText); break; } } #endregion this.GetNextToken(); //now expect either a semicolon, or {ConstraintsAndWitness} if (this.currentToken == Token.Semicolon) { this.GetNextToken(); } else { this.Skip(Token.LeftBrace); #region Parse zero or more satisfies clauses (satisfies true on zero) and zero or one witness clauses while (this.currentToken == Token.Witness || this.currentToken == Token.Satisfies) { if (this.currentToken == Token.Witness) { if (!mfC.HasExplicitWitness) { //did not parse a witness yet this.GetNextToken(); mfC.Witness = this.ParseExpression(followers | Token.Semicolon); mfC.HasExplicitWitness = true; this.Skip(Token.Semicolon); } else { this.HandleError(Error.UnexpectedToken, this.scanner.GetTokenSource()); //there should be at most one witness this.GetNextToken(); break; } } else if (this.currentToken == Token.Satisfies) { this.GetNextToken(); Expression sat = this.ParseExpression(followers | Token.Semicolon); if (sat != null && !(parentType is Interface)) mfC.SatisfiesList.Add(sat); else if (parentType is Interface) this.HandleError(Error.SatisfiesInInterface, this.scanner.GetTokenSource()); this.Skip(Token.Semicolon); } } #endregion this.Skip(Token.RightBrace); } if (parentType.Contract == null) parentType.Contract = new TypeContract(parentType); parentType.Contract.ModelfieldContracts.Add(mfC); if (!mfC.IsOverride) parentType.Members.Add(mfC.Modelfield); //this.SkipSemiColon(followers); a modelfield is not terminated by a ; }
public override ModelfieldContract VisitModelfieldContract(ModelfieldContract mfC) { if (mfC == null) return null; SpecSharpCompilerOptions options = this.currentOptions as SpecSharpCompilerOptions; if (options != null && options.CheckContractAdmissibility) { AdmissibilityChecker checker = new AdmissibilityChecker(this); this.TransferStateTo(checker); checker.CheckModelfieldAdmissibility(mfC); } return base.VisitModelfieldContract(mfC); }