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

Aaron Ballman aaron at aaronballman.com
Tue Jan 14 07:31:18 PST 2014


On Mon, Jan 13, 2014 at 7:36 PM, DeLesley Hutchins <delesley at google.com> wrote:
> 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()); */

Why is this code now commented out?

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

I think you have to check whether the function is static or not as well.

> +
> +  // 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))

Every time I see this, I think "how do you re-fish something?" ;-)

> +      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))

These can be hoisted into the enclosing if.

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

Quite a few style guideline mishaps in here (extra newlines, curly
braces when they should be elided, else clauses on their own line,
etc). It might make sense to run clang-format over the file at some
point.

~Aaron



More information about the cfe-commits mailing list