r199169 - Consumed analysis: add two new attributes which fine-tune the behavior of

DeLesley Hutchins delesley at google.com
Mon Jan 13 16:36:54 PST 2014


Author: delesley
Date: Mon Jan 13 18:36:53 2014
New Revision: 199169

URL: http://llvm.org/viewvc/llvm-project?rev=199169&view=rev
Log:
Consumed analysis: add two new attributes which fine-tune the behavior of
consumable objects.  These are useful for implementing error codes that
must be checked.  Patch also includes some significant refactoring, which was
necesary to implement the new behavior.

Modified:
    cfe/trunk/include/clang/Basic/Attr.td
    cfe/trunk/lib/Analysis/Consumed.cpp
    cfe/trunk/lib/Sema/SemaDeclAttr.cpp
    cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp
    cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp

Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=199169&r1=199168&r2=199169&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Mon Jan 13 18:36:53 2014
@@ -1187,6 +1187,16 @@ def Consumable : InheritableAttr {
                            ["Unknown", "Consumed", "Unconsumed"]>];
 }
 
+def ConsumableAutoCast : InheritableAttr {
+  let Spellings = [GNU<"consumable_auto_cast_state">];
+  let Subjects = SubjectList<[CXXRecord]>;
+}
+
+def ConsumableSetOnRead : InheritableAttr {
+  let Spellings = [GNU<"consumable_set_state_on_read">];
+  let Subjects = SubjectList<[CXXRecord]>;
+}
+
 def CallableWhen : InheritableAttr {
   let Spellings = [GNU<"callable_when">];
   let Subjects = SubjectList<[CXXMethod]>;

Modified: cfe/trunk/lib/Analysis/Consumed.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/Consumed.cpp?rev=199169&r1=199168&r2=199169&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/Consumed.cpp (original)
+++ cfe/trunk/lib/Analysis/Consumed.cpp Mon Jan 13 18:36:53 2014
@@ -143,6 +143,7 @@ static bool isCallableInState(const Call
   return false;
 }
 
+
 static bool isConsumableType(const QualType &QT) {
   if (QT->isPointerType() || QT->isReferenceType())
     return false;
@@ -153,6 +154,23 @@ static bool isConsumableType(const QualT
   return false;
 }
 
+static bool isAutoCastType(const QualType &QT) {
+  if (QT->isPointerType() || QT->isReferenceType())
+    return false;
+
+  if (const CXXRecordDecl *RD = QT->getAsCXXRecordDecl())
+    return RD->hasAttr<ConsumableAutoCastAttr>();
+
+  return false;
+}
+
+static bool isSetOnReadPtrType(const QualType &QT) {
+  if (const CXXRecordDecl *RD = QT->getPointeeCXXRecordDecl())
+    return RD->hasAttr<ConsumableSetOnReadAttr>();
+  return false;
+}
+
+
 static bool isKnownState(ConsumedState State) {
   switch (State) {
   case CS_Unconsumed:
@@ -166,18 +184,18 @@ static bool isKnownState(ConsumedState S
 }
 
 static bool isRValueRefish(QualType ParamType) {
-  return ParamType->isRValueReferenceType() ||
+  return ParamType->isRValueReferenceType(); /* ||
         (ParamType->isLValueReferenceType() &&
          !cast<LValueReferenceType>(
-           ParamType.getCanonicalType())->isSpelledAsLValue());
+           ParamType.getCanonicalType())->isSpelledAsLValue()); */
 }
 
 static bool isTestingFunction(const FunctionDecl *FunDecl) {
   return FunDecl->hasAttr<TestTypestateAttr>();
 }
 
-static bool isValueType(QualType ParamType) {
-  return !(ParamType->isPointerType() || ParamType->isReferenceType());
+static bool isPointerOrRef(QualType ParamType) {
+  return ParamType->isPointerType() || ParamType->isReferenceType();
 }
 
 static ConsumedState mapConsumableAttrState(const QualType QT) {
@@ -455,15 +473,19 @@ class ConsumedStmtVisitor : public Const
   ConsumedAnalyzer &Analyzer;
   ConsumedStateMap *StateMap;
   MapType PropagationMap;
+
   void forwardInfo(const Stmt *From, const Stmt *To);
-  bool isLikeMoveAssignment(const CXXMethodDecl *MethodDecl);
-  void propagateReturnType(const Stmt *Call, const FunctionDecl *Fun,
-                           QualType ReturnType);
+  void copyInfo(const Stmt *From, const Stmt *To, ConsumedState CS);
+  ConsumedState getInfo(const Stmt *From);
+  void setInfo(const Stmt *To, ConsumedState NS);
+  void propagateReturnType(const Stmt *Call, const FunctionDecl *Fun);
 
 public:
   void checkCallability(const PropagationInfo &PInfo,
                         const FunctionDecl *FunDecl,
                         SourceLocation BlameLoc);
+  bool handleCall(const CallExpr *Call, const Expr *ObjArg,
+                  const FunctionDecl *FunD);
   
   void VisitBinaryOperator(const BinaryOperator *BinOp);
   void VisitCallExpr(const CallExpr *Call);
@@ -499,68 +521,182 @@ public:
   }
 };
 
+
+void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) {
+  InfoEntry Entry = PropagationMap.find(From);
+  if (Entry != PropagationMap.end())
+    PropagationMap.insert(PairType(To, Entry->second));
+}
+
+
+// Create a new state for To, which is initialized to the state of From.
+// If NS is not CS_None, sets the state of From to NS.
+void ConsumedStmtVisitor::copyInfo(const Stmt *From, const Stmt *To,
+                                   ConsumedState NS) {
+  InfoEntry Entry = PropagationMap.find(From);
+  if (Entry != PropagationMap.end()) {
+    PropagationInfo& PInfo = Entry->second;
+    ConsumedState CS = PInfo.getAsState(StateMap);
+    if (CS != CS_None)
+      PropagationMap.insert(PairType(To, CS));
+    if (NS != CS_None && PInfo.isPointerToValue())
+      setStateForVarOrTmp(StateMap, PInfo, NS);
+  }
+}
+
+
+// Get the ConsumedState for From
+ConsumedState ConsumedStmtVisitor::getInfo(const Stmt *From) {
+  InfoEntry Entry = PropagationMap.find(From);
+  if (Entry != PropagationMap.end()) {
+    PropagationInfo& PInfo = Entry->second;
+    return PInfo.getAsState(StateMap);
+  }
+  return CS_None;
+}
+
+
+// If we already have info for To then update it, otherwise create a new entry.
+void ConsumedStmtVisitor::setInfo(const Stmt *To, ConsumedState NS) {
+  InfoEntry Entry = PropagationMap.find(To);
+  if (Entry != PropagationMap.end()) {
+    PropagationInfo& PInfo = Entry->second;
+    if (PInfo.isPointerToValue())
+      setStateForVarOrTmp(StateMap, PInfo, NS);
+  } else if (NS != CS_None) {
+     PropagationMap.insert(PairType(To, PropagationInfo(NS)));
+  }
+}
+
+
+
 void ConsumedStmtVisitor::checkCallability(const PropagationInfo &PInfo,
                                            const FunctionDecl *FunDecl,
                                            SourceLocation BlameLoc) {
   assert(!PInfo.isTest());
-  
+
   const CallableWhenAttr *CWAttr = FunDecl->getAttr<CallableWhenAttr>();
   if (!CWAttr)
     return;
-  
+
   if (PInfo.isVar()) {
     ConsumedState VarState = StateMap->getState(PInfo.getVar());
-    
+
     if (VarState == CS_None || isCallableInState(CWAttr, VarState))
       return;
-    
+
     Analyzer.WarningsHandler.warnUseInInvalidState(
       FunDecl->getNameAsString(), PInfo.getVar()->getNameAsString(),
       stateToString(VarState), BlameLoc);
-    
+
   } else {
     ConsumedState TmpState = PInfo.getAsState(StateMap);
-    
+
     if (TmpState == CS_None || isCallableInState(CWAttr, TmpState))
       return;
-    
+
     Analyzer.WarningsHandler.warnUseOfTempInInvalidState(
       FunDecl->getNameAsString(), stateToString(TmpState), BlameLoc);
   }
 }
 
-void ConsumedStmtVisitor::forwardInfo(const Stmt *From, const Stmt *To) {
-  InfoEntry Entry = PropagationMap.find(From);
-  
-  if (Entry != PropagationMap.end())
-    PropagationMap.insert(PairType(To, Entry->second));
-}
 
-bool ConsumedStmtVisitor::isLikeMoveAssignment(
-  const CXXMethodDecl *MethodDecl) {
-  
-  return MethodDecl->isMoveAssignmentOperator() ||
-         (MethodDecl->getOverloadedOperator() == OO_Equal &&
-          MethodDecl->getNumParams() == 1 &&
-          MethodDecl->getParamDecl(0)->getType()->isRValueReferenceType());
+// Factors out common behavior for function, method, and operator calls.
+// Check parameters and set parameter state if necessary.
+// Returns true if the state of ObjArg is set, or false otherwise.
+bool ConsumedStmtVisitor::handleCall(const CallExpr *Call, const Expr *ObjArg,
+                                     const FunctionDecl *FunD) {
+  unsigned Offset = 0;
+  if (isa<CXXMethodDecl>(FunD))
+    Offset = 1;  // First argument to call is 'this' parameter
+
+  // check explicit parameters
+  for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
+    // Skip variable argument lists.
+    if (Index - Offset >= FunD->getNumParams())
+      break;
+
+    const ParmVarDecl *Param = FunD->getParamDecl(Index - Offset);
+    QualType ParamType = Param->getType();
+
+    InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
+
+    if (Entry == PropagationMap.end() || Entry->second.isTest())
+      continue;
+    PropagationInfo PInfo = Entry->second;
+
+    // Check that the parameter is in the correct state.
+    if (ParamTypestateAttr *PTA = Param->getAttr<ParamTypestateAttr>()) {
+      ConsumedState ParamState = PInfo.getAsState(StateMap);
+      ConsumedState ExpectedState = mapParamTypestateAttrState(PTA);
+
+      if (ParamState != ExpectedState)
+        Analyzer.WarningsHandler.warnParamTypestateMismatch(
+          Call->getArg(Index)->getExprLoc(),
+          stateToString(ExpectedState), stateToString(ParamState));
+    }
+
+    if (!(Entry->second.isVar() || Entry->second.isTmp()))
+      continue;
+
+    // Adjust state on the caller side.
+    if (isRValueRefish(ParamType))
+      setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed);
+    else if (ReturnTypestateAttr *RT = Param->getAttr<ReturnTypestateAttr>())
+      setStateForVarOrTmp(StateMap, PInfo, mapReturnTypestateAttrState(RT));
+    else if (isPointerOrRef(ParamType)) {
+      if (!ParamType->getPointeeType().isConstQualified() ||
+          isSetOnReadPtrType(ParamType))
+        setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown);
+    }
+  }
+
+  if (!ObjArg)
+    return false;
+
+  // check implicit 'self' parameter, if present
+  InfoEntry Entry = PropagationMap.find(ObjArg);
+  if (Entry != PropagationMap.end()) {
+    PropagationInfo PInfo = Entry->second;
+    checkCallability(PInfo, FunD, Call->getExprLoc());
+
+    if (SetTypestateAttr *STA = FunD->getAttr<SetTypestateAttr>()) {
+      if (PInfo.isVar()) {
+        StateMap->setState(PInfo.getVar(), mapSetTypestateAttrState(STA));
+        return true;
+      }
+      else if (PInfo.isTmp()) {
+        StateMap->setState(PInfo.getTmp(), mapSetTypestateAttrState(STA));
+        return true;
+      }
+    }
+    else if (isTestingFunction(FunD) && PInfo.isVar()) {
+      PropagationMap.insert(PairType(Call,
+        PropagationInfo(PInfo.getVar(), testsFor(FunD))));
+    }
+  }
+  return false;
 }
 
+
 void ConsumedStmtVisitor::propagateReturnType(const Stmt *Call,
-                                              const FunctionDecl *Fun,
-                                              QualType ReturnType) {
-  if (isConsumableType(ReturnType)) {
-    
+                                              const FunctionDecl *Fun) {
+  QualType RetType = Fun->getCallResultType();
+  if (RetType->isReferenceType())
+    RetType = RetType->getPointeeType();
+
+  if (isConsumableType(RetType)) {
     ConsumedState ReturnState;
-    
     if (ReturnTypestateAttr *RTA = Fun->getAttr<ReturnTypestateAttr>())
       ReturnState = mapReturnTypestateAttrState(RTA);
     else
-      ReturnState = mapConsumableAttrState(ReturnType);
+      ReturnState = mapConsumableAttrState(RetType);
     
     PropagationMap.insert(PairType(Call, PropagationInfo(ReturnState)));
   }
 }
 
+
 void ConsumedStmtVisitor::VisitBinaryOperator(const BinaryOperator *BinOp) {
   switch (BinOp->getOpcode()) {
   case BO_LAnd:
@@ -614,63 +750,21 @@ static bool isStdNamespace(const DeclCon
 }
 
 void ConsumedStmtVisitor::VisitCallExpr(const CallExpr *Call) {
-  if (const FunctionDecl *FunDecl =
-    dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee())) {
-    
-    // Special case for the std::move function.
-    // TODO: Make this more specific. (Deferred)
-    if (Call->getNumArgs() == 1 &&
-        FunDecl->getNameAsString() == "move" &&
-        isStdNamespace(FunDecl->getDeclContext())) {
-      forwardInfo(Call->getArg(0), Call);
-      return;
-    }
-    
-    unsigned Offset = Call->getNumArgs() - FunDecl->getNumParams();
-    
-    for (unsigned Index = Offset; Index < Call->getNumArgs(); ++Index) {
-      const ParmVarDecl *Param = FunDecl->getParamDecl(Index - Offset);
-      QualType ParamType = Param->getType();
-      
-      InfoEntry Entry = PropagationMap.find(Call->getArg(Index));
-      
-      if (Entry == PropagationMap.end() || Entry->second.isTest())
-        continue;
-      
-      PropagationInfo PInfo = Entry->second;
-      
-      // Check that the parameter is in the correct state.
-      
-      if (ParamTypestateAttr *PTA = Param->getAttr<ParamTypestateAttr>()) {
-        ConsumedState ParamState = PInfo.getAsState(StateMap);        
-        ConsumedState ExpectedState = mapParamTypestateAttrState(PTA);
-        
-        if (ParamState != ExpectedState)
-          Analyzer.WarningsHandler.warnParamTypestateMismatch(
-            Call->getArg(Index - Offset)->getExprLoc(),
-            stateToString(ExpectedState), stateToString(ParamState));
-      }
-      
-      if (!(Entry->second.isVar() || Entry->second.isTmp()))
-        continue;
-      
-      // Adjust state on the caller side.
-      
-      if (isRValueRefish(ParamType))
-        setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Consumed);
-      else if (ReturnTypestateAttr *RT = Param->getAttr<ReturnTypestateAttr>())
-        setStateForVarOrTmp(StateMap, PInfo, mapReturnTypestateAttrState(RT));
-      else if (!isValueType(ParamType) &&
-               !ParamType->getPointeeType().isConstQualified())
-        setStateForVarOrTmp(StateMap, PInfo, consumed::CS_Unknown);
-    }
-    
-    QualType RetType = FunDecl->getCallResultType();
-    if (RetType->isReferenceType())
-      RetType = RetType->getPointeeType();
-    
-    propagateReturnType(Call, FunDecl, RetType);
+  const FunctionDecl *FunDecl = Call->getDirectCallee();
+  if (!FunDecl)
+    return;
+
+  // Special case for the std::move function.
+  // TODO: Make this more specific. (Deferred)
+  if (Call->getNumArgs() == 1 &&
+      FunDecl->getNameAsString() == "move" &&
+      isStdNamespace(FunDecl->getDeclContext())) {
+    copyInfo(Call->getArg(0), Call, CS_Consumed);
+    return;
   }
+
+  handleCall(Call, 0, FunDecl);
+  propagateReturnType(Call, FunDecl);
 }
 
 void ConsumedStmtVisitor::VisitCastExpr(const CastExpr *Cast) {
@@ -701,154 +795,57 @@ void ConsumedStmtVisitor::VisitCXXConstr
   if (ReturnTypestateAttr *RTA = Constructor->getAttr<ReturnTypestateAttr>()) {
     // TODO: Adjust state of args appropriately.    
     ConsumedState RetState = mapReturnTypestateAttrState(RTA);
-    PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));    
-  } else if (Constructor->isDefaultConstructor()) {    
+    PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
+  } else if (Constructor->isDefaultConstructor()) {
     PropagationMap.insert(PairType(Call,
       PropagationInfo(consumed::CS_Consumed)));
-  } else if (Constructor->isMoveConstructor()) {    
-    InfoEntry Entry = PropagationMap.find(Call->getArg(0));
-    
-    if (Entry != PropagationMap.end()) {
-      PropagationInfo PInfo = Entry->second;
-      
-      if (PInfo.isVar()) {
-        const VarDecl* Var = PInfo.getVar();
-        
-        PropagationMap.insert(PairType(Call,
-          PropagationInfo(StateMap->getState(Var))));
-        
-        StateMap->setState(Var, consumed::CS_Consumed);
-        
-      } else if (PInfo.isTmp()) {
-        const CXXBindTemporaryExpr *Tmp = PInfo.getTmp();
-        
-        PropagationMap.insert(PairType(Call,
-          PropagationInfo(StateMap->getState(Tmp))));
-        
-        StateMap->setState(Tmp, consumed::CS_Consumed);
-        
-      } else {
-        PropagationMap.insert(PairType(Call, PInfo));
-      }
-    }
+  } else if (Constructor->isMoveConstructor()) {
+    copyInfo(Call->getArg(0), Call, CS_Consumed);
   } else if (Constructor->isCopyConstructor()) {
-    forwardInfo(Call->getArg(0), Call);
-    
+    // Copy state from arg.  If setStateOnRead then set arg to CS_Unknown.
+    ConsumedState NS =
+      isSetOnReadPtrType(Constructor->getThisType(CurrContext)) ?
+      CS_Unknown : CS_None;
+    copyInfo(Call->getArg(0), Call, NS);
   } else {
     // TODO: Adjust state of args appropriately.
-    
     ConsumedState RetState = mapConsumableAttrState(ThisType);
     PropagationMap.insert(PairType(Call, PropagationInfo(RetState)));
   }
 }
 
+
 void ConsumedStmtVisitor::VisitCXXMemberCallExpr(
-  const CXXMemberCallExpr *Call) {
-  
-  VisitCallExpr(Call);
-  
-  InfoEntry Entry = PropagationMap.find(Call->getCallee()->IgnoreParens());
-  
-  if (Entry != PropagationMap.end()) {
-    PropagationInfo PInfo = Entry->second;
-    const CXXMethodDecl *MethodDecl = Call->getMethodDecl();
-    
-    checkCallability(PInfo, MethodDecl, Call->getExprLoc());
-    
-    SetTypestateAttr *STA = MethodDecl->getAttr<SetTypestateAttr>();
-    if (PInfo.isVar()) {
-      if (isTestingFunction(MethodDecl))
-        PropagationMap.insert(PairType(Call,
-          PropagationInfo(PInfo.getVar(), testsFor(MethodDecl))));
-      else if (STA)
-        StateMap->setState(PInfo.getVar(), mapSetTypestateAttrState(STA));
-    } else if (STA && PInfo.isTmp())
-      StateMap->setState(PInfo.getTmp(), mapSetTypestateAttrState(STA));
-  }
+    const CXXMemberCallExpr *Call) {
+  CXXMethodDecl* MD = Call->getMethodDecl();
+  if (!MD)
+    return;
+
+  handleCall(Call, Call->getImplicitObjectArgument(), MD);
+  propagateReturnType(Call, MD);
 }
 
+
 void ConsumedStmtVisitor::VisitCXXOperatorCallExpr(
-  const CXXOperatorCallExpr *Call) {
-  
+    const CXXOperatorCallExpr *Call) {
+
   const FunctionDecl *FunDecl =
     dyn_cast_or_null<FunctionDecl>(Call->getDirectCallee());
-  
   if (!FunDecl) return;
-    
-  if (isa<CXXMethodDecl>(FunDecl) &&
-      isLikeMoveAssignment(cast<CXXMethodDecl>(FunDecl))) {
-    
-    InfoEntry LEntry = PropagationMap.find(Call->getArg(0));
-    InfoEntry REntry = PropagationMap.find(Call->getArg(1));
-    
-    PropagationInfo LPInfo, RPInfo;
-    
-    if (LEntry != PropagationMap.end() &&
-        REntry != PropagationMap.end()) {
-      
-      LPInfo = LEntry->second;
-      RPInfo = REntry->second;
-      
-      if (LPInfo.isPointerToValue() && RPInfo.isPointerToValue()) {
-        setStateForVarOrTmp(StateMap, LPInfo, RPInfo.getAsState(StateMap));
-        PropagationMap.insert(PairType(Call, LPInfo));
-        setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
-        
-      } else if (RPInfo.isState()) {
-        setStateForVarOrTmp(StateMap, LPInfo, RPInfo.getState());
-        PropagationMap.insert(PairType(Call, LPInfo));
-        
-      } else {
-        setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
-      }
-      
-    } else if (LEntry != PropagationMap.end() &&
-               REntry == PropagationMap.end()) {
-      
-      LPInfo = LEntry->second;
-      
-      assert(!LPInfo.isTest());
-      
-      if (LPInfo.isPointerToValue()) {
-        setStateForVarOrTmp(StateMap, LPInfo, consumed::CS_Unknown);
-        PropagationMap.insert(PairType(Call, LPInfo));
-        
-      } else {
-        PropagationMap.insert(PairType(Call,
-          PropagationInfo(consumed::CS_Unknown)));
-      }
-      
-    } else if (LEntry == PropagationMap.end() &&
-               REntry != PropagationMap.end()) {
-      
-      RPInfo = REntry->second;
-      
-      if (RPInfo.isPointerToValue())
-        setStateForVarOrTmp(StateMap, RPInfo, consumed::CS_Consumed);
-    }
-    
-  } else {
-    
-    VisitCallExpr(Call);
-    
-    InfoEntry Entry = PropagationMap.find(Call->getArg(0));
-    
-    if (Entry != PropagationMap.end()) {
-      PropagationInfo PInfo = Entry->second;
-      
-      checkCallability(PInfo, FunDecl, Call->getExprLoc());
-      
-      SetTypestateAttr *STA = FunDecl->getAttr<SetTypestateAttr>();
-      if (PInfo.isVar()) {
-        if (isTestingFunction(FunDecl))
-          PropagationMap.insert(PairType(Call,
-            PropagationInfo(PInfo.getVar(), testsFor(FunDecl))));
-        else if (STA)
-          StateMap->setState(PInfo.getVar(), mapSetTypestateAttrState(STA));        
-      } else if (STA && PInfo.isTmp())
-        StateMap->setState(PInfo.getTmp(), mapSetTypestateAttrState(STA));
-    }
+
+  if (Call->getOperator() == OO_Equal) {
+    ConsumedState CS = getInfo(Call->getArg(1));
+    if (!handleCall(Call, Call->getArg(0), FunDecl))
+      setInfo(Call->getArg(0), CS);
+    return;
   }
+
+  if (const CXXMemberCallExpr *MCall = dyn_cast<CXXMemberCallExpr>(Call))
+    handleCall(MCall, MCall->getImplicitObjectArgument(), FunDecl);
+  else
+    handleCall(Call, Call->getArg(0), FunDecl);
+
+  propagateReturnType(Call, FunDecl);
 }
 
 void ConsumedStmtVisitor::VisitDeclRefExpr(const DeclRefExpr *DeclRef) {
@@ -1281,8 +1278,12 @@ void ConsumedAnalyzer::determineExpected
       ExpectedReturnState = CS_None;
     } else
       ExpectedReturnState = mapReturnTypestateAttrState(RTSAttr);
-  } else if (isConsumableType(ReturnType))
-    ExpectedReturnState = mapConsumableAttrState(ReturnType);
+  } else if (isConsumableType(ReturnType)) {
+    if (isAutoCastType(ReturnType))   // We can auto-cast the state to the
+      ExpectedReturnState = CS_None;  // expected state.
+    else
+      ExpectedReturnState = mapConsumableAttrState(ReturnType);
+  }
   else
     ExpectedReturnState = CS_None;
 }

Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=199169&r1=199168&r2=199169&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Mon Jan 13 18:36:53 2014
@@ -877,6 +877,7 @@ static void handleConsumableAttr(Sema &S
                             Attr.getAttributeSpellingListIndex()));
 }
 
+
 static bool checkForConsumableClass(Sema &S, const CXXMethodDecl *MD,
                                         const AttributeList &Attr) {
   ASTContext &CurrContext = S.getASTContext();
@@ -4264,6 +4265,12 @@ static void ProcessDeclAttribute(Sema &S
   case AttributeList::AT_Consumable:
     handleConsumableAttr(S, D, Attr);
     break;
+  case AttributeList::AT_ConsumableAutoCast:
+    handleSimpleAttribute<ConsumableAutoCastAttr>(S, D, Attr); break;
+    break;
+  case AttributeList::AT_ConsumableSetOnRead:
+    handleSimpleAttribute<ConsumableSetOnReadAttr>(S, D, Attr); break;
+    break;
   case AttributeList::AT_CallableWhen:
     handleCallableWhenAttr(S, D, Attr);
     break;

Modified: cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp?rev=199169&r1=199168&r2=199169&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-consumed-analysis.cpp Mon Jan 13 18:36:53 2014
@@ -645,9 +645,12 @@ void read(bool sf) {
 } // end namespace ContinueICETest
 
 
-namespace InitializerAssertionFailTest {
+namespace StatusUseCaseTests {
 
-class CONSUMABLE(unconsumed) Status {
+class CONSUMABLE(unconsumed)
+      __attribute__((consumable_auto_cast_state))
+      __attribute__((consumable_set_state_on_read))
+    Status {
   int code;
 
 public:
@@ -673,7 +676,9 @@ public:
 bool   cond();
 Status doSomething();
 void   handleStatus(const Status& s RETURN_TYPESTATE(consumed));
-void   handleStatusPtr(const Status* s);
+void   handleStatusRef(Status& s);
+void   handleStatusPtr(Status* s);
+void   handleStatusUnmarked(const Status& s);
 
 void testSimpleTemporaries0() {
   doSomething(); // expected-warning {{invalid invocation of method '~Status' on a temporary object while it is in the 'unconsumed' state}}
@@ -691,6 +696,15 @@ void testSimpleTemporaries3() {
   Status s = doSomething();
 }  // expected-warning {{invalid invocation of method '~Status' on object 's' while it is in the 'unconsumed' state}}
 
+Status testSimpleTemporariesReturn0() {
+  return doSomething();
+}
+
+Status testSimpleTemporariesReturn1() {
+  Status s = doSomething();
+  return s;
+}
+
 void testSimpleTemporaries4() {
   Status s = doSomething();
   s.check();
@@ -702,8 +716,17 @@ void testSimpleTemporaries5() {
 }
 
 void testSimpleTemporaries6() {
-  Status s = doSomething();
-  handleStatus(s);
+  Status s1 = doSomething();
+  handleStatus(s1);
+
+  Status s2 = doSomething();
+  handleStatusRef(s2);
+
+  Status s3 = doSomething();
+  handleStatusPtr(&s3);
+
+  Status s4 = doSomething();
+  handleStatusUnmarked(s4);
 }
 
 void testSimpleTemporaries7() {
@@ -745,38 +768,58 @@ void testTemporariesWithConditionals3()
 }
 
 void testTemporariesAndConstructors0() {
-  Status s(doSomething());
+  Status s(doSomething());    // Test the copy constructor.
   s.check();
 }
 
-void testTemporariesAndConstructors1() {
-  // Test the copy constructor.
-  
-  Status s1 = doSomething();
+void testTemporariesAndConstructors1F() {
+  Status s1 = doSomething();  // Test the copy constructor.
+  Status s2 = s1;
+} // expected-warning {{invalid invocation of method '~Status' on object 's2' while it is in the 'unconsumed' state}}
+
+void testTemporariesAndConstructors1S() {
+  Status s1 = doSomething();  // Test the copy constructor.
   Status s2(s1);
   s2.check();
-}  // expected-warning {{invalid invocation of method '~Status' on object 's1' while it is in the 'unconsumed' state}}
+}
 
-void testTemporariesAndConstructors2() {
+void testTemporariesAndConstructors2F() {
   // Test the move constructor.
-  
   Status s1 = doSomething();
-  Status s2(static_cast<Status&&>(s1));
+  Status s2 = static_cast<Status&&>(s1);
+} // expected-warning {{invalid invocation of method '~Status' on object 's2' while it is in the 'unconsumed' state}}
+
+void testTemporariesAndConstructors2S() {
+  // Test the move constructor.
+  Status s1 = doSomething();
+  Status s2 = static_cast<Status&&>(s1);
   s2.check();
 }
 
-void testTemporariesAndOperators0() {
+void testTemporariesAndOperators0F() {
+  // Test the assignment operator.
+  Status s1 = doSomething();
+  Status s2;
+  s2 = s1;
+} // expected-warning {{invalid invocation of method '~Status' on object 's2' while it is in the 'unconsumed' state}}
+
+void testTemporariesAndOperators0S() {
   // Test the assignment operator.
-  
   Status s1 = doSomething();
   Status s2;
   s2 = s1;
   s2.check();
-} // expected-warning {{invalid invocation of method '~Status' on object 's1' while it is in the 'unconsumed' state}}
+}
 
-void testTemporariesAndOperators1() {
+void testTemporariesAndOperators1F() {
+  // Test the move assignment operator.
+  Status s1 = doSomething();
+  Status s2;
+  s2 = static_cast<Status&&>(s1);
+} // expected-warning {{invalid invocation of method '~Status' on object 's2' while it is in the 'unconsumed' state}}
+
+void testTemporariesAndOperators1S() {
   // Test the move assignment operator.
-  
   Status s1 = doSomething();
   Status s2;
   s2 = static_cast<Status&&>(s1);
@@ -791,6 +834,12 @@ void testTemporariesAndOperators2() {
   s2.check();
 }
 
+Status testReturnAutocast() {
+  Status s = doSomething();
+  s.check();  // consume s
+  return s;   // should autocast back to unconsumed
+}
+
 } // end namespace InitializerAssertionFailTest
 
 

Modified: cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp?rev=199169&r1=199168&r2=199169&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp (original)
+++ cfe/trunk/test/SemaCXX/warn-consumed-parsing.cpp Mon Jan 13 18:36:53 2014
@@ -53,3 +53,13 @@ class AttrTester2 {
 };
 
 class CONSUMABLE(42) AttrTester3; // expected-error {{'consumable' attribute requires an identifier}}
+
+
+class CONSUMABLE(unconsumed)
+      __attribute__((consumable_auto_cast_state))
+      __attribute__((consumable_set_state_on_read))
+      Status {
+};
+
+
+





More information about the cfe-commits mailing list