Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/LangOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,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
Expand Down
8 changes: 8 additions & 0 deletions clang/include/clang/Options/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -1955,6 +1955,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">,
Expand Down
22 changes: 21 additions & 1 deletion clang/lib/Analysis/LifetimeSafety/Checker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>())
Expand All @@ -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 differentiates 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
Expand Down Expand Up @@ -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

Expand Down
94 changes: 93 additions & 1 deletion clang/test/Sema/warn-lifetime-safety-suggestions.cpp
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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
//===----------------------------------------------------------------------===//
Expand Down