llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-temporal-safety Author: NeKon69 <details> <summary>Changes</summary> Extends `-Wlifetime-safety-inapplicable-lifetimebound` to also diagnose when the function's return type cannot carry a lifetime, making `[[clang::lifetimebound]]` on parameters/implicit this inapplicable. The attribute is only meaningful when the return value can refer to another object. When the return type is `int`, `void`, `Owner`, or any other type that cannot carry a lifetime, the attribute has no effect. For constructors, we use the class type as the effective return type instead of `void`, so `gsl::Pointer` constructors still work correctly. However, this means existing constructors with `clang::lifetimebound` on plain/mixed types now get diagnosed. Closes #<!-- -->188655 Assisted-by: Kimi K2.6 for writing some of the tests. --- Full diff: https://github.com/llvm/llvm-project/pull/203767.diff 8 Files Affected: - (modified) clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h (+4-1) - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+5) - (modified) clang/lib/Analysis/LifetimeSafety/Checker.cpp (+19-4) - (modified) clang/lib/Sema/SemaLifetimeSafety.h (+14-3) - (modified) clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp (+3-3) - (modified) clang/test/Sema/LifetimeSafety/inapplicable-lifetimebound.cpp (+26-1) - (modified) clang/test/Sema/LifetimeSafety/misplaced-lifetimebound-intra-tu.cpp (+1-1) - (modified) clang/test/Sema/LifetimeSafety/safety.cpp (+4-4) ``````````diff diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h index 28886b826f72f..ce35a14641a15 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeSafety.h @@ -142,7 +142,10 @@ class LifetimeSafetySemaHelper { const ParmVarDecl *PVDDef, const ParmVarDecl *PVDDecl) {} - virtual void reportInapplicableLifetimebound(const ParmVarDecl *PVD) {} + virtual void reportInapplicableLifetimebound(const ParmVarDecl *PVD, + QualType Type, + bool IsReturnType) {} + virtual void reportInapplicableLifetimebound(const CXXMethodDecl *MD) {} // Suggests lifetime bound annotations for implicit this. virtual void suggestLifetimeboundToImplicitThis(WarningScope Scope, diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 8097800e6744a..30ab502c32000 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11048,6 +11048,11 @@ def warn_lifetime_safety_inapplicable_lifetimebound InGroup<LifetimeSafetyInapplicableLifetimebound>, DefaultIgnore; +def warn_lifetime_safety_inapplicable_lifetimebound_return + : Warning<"'lifetimebound' attribute has no effect on this function because return type %0 cannot carry a lifetime. The attribute only applies when the return type is a reference type, pointer type, or non-owner record type">, + InGroup<LifetimeSafetyInapplicableLifetimebound>, + DefaultIgnore; + def note_lifetime_safety_used_here : Note<"later used here">; def note_lifetime_safety_invalidated_here : Note<"invalidated here">; def note_lifetime_safety_destroyed_here : Note<"destroyed here">; diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index d41d6f43f837b..41945607cf531 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -484,11 +484,26 @@ class LifetimeChecker { FDef->getTemplateSpecializationKind() == TSK_ImplicitInstantiation) return; + QualType ReturnType = FDef->getReturnType(); + if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(FDef)) + ReturnType = AST.getTypeDeclType(cast<TypeDecl>(Ctor->getParent())); + + bool ReturnTypeHasOrigins = + FactMgr.getOriginMgr().hasOrigins(ReturnType, + /*IntrinsicOnly=*/true); + if (getImplicitObjectParamLifetimeBoundAttr(FDef) && !ReturnTypeHasOrigins) + SemaHelper->reportInapplicableLifetimebound(cast<CXXMethodDecl>(FDef)); + for (const auto &PVD : FDef->parameters()) - if (PVD->hasAttr<LifetimeBoundAttr>() && - !FactMgr.getOriginMgr().hasOrigins(PVD->getType(), - /*IntrinsicOnly=*/true)) - SemaHelper->reportInapplicableLifetimebound(PVD); + if (PVD->hasAttr<LifetimeBoundAttr>()) { + if (!FactMgr.getOriginMgr().hasOrigins(PVD->getType(), + /*IntrinsicOnly=*/true)) + SemaHelper->reportInapplicableLifetimebound(PVD, PVD->getType(), + /*IsReturnType=*/false); + if (!ReturnTypeHasOrigins) + SemaHelper->reportInapplicableLifetimebound(PVD, ReturnType, + /*IsReturnType=*/true); + } } void inferAnnotations() { diff --git a/clang/lib/Sema/SemaLifetimeSafety.h b/clang/lib/Sema/SemaLifetimeSafety.h index 91f1290f38723..20312d1787c87 100644 --- a/clang/lib/Sema/SemaLifetimeSafety.h +++ b/clang/lib/Sema/SemaLifetimeSafety.h @@ -353,13 +353,24 @@ class LifetimeSafetySemaHelperImpl : public LifetimeSafetySemaHelper { << Attr->getRange(); } - void reportInapplicableLifetimebound(const ParmVarDecl *PVD) override { + void reportInapplicableLifetimebound(const ParmVarDecl *PVD, QualType Type, + bool IsReturnType) override { assert(PVD->hasAttr<LifetimeBoundAttr>() && "Expected parameter to have lifetimebound attribute"); const auto *Attr = PVD->getAttr<LifetimeBoundAttr>(); + unsigned DiagID = + IsReturnType + ? diag::warn_lifetime_safety_inapplicable_lifetimebound_return + : diag::warn_lifetime_safety_inapplicable_lifetimebound; + S.Diag(Attr->getLocation(), DiagID) << Type << Attr->getRange(); + } + + void reportInapplicableLifetimebound(const CXXMethodDecl *MD) override { + const auto *Attr = getImplicitObjectParamLifetimeBoundAttr(MD); + assert(Attr && "Expected lifetimebound attribute"); S.Diag(Attr->getLocation(), - diag::warn_lifetime_safety_inapplicable_lifetimebound) - << PVD->getType() << Attr->getRange(); + diag::warn_lifetime_safety_inapplicable_lifetimebound_return) + << MD->getReturnType() << Attr->getRange(); } void suggestLifetimeboundToImplicitThis(WarningScope Scope, diff --git a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp index cef3397b57a6f..754533b065836 100644 --- a/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp +++ b/clang/test/Sema/LifetimeSafety/annotation-suggestions.cpp @@ -1,7 +1,7 @@ // RUN: rm -rf %t // RUN: split-file %s %t // RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wlifetime-safety-annotation-placement -Wno-dangling -I%t -I%S -verify %t/test_source.cpp -// RUN: %clang_cc1 -fsyntax-only -std=c++23 -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wno-dangling -I%t -I%S -verify %t/test_source.cpp +// RUN: %clang_cc1 -fsyntax-only -std=c++23 -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wlifetime-safety-annotation-placement -Wno-dangling -I%t -I%S -verify %t/test_source.cpp // RUN: %clang_cc1 -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wlifetime-safety -Wno-dangling -I%t -I%S -fixit %t/test_source.cpp // RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety-suggestions -Wno-dangling -I%t -I%S -Werror=lifetime-safety-suggestions %t/test_source.cpp @@ -615,7 +615,7 @@ namespace make_unique_suggestion { struct LifetimeBoundCtor { View v; // FIXME: This test fails to propagate the lifetimebound in ctor if this is inferred (instead of the current explicit annotation). - LifetimeBoundCtor(const MyObj& obj [[clang::lifetimebound]]): v(obj) {} + LifetimeBoundCtor(const MyObj& obj [[clang::lifetimebound]]): v(obj) {} // expected-warning {{'lifetimebound' attribute has no effect on this function because return type 'make_unique_suggestion::LifetimeBoundCtor' cannot carry a lifetime}} }; std::unique_ptr<LifetimeBoundCtor> create_target(const MyObj& obj) { // expected-warning {{parameter in intra-TU function should be marked [[clang::lifetimebound]]}} @@ -649,7 +649,7 @@ void test_new_allocation() { struct LifetimeBoundCtor { View v; LifetimeBoundCtor(); - LifetimeBoundCtor(const MyObj& obj [[clang::lifetimebound]]) : v(obj) {} + LifetimeBoundCtor(const MyObj& obj [[clang::lifetimebound]]) : v(obj) {} // expected-warning {{'lifetimebound' attribute has no effect on this function because return type 'new_allocation_suggestion::LifetimeBoundCtor' cannot carry a lifetime}} }; struct HasCtorField { diff --git a/clang/test/Sema/LifetimeSafety/inapplicable-lifetimebound.cpp b/clang/test/Sema/LifetimeSafety/inapplicable-lifetimebound.cpp index 624a8e9bb00cc..0e8f3196df972 100644 --- a/clang/test/Sema/LifetimeSafety/inapplicable-lifetimebound.cpp +++ b/clang/test/Sema/LifetimeSafety/inapplicable-lifetimebound.cpp @@ -9,7 +9,14 @@ struct [[gsl::Pointer()]] View { View(const Owner &o [[clang::lifetimebound]]); }; -struct Plain {}; +struct Plain { + Plain() {} + Plain(const Plain &other [[clang::lifetimebound]]) {} // expected-warning {{'lifetimebound' attribute has no effect on this function because return type 'Plain' cannot carry a lifetime}} + Plain operator+(const Plain &other [[clang::lifetimebound]]) const { return {}; } // expected-warning {{'lifetimebound' attribute has no effect on this function because return type 'Plain' cannot carry a lifetime}} + Plain &operator=(const Plain &other [[clang::lifetimebound]]) { return *this; } + operator int() const [[clang::lifetimebound]] { return 0; } // expected-warning {{'lifetimebound' attribute has no effect on this function because return type 'int' cannot carry a lifetime}} + Plain(const Owner &o [[clang::lifetimebound]]) {} // expected-warning {{'lifetimebound' attribute has no effect on this function because return type 'Plain' cannot carry a lifetime}} +}; enum Enum { Enumerator }; @@ -90,3 +97,21 @@ int *context_sensitive_origin_type( lifetime_annotated_return(i); return v[0]; } + +int getInt(const Owner &o [[clang::lifetimebound]]) { return 0; } // expected-warning {{'lifetimebound' attribute has no effect on this function because return type 'int' cannot carry a lifetime}} +Owner getOwner(const Owner &o [[clang::lifetimebound]]) { return o; } // expected-warning {{'lifetimebound' attribute has no effect on this function because return type 'Owner' cannot carry a lifetime}} +Plain getPlain(const Owner &o [[clang::lifetimebound]]) { return {}; } // expected-warning {{'lifetimebound' attribute has no effect on this function because return type 'Plain' cannot carry a lifetime}} + +View getView(const Owner &o [[clang::lifetimebound]]) { return View(); } +Owner *getOwnerPtr(Owner &o [[clang::lifetimebound]]) { return &o; } + +auto getAuto(const Owner &o [[clang::lifetimebound]]) { return 0; } // expected-warning {{'lifetimebound' attribute has no effect on this function because return type 'int' cannot carry a lifetime}} + +int getIntMultiParam(const Owner &o [[clang::lifetimebound]], int i [[clang::lifetimebound]]) { return 0; } // expected-warning 2 {{'lifetimebound' attribute has no effect on this function because return type 'int' cannot carry a lifetime}} \ + // expected-warning {{'lifetimebound' attribute has no effect on parameter of type 'int'}} + +void test_lambda() { + auto lambda = [](const Owner &o [[clang::lifetimebound]]) -> int { return 0; }; // expected-warning {{'lifetimebound' attribute has no effect on this function because return type 'int' cannot carry a lifetime}} + (void)lambda; +} + diff --git a/clang/test/Sema/LifetimeSafety/misplaced-lifetimebound-intra-tu.cpp b/clang/test/Sema/LifetimeSafety/misplaced-lifetimebound-intra-tu.cpp index 7fa4cae100509..b1cfb490446e0 100644 --- a/clang/test/Sema/LifetimeSafety/misplaced-lifetimebound-intra-tu.cpp +++ b/clang/test/Sema/LifetimeSafety/misplaced-lifetimebound-intra-tu.cpp @@ -119,7 +119,7 @@ IntraSuppressedObj &intra_suppressed( return obj; } -struct View { +struct [[gsl::Pointer()]] View { friend View friend_redecl(MyObj & // expected-warning {{'lifetimebound' attribute on this definition is not visible to callers before the definition; add it to the declaration instead}} obj // CHECK: fix-it:"{{.*}}":{[[@LINE]]:{{[0-9]+}}-[[@LINE]]:{{[0-9]+}}}:" {{\[\[clang::lifetimebound\]\]}}" ); diff --git a/clang/test/Sema/LifetimeSafety/safety.cpp b/clang/test/Sema/LifetimeSafety/safety.cpp index c838918eb556d..687aa4c962b6c 100644 --- a/clang/test/Sema/LifetimeSafety/safety.cpp +++ b/clang/test/Sema/LifetimeSafety/safety.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety -Wlifetime-safety-annotation-placement -Wno-dangling -verify=expected,function %s // RUN: %clang_cc1 -fsyntax-only -flifetime-safety-inference -fexperimental-lifetime-safety-tu-analysis -Wlifetime-safety -Wno-dangling -verify=expected,tu %s -// RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety -Wno-dangling -fcxx-exceptions -verify=expected,function %s +// RUN: %clang_cc1 -fsyntax-only -Wlifetime-safety -Wlifetime-safety-annotation-placement -Wno-dangling -fcxx-exceptions -verify=expected,function %s #include "Inputs/lifetime-analysis.h" @@ -2533,7 +2533,7 @@ S S::return_self_after_registration() const { struct SWithUserDefinedCopyLikeOps { SWithUserDefinedCopyLikeOps(); - SWithUserDefinedCopyLikeOps(const std::string &s [[clang::lifetimebound]]) : owned(s), data(s) {} + SWithUserDefinedCopyLikeOps(const std::string &s [[clang::lifetimebound]]) : owned(s), data(s) {} // function-warning {{'lifetimebound' attribute has no effect on this function because return type 'track_origins_for_lifetimebound_record_type::SWithUserDefinedCopyLikeOps' cannot carry a lifetime}} SWithUserDefinedCopyLikeOps(const SWithUserDefinedCopyLikeOps &other) : owned("copy"), data(owned) {} @@ -2586,7 +2586,7 @@ void from_typedef_return() { struct SWithOriginPropagatingCopy { SWithOriginPropagatingCopy(); - SWithOriginPropagatingCopy(const std::string &s [[clang::lifetimebound]]) : data(s) {} + SWithOriginPropagatingCopy(const std::string &s [[clang::lifetimebound]]) : data(s) {} // function-warning {{'lifetimebound' attribute has no effect on this function because return type 'track_origins_for_lifetimebound_record_type::SWithOriginPropagatingCopy' cannot carry a lifetime}} SWithOriginPropagatingCopy(const SWithOriginPropagatingCopy &other) : data(other.data) {} std::string_view data; }; @@ -2603,7 +2603,7 @@ SWithOriginPropagatingCopy user_defined_copy_with_origin_propagation() { struct DefaultedOuter { DefaultedOuter(); - DefaultedOuter(const std::string &s [[clang::lifetimebound]]) : inner(s) {} + DefaultedOuter(const std::string &s [[clang::lifetimebound]]) : inner(s) {} // function-warning {{'lifetimebound' attribute has no effect on this function because return type 'track_origins_for_lifetimebound_record_type::DefaultedOuter' cannot carry a lifetime}} SWithUserDefinedCopyLikeOps inner; }; `````````` </details> https://github.com/llvm/llvm-project/pull/203767 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
