Author: Kashika Akhouri Date: 2025-12-11T15:23:01+05:30 New Revision: 86f8445293934b1730b8024ffacf14f20387a0fc
URL: https://github.com/llvm/llvm-project/commit/86f8445293934b1730b8024ffacf14f20387a0fc DIFF: https://github.com/llvm/llvm-project/commit/86f8445293934b1730b8024ffacf14f20387a0fc.diff LOG: [LifetimeSafety] Infer [[clang::lifetimebound]] annotation (#171081) Adding Annotation Inference in Lifetime Analysis. This PR implicitly adds lifetime bound annotations to the AST which is then used by functions which are parsed later to detect UARs etc. Example: ```cpp std::string_view f1(std::string_view a) { return a; } std::string_view f2(std::string_view a) { return f1(a); } std::string_view ff(std::string_view a) { std::string stack = "something on stack"; return f2(stack); // warning: address of stack memory is returned } ``` Note: 1. We only add lifetime bound annotations to the functions being analyzed currently. 2. Currently, both annotation suggestion and inference work simultaneously. This can be modified based on requirements. 3. The current approach works given that functions are already present in the correct order (callee-before-caller). For not so ideal cases, we can create a CallGraph prior to calling the analysis. This can be done in the next PR. Added: Modified: clang/include/clang/Basic/LangOptions.def clang/include/clang/Options/Options.td clang/lib/Analysis/LifetimeSafety/Checker.cpp clang/test/Sema/warn-lifetime-safety-suggestions.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 093d2709e59f9..891c9c05afc87 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -501,6 +501,8 @@ LANGOPT(BoundsSafety, 1, 0, NotCompatible, "Bounds safety extension for C") LANGOPT(EnableLifetimeSafety, 1, 0, NotCompatible, "Experimental lifetime safety analysis for C++") +LANGOPT(EnableLifetimeSafetyInference, 1, 0, NotCompatible, "Experimental lifetime safety inference analysis for C++") + LANGOPT(PreserveVec3Type, 1, 0, NotCompatible, "Preserve 3-component vector type") #undef LANGOPT diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td index e704d9e6275ec..24b63438e22fc 100644 --- a/clang/include/clang/Options/Options.td +++ b/clang/include/clang/Options/Options.td @@ -1964,6 +1964,14 @@ defm lifetime_safety : BoolFOption< BothFlags<[], [CC1Option], " experimental lifetime safety for C++">>; +defm lifetime_safety_inference + : BoolFOption<"experimental-lifetime-safety-inference", + LangOpts<"EnableLifetimeSafetyInference">, DefaultFalse, + PosFlag<SetTrue, [], [CC1Option], "Enable">, + NegFlag<SetFalse, [], [CC1Option], "Disable">, + BothFlags<[], [CC1Option], + " experimental lifetime safety inference for C++">>; + defm addrsig : BoolFOption<"addrsig", CodeGenOpts<"Addrsig">, DefaultFalse, PosFlag<SetTrue, [], [ClangOption, CC1Option], "Emit">, diff --git a/clang/lib/Analysis/LifetimeSafety/Checker.cpp b/clang/lib/Analysis/LifetimeSafety/Checker.cpp index 74792768e2c57..99071d6b46c1e 100644 --- a/clang/lib/Analysis/LifetimeSafety/Checker.cpp +++ b/clang/lib/Analysis/LifetimeSafety/Checker.cpp @@ -55,13 +55,14 @@ class LifetimeChecker { const LiveOriginsAnalysis &LiveOrigins; const FactManager &FactMgr; LifetimeSafetyReporter *Reporter; + ASTContext &AST; public: LifetimeChecker(const LoanPropagationAnalysis &LoanPropagation, const LiveOriginsAnalysis &LiveOrigins, const FactManager &FM, AnalysisDeclContext &ADC, LifetimeSafetyReporter *Reporter) : LoanPropagation(LoanPropagation), LiveOrigins(LiveOrigins), FactMgr(FM), - Reporter(Reporter) { + Reporter(Reporter), AST(ADC.getASTContext()) { for (const CFGBlock *B : *ADC.getAnalysis<PostOrderCFGView>()) for (const Fact *F : FactMgr.getFacts(B)) if (const auto *EF = F->getAs<ExpireFact>()) @@ -70,6 +71,11 @@ class LifetimeChecker { checkAnnotations(OEF); issuePendingWarnings(); suggestAnnotations(); + // Annotation inference is currently guarded by a frontend flag. In the + // future, this might be replaced by a design that diff erentiates between + // explicit and inferred findings with separate warning groups. + if (AST.getLangOpts().EnableLifetimeSafetyInference) + inferAnnotations(); } /// Checks if an escaping origin holds a placeholder loan, indicating a @@ -160,6 +166,20 @@ class LifetimeChecker { for (const auto &[PVD, EscapeExpr] : AnnotationWarningsMap) Reporter->suggestAnnotation(PVD, EscapeExpr); } + + void inferAnnotations() { + // FIXME: To maximise inference propagation, functions should be analyzed in + // post-order of the call graph, allowing inferred annotations to propagate + // through the call chain + // FIXME: Add the inferred attribute to all redeclarations of the function, + // not just the definition being analyzed. + for (const auto &[ConstPVD, EscapeExpr] : AnnotationWarningsMap) { + ParmVarDecl *PVD = const_cast<ParmVarDecl *>(ConstPVD); + if (!PVD->hasAttr<LifetimeBoundAttr>()) + PVD->addAttr( + LifetimeBoundAttr::CreateImplicit(AST, PVD->getLocation())); + } + } }; } // namespace diff --git a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp index c0f675a301d14..9f3ccb7fca770 100644 --- a/clang/test/Sema/warn-lifetime-safety-suggestions.cpp +++ b/clang/test/Sema/warn-lifetime-safety-suggestions.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -Wexperimental-lifetime-safety-suggestions -verify %s +// RUN: %clang_cc1 -fsyntax-only -fexperimental-lifetime-safety -fexperimental-lifetime-safety-inference -Wexperimental-lifetime-safety-suggestions -Wexperimental-lifetime-safety -verify %s struct MyObj { int id; @@ -89,6 +89,98 @@ void test_getView_on_temporary() { (void)sv; } +//===----------------------------------------------------------------------===// +// Annotation Inference Test Cases +//===----------------------------------------------------------------------===// + +namespace correct_order_inference { +View return_view_by_func (View a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. + return return_view_directly(a); // expected-note {{param returned here}} +} + +MyObj* return_pointer_by_func (MyObj* a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. + return return_pointer_object(a); // expected-note {{param returned here}} +} +} // namespace correct_order_inference + +namespace incorrect_order_inference_view { +View return_view_callee(View a); + +// FIXME: No lifetime annotation suggestion when functions are not present in the callee-before-caller pattern +View return_view_caller(View a) { + return return_view_callee(a); +} + +View return_view_callee(View a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. + return a; // expected-note {{param returned here}} +} +} // namespace incorrect_order_inference_view + +namespace incorrect_order_inference_object { +MyObj* return_object_callee(MyObj* a); + +// FIXME: No lifetime annotation suggestion warning when functions are not present in the callee-before-caller pattern +MyObj* return_object_caller(MyObj* a) { + return return_object_callee(a); +} + +MyObj* return_object_callee(MyObj* a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. + return a; // expected-note {{param returned here}} +} +} // namespace incorrect_order_inference_object + +namespace simple_annotation_inference { +View inference_callee_return_identity(View a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. + return a; // expected-note {{param returned here}} +} + +View inference_caller_forwards_callee(View a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. + return inference_callee_return_identity(a); // expected-note {{param returned here}} +} + +View inference_top_level_return_stack_view() { + MyObj local_stack; + return inference_caller_forwards_callee(local_stack); // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} +} +} // namespace simple_annotation_inference + +namespace inference_in_order_with_redecls { +View inference_callee_return_identity(View a); +View inference_callee_return_identity(View a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. + return a; // expected-note {{param returned here}} +} + +View inference_caller_forwards_callee(View a); +View inference_caller_forwards_callee(View a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. + return inference_callee_return_identity(a); // expected-note {{param returned here}} +} + +View inference_top_level_return_stack_view() { + MyObj local_stack; + return inference_caller_forwards_callee(local_stack); // expected-warning {{address of stack memory is returned later}} + // expected-note@-1 {{returned here}} +} +} // namespace inference_in_order_with_redecls + +namespace inference_with_templates { +template<typename T> +T* template_identity(T* a) { // expected-warning {{param should be marked [[clang::lifetimebound]]}}. + return a; // expected-note {{param returned here}} +} + +template<typename T> +T* template_caller(T* a) { + return template_identity(a); // expected-note {{in instantiation of function template specialization 'inference_with_templates::template_identity<MyObj>' requested here}} +} + +// FIXME: Fails to detect UAR as template instantiations are deferred to the end of the Translation Unit. +MyObj* test_template_inference_with_stack() { + MyObj local_stack; + return template_caller(&local_stack); // expected-note {{in instantiation of function template specialization 'inference_with_templates::template_caller<MyObj>' requested here}} +} +} // namespace inference_with_templates + //===----------------------------------------------------------------------===// // Negative Test Cases //===----------------------------------------------------------------------===// _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
