Skip to content

Commit acf52a7

Browse files
committed
lifetime-safety-cxx-conditional
1 parent c3b31ba commit acf52a7

File tree

5 files changed

+79
-2
lines changed

5 files changed

+79
-2
lines changed

clang/include/clang/Analysis/Analyses/LifetimeSafety/FactsGenerator.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class FactsGenerator : public ConstStmtVisitor<FactsGenerator> {
4343
void VisitUnaryOperator(const UnaryOperator *UO);
4444
void VisitReturnStmt(const ReturnStmt *RS);
4545
void VisitBinaryOperator(const BinaryOperator *BO);
46+
void VisitConditionalOperator(const ConditionalOperator *CO);
4647
void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE);
4748
void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *FCE);
4849
void VisitInitListExpr(const InitListExpr *ILE);

clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,13 @@ void FactsGenerator::VisitBinaryOperator(const BinaryOperator *BO) {
176176
handleAssignment(BO->getLHS(), BO->getRHS());
177177
}
178178

179+
void FactsGenerator::VisitConditionalOperator(const ConditionalOperator *CO) {
180+
if (hasOrigin(CO)) {
181+
killAndFlowOrigin(*CO, *CO->getTrueExpr());
182+
flowOrigin(*CO, *CO->getFalseExpr());
183+
}
184+
}
185+
179186
void FactsGenerator::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *OCE) {
180187
// Assignment operators have special "kill-then-propagate" semantics
181188
// and are handled separately.

clang/test/Sema/warn-lifetime-safety-dataflow.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,3 +414,20 @@ void test_use_lifetimebound_call() {
414414
// CHECK: Expire ([[L_Y]] (Path: y))
415415
// CHECK: Expire ([[L_X]] (Path: x))
416416
}
417+
// CHECK-LABEL: Function: test_conditional_operator
418+
void test_conditional_operator(bool cond) {
419+
MyObj x, y;
420+
MyObj *p = cond ? &x : &y;
421+
// CHECK: Block B{{[0-9]+}}:
422+
// CHECK: Issue ([[L_X:[0-9]+]] (Path: x), ToOrigin: [[O_DRE_X:[0-9]+]] (Expr: DeclRefExpr))
423+
// CHECK: OriginFlow (Dest: [[O_ADDR_X:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_X]] (Expr: DeclRefExpr))
424+
// CHECK: Block B{{[0-9]+}}:
425+
// CHECK: Issue ([[L_Y:[0-9]+]] (Path: y), ToOrigin: [[O_DRE_Y:[0-9]+]] (Expr: DeclRefExpr))
426+
// CHECK: OriginFlow (Dest: [[O_ADDR_Y:[0-9]+]] (Expr: UnaryOperator), Src: [[O_DRE_Y]] (Expr: DeclRefExpr))
427+
// CHECK: Block B{{[0-9]+}}:
428+
// CHECK: OriginFlow (Dest: [[O_COND_OP:[0-9]+]] (Expr: ConditionalOperator), Src: [[O_ADDR_X]] (Expr: UnaryOperator))
429+
// CHECK: OriginFlow (Dest: [[O_COND_OP]] (Expr: ConditionalOperator), Src: [[O_ADDR_Y]] (Expr: UnaryOperator), Merge)
430+
// CHECK: OriginFlow (Dest: [[O_P:[0-9]+]] (Decl: p), Src: [[O_COND_OP]] (Expr: ConditionalOperator))
431+
// CHECK: Expire ([[L_Y]] (Path: y))
432+
// CHECK: Expire ([[L_X]] (Path: x))
433+
}

clang/test/Sema/warn-lifetime-safety.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,7 @@ void no_error_loan_from_current_iteration(bool cond) {
440440
//===----------------------------------------------------------------------===//
441441

442442
View Identity(View v [[clang::lifetimebound]]);
443+
MyObj* Identity(MyObj* v [[clang::lifetimebound]]);
443444
View Choose(bool cond, View a [[clang::lifetimebound]], View b [[clang::lifetimebound]]);
444445
MyObj* GetPointer(const MyObj& obj [[clang::lifetimebound]]);
445446

@@ -582,3 +583,55 @@ void lifetimebound_ctor() {
582583
}
583584
(void)v;
584585
}
586+
587+
// Conditional operator.
588+
void conditional_operator(bool cond) {
589+
MyObj safe;
590+
MyObj* p = &safe;
591+
{
592+
MyObj temp;
593+
p = cond ? &temp // expected-warning {{object whose reference is captured may not live long enough}}
594+
: &safe;
595+
} // expected-note {{destroyed here}}
596+
if (cond) p = &safe;
597+
(void)*p; // expected-note {{later used here}}
598+
599+
{
600+
MyObj a, b;
601+
p = cond ? &a // expected-warning {{object whose reference is captured does not live long enough}}
602+
: &b; // expected-warning {{object whose reference is captured does not live long enough}}
603+
} // expected-note 2 {{destroyed here}}
604+
(void)*p; // expected-note 2 {{later used here}}
605+
606+
{
607+
MyObj a, b, c, d;
608+
p = cond ? cond ? &a // expected-warning {{object whose reference is captured does not live long enough}}.
609+
: &b // expected-warning {{object whose reference is captured does not live long enough}}.
610+
: cond ? &c // expected-warning {{object whose reference is captured does not live long enough}}.
611+
: &d; // expected-warning {{object whose reference is captured does not live long enough}}.
612+
} // expected-note 4 {{destroyed here}}
613+
(void)*p; // expected-note 4 {{later used here}}
614+
615+
{
616+
MyObj a, b;
617+
p = Identity(cond ? &a // expected-warning {{object whose reference is captured does not live long enough}}
618+
: &b); // expected-warning {{object whose reference is captured does not live long enough}}
619+
} // expected-note 2 {{destroyed here}}
620+
(void)*p; // expected-note 2 {{later used here}}
621+
622+
{
623+
MyObj a, b;
624+
p = Identity(cond ? Identity(&a) // expected-warning {{object whose reference is captured does not live long enough}}
625+
: Identity(&b)); // expected-warning {{object whose reference is captured does not live long enough}}
626+
} // expected-note 2 {{destroyed here}}
627+
(void)*p; // expected-note 2 {{later used here}}
628+
629+
{
630+
MyObj a, b, c, d;
631+
p = Identity(cond ? Identity(cond ? &a // expected-warning {{object whose reference is captured does not live long enough}}
632+
: &b) // expected-warning {{object whose reference is captured does not live long enough}}
633+
: Identity(cond ? &c // expected-warning {{object whose reference is captured does not live long enough}}
634+
: &d)); // expected-warning {{object whose reference is captured does not live long enough}}
635+
} // expected-note 4 {{destroyed here}}
636+
(void)*p; // expected-note 4 {{later used here}}
637+
}

clang/unittests/Analysis/LifetimeSafetyTest.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -689,7 +689,6 @@ TEST_F(LifetimeAnalysisTest, GslPointerConstructFromView) {
689689
EXPECT_THAT(Origin("q"), HasLoansTo({"a"}, "p1"));
690690
}
691691

692-
// FIXME: Handle loans in ternary operator!
693692
TEST_F(LifetimeAnalysisTest, GslPointerInConditionalOperator) {
694693
SetupTest(R"(
695694
void target(bool cond) {
@@ -698,7 +697,7 @@ TEST_F(LifetimeAnalysisTest, GslPointerInConditionalOperator) {
698697
POINT(p1);
699698
}
700699
)");
701-
EXPECT_THAT(Origin("v"), HasLoansTo({}, "p1"));
700+
EXPECT_THAT(Origin("v"), HasLoansTo({"a", "b"}, "p1"));
702701
}
703702

704703
// FIXME: Handle temporaries.

0 commit comments

Comments
 (0)