Index: include/clang/Analysis/Analyses/Consumed.h =================================================================== --- include/clang/Analysis/Analyses/Consumed.h +++ include/clang/Analysis/Analyses/Consumed.h @@ -89,16 +89,6 @@ StringRef ExpectedState, StringRef ObservedState) {} - // FIXME: This can be removed when the attr propagation fix for templated - // classes lands. - /// Warn about return typestates set for unconsumable types. - /// - /// \param Loc -- The location of the attributes. - /// - /// \param TypeName -- The name of the unconsumable type. - virtual void warnReturnTypestateForUnconsumableType(SourceLocation Loc, - StringRef TypeName) {} - /// Warn about return typestate mismatches. /// /// \param Loc -- The SourceLocation of the return statement. Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -3229,9 +3229,14 @@ def warn_attr_on_unconsumable_class : Warning< "consumed analysis attribute is attached to member of class '%0' which isn't " "marked as consumable">, InGroup, DefaultIgnore; -def warn_return_typestate_for_unconsumable_type : Warning< - "return state set for an unconsumable type '%0'">, InGroup, - DefaultIgnore; +def warn_consumable_attr_on_static : Warning< + "consumed analysis attribute is attached to a " + "%select{static method|constructor}0 of class '%1'">, + InGroup, DefaultIgnore; +def warn_consumable_attr_for_unconsumable_type : Warning< + "attribute %0 invalid for %select{parameter|return value}1 of type %2: " + "expected a consumable class type as a value, reference, or rvalue reference">, + InGroup, DefaultIgnore; def warn_return_typestate_mismatch : Warning< "return value not in expected state; expected '%0', observed '%1'">, InGroup, DefaultIgnore; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -8942,6 +8942,9 @@ bool checkNSReturnsRetainedReturnType(SourceLocation loc, QualType type); + bool CheckParamOrReturnTypestateAttr(const Attr *A, const Decl *D, + const Decl *Old); + //===--------------------------------------------------------------------===// // C++ Coroutines TS // Index: lib/Analysis/Consumed.cpp =================================================================== --- lib/Analysis/Consumed.cpp +++ lib/Analysis/Consumed.cpp @@ -1203,19 +1203,9 @@ } else ReturnType = D->getCallResultType(); - if (const ReturnTypestateAttr *RTSAttr = D->getAttr()) { - const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl(); - if (!RD || !RD->hasAttr()) { - // FIXME: This should be removed when template instantiation propagates - // attributes at template specialization definition, not - // declaration. When it is removed the test needs to be enabled - // in SemaDeclAttr.cpp. - WarningsHandler.warnReturnTypestateForUnconsumableType( - RTSAttr->getLocation(), ReturnType.getAsString()); - ExpectedReturnState = CS_None; - } else - ExpectedReturnState = mapReturnTypestateAttrState(RTSAttr); - } else if (isConsumableType(ReturnType)) { + if (const ReturnTypestateAttr *RTSAttr = D->getAttr()) + ExpectedReturnState = mapReturnTypestateAttrState(RTSAttr); + else if (isConsumableType(ReturnType)) { if (isAutoCastType(ReturnType)) // We can auto-cast the state to the ExpectedReturnState = CS_None; // expected state. else Index: lib/Sema/AnalysisBasedWarnings.cpp =================================================================== --- lib/Sema/AnalysisBasedWarnings.cpp +++ lib/Sema/AnalysisBasedWarnings.cpp @@ -1914,14 +1914,6 @@ Warnings.emplace_back(std::move(Warning), OptionalNotes()); } - void warnReturnTypestateForUnconsumableType(SourceLocation Loc, - StringRef TypeName) override { - PartialDiagnosticAt Warning(Loc, S.PDiag( - diag::warn_return_typestate_for_unconsumable_type) << TypeName); - - Warnings.emplace_back(std::move(Warning), OptionalNotes()); - } - void warnReturnTypestateMismatch(SourceLocation Loc, StringRef ExpectedState, StringRef ObservedState) override { Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -1122,15 +1122,20 @@ static bool checkForConsumableClass(Sema &S, const CXXMethodDecl *MD, const ParsedAttr &AL) { - QualType ThisType = MD->getThisType()->getPointeeType(); + const CXXRecordDecl *RD = MD->getParent(); - if (const CXXRecordDecl *RD = ThisType->getAsCXXRecordDecl()) { - if (!RD->hasAttr()) { - S.Diag(AL.getLoc(), diag::warn_attr_on_unconsumable_class) << - RD->getNameAsString(); + if (!RD->hasAttr()) { + S.Diag(AL.getLoc(), diag::warn_attr_on_unconsumable_class) << + RD->getNameAsString(); - return false; - } + return false; + } + + if (MD->isStatic() || isa(MD)) { + S.Diag(AL.getLoc(), diag::warn_consumable_attr_on_static) << + (MD->isStatic() ? 0 : 1) << + RD->getNameAsString(); + return false; } return true; @@ -1190,19 +1195,9 @@ return; } - // FIXME: This check is currently being done in the analysis. It can be - // enabled here only after the parser propagates attributes at - // template specialization definition, not declaration. - //QualType ReturnType = cast(D)->getType(); - //const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl(); - // - //if (!RD || !RD->hasAttr()) { - // S.Diag(AL.getLoc(), diag::warn_return_state_for_unconsumable_type) << - // ReturnType.getAsString(); - // return; - //} - - D->addAttr(::new (S.Context) ParamTypestateAttr(S.Context, AL, ParamState)); + Attr *A = ::new (S.Context) ParamTypestateAttr(S.Context, AL, ParamState); + if (S.CheckParamOrReturnTypestateAttr(A, D, nullptr)) + D->addAttr(A); } static void handleReturnTypestateAttr(Sema &S, Decl *D, const ParsedAttr &AL) { @@ -1222,32 +1217,42 @@ return; } - // FIXME: This check is currently being done in the analysis. It can be - // enabled here only after the parser propagates attributes at - // template specialization definition, not declaration. - //QualType ReturnType; - // - //if (const ParmVarDecl *Param = dyn_cast(D)) { - // ReturnType = Param->getType(); - // - //} else if (const CXXConstructorDecl *Constructor = - // dyn_cast(D)) { - // ReturnType = Constructor->getThisType()->getPointeeType(); - // - //} else { - // - // ReturnType = cast(D)->getCallResultType(); - //} - // - //const CXXRecordDecl *RD = ReturnType->getAsCXXRecordDecl(); - // - //if (!RD || !RD->hasAttr()) { - // S.Diag(Attr.getLoc(), diag::warn_return_state_for_unconsumable_type) << - // ReturnType.getAsString(); - // return; - //} + Attr *A = ::new (S.Context) ReturnTypestateAttr(S.Context, AL, ReturnState); + if (S.CheckParamOrReturnTypestateAttr(A, D, nullptr)) + D->addAttr(A); +} + +// Check a `param_typestate` or `return_typestate` attribute: emit a warning +// and return false if the parameter or return type is not consumable. - D->addAttr(::new (S.Context) ReturnTypestateAttr(S.Context, AL, ReturnState)); +bool Sema::CheckParamOrReturnTypestateAttr(const Attr *A, const Decl *D, + const Decl *Old) { + auto GetType = [&](const Decl *X) -> QualType { + // `return_typestate` can be applied to parameters (referring to the + // parameter), constructors (referring to the new object), or functions + // (referring to the return value). `param_typestate` can only be applied + // to parameters. + if (auto *PX = dyn_cast(X)) + return PX->getType(); + else if (auto *CX = dyn_cast(X)) + return CX->getThisType()->getPointeeType(); + else + return cast(X)->getCallResultType(); + }; + QualType Type = GetType(D); + if (Type->isDependentType()) + return true; // will re-check on instantiation + if (Old && !GetType(Old)->isDependentType()) + return true; // we already checked this + if (Type->isReferenceType()) + Type = Type->getPointeeType(); + if (const CXXRecordDecl *RD = Type->getAsCXXRecordDecl()) + if (RD->hasAttr()) + return true; // OK + + Diag(A->getLoc(), diag::warn_consumable_attr_for_unconsumable_type) << + A << (isa(D) ? 0 : 1) << Type; + return false; } static void handleSetTypestateAttr(Sema &S, Decl *D, const ParsedAttr &AL) { Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -549,6 +549,11 @@ continue; } + if (isa(TmplAttr) || isa(TmplAttr)) { + if (!CheckParamOrReturnTypestateAttr(TmplAttr, New, Tmpl)) + continue; // don't add + } + assert(!TmplAttr->isPackExpansion()); if (TmplAttr->isLateParsed() && LateAttrs) { // Late parsed attributes must be instantiated and attached after the Index: test/SemaCXX/warn-consumed-parsing.cpp =================================================================== --- test/SemaCXX/warn-consumed-parsing.cpp +++ test/SemaCXX/warn-consumed-parsing.cpp @@ -6,16 +6,6 @@ #define RETURN_TYPESTATE(state) __attribute__ ((return_typestate(state))) #define TEST_TYPESTATE(state) __attribute__ ((test_typestate(state))) -// FIXME: This test is here because the warning is issued by the Consumed -// analysis, not SemaDeclAttr. The analysis won't run after an error -// has been issued. Once the attribute propagation for template -// instantiation has been fixed, this can be moved somewhere else and the -// definition can be removed. -int returnTypestateForUnconsumable() RETURN_TYPESTATE(consumed); // expected-warning {{return state set for an unconsumable type 'int'}} -int returnTypestateForUnconsumable() { - return 0; -} - class AttrTester0 { void consumes() __attribute__ ((set_typestate())); // expected-error {{'set_typestate' attribute takes one argument}} bool testUnconsumed() __attribute__ ((test_typestate())); // expected-error {{'test_typestate' attribute takes one argument}} @@ -62,5 +52,37 @@ Status { }; +int returnTypestateForUnconsumable() RETURN_TYPESTATE(consumed); // expected-warning {{attribute 'return_typestate' invalid for return value of type 'int': expected a consumable class type as a value, reference, or rvalue reference}} +int returnTypestateForUnconsumable() { + return 0; +} + +struct CONSUMABLE(unknown) UselessAttrs { + static void x() SET_TYPESTATE(consumed); // expected-warning {{consumed analysis attribute is attached to a static method of class 'UselessAttrs'}} + UselessAttrs() SET_TYPESTATE(consumed); // expected-warning {{consumed analysis attribute is attached to a constructor of class 'UselessAttrs'}} + UselessAttrs(int) CALLABLE_WHEN(consumed); // expected-warning {{consumed analysis attribute is attached to a constructor of class 'UselessAttrs'}} + void operator+(UselessAttrs) SET_TYPESTATE(consumed); // OK + static void *operator new(unsigned long) SET_TYPESTATE(consumed); // expected-warning {{consumed analysis attribute is attached to a static method of class 'UselessAttrs'}} + template + void a([[clang::param_typestate(consumed)]] int) {} // expected-warning {{attribute 'param_typestate' invalid for parameter of type 'int': expected a consumable class type as a value, reference, or rvalue reference}} + void b([[clang::return_typestate(consumed)]] UselessAttrs *) {} // expected-warning {{attribute 'return_typestate' invalid for parameter of type 'UselessAttrs *': expected a consumable class type as a value, reference, or rvalue reference}} + template + void c([[clang::return_typestate(consumed)]] T) {} // expected-warning {{attribute 'return_typestate' invalid for parameter of type 'int': expected a consumable class type as a value, reference, or rvalue reference}} + template <> // test a template specialization + void c([[clang::return_typestate(consumed)]] long); // expected-warning {{attribute 'return_typestate' invalid for parameter of type 'long': expected a consumable class type as a value, reference, or rvalue reference}} + void instantiate() { + a(42); + c(42); + } +}; +void operator-(UselessAttrs, UselessAttrs) SET_TYPESTATE(consumed); // expected-warning {{'set_typestate' attribute only applies to functions}} +template +struct CONSUMABLE(unknown) ClassTemplateSpecialization; +template +struct ClassTemplateSpecialization { + [[clang::return_typestate(consumed)]] ClassTemplateSpecialization(); // expected-warning {{attribute 'return_typestate' invalid for return value of type 'ClassTemplateSpecialization': expected a consumable class type as a value, reference, or rvalue reference}} + void a([[clang::return_typestate(consumed)]] T) {} // expected-warning {{attribute 'return_typestate' invalid for parameter of type 'long': expected a consumable class type as a value, reference, or rvalue reference}} +}; +void testTS() { ClassTemplateSpecialization().a(1); } // expected-note {{in instantiation of template class 'ClassTemplateSpecialization' requested here}}