Ejemplo n.º 1
0
 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);
   }
 }
Ejemplo n.º 2
0
 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;
 }
Ejemplo n.º 3
0
 /// <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);  
 }
Ejemplo n.º 4
0
    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;
    }
Ejemplo n.º 5
0
 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;
 }
Ejemplo n.º 6
0
 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]);
 }
Ejemplo n.º 7
0
    /// <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 ;
    }
Ejemplo n.º 8
0
 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);
 }