@@ -1040,6 +1040,7 @@ class ThreadSafetyAnalyzer {
1040
1040
std::vector<CFGBlockInfo> BlockInfo;
1041
1041
1042
1042
BeforeSet *GlobalBeforeSet;
1043
+ CapExprSet ExpectedReturnedCapabilities;
1043
1044
1044
1045
public:
1045
1046
ThreadSafetyAnalyzer (ThreadSafetyHandler &H, BeforeSet* Bset)
@@ -2041,15 +2042,16 @@ void BuildLockset::handleCall(const Expr *Exp, const NamedDecl *D,
2041
2042
if (!a.has_value ()) {
2042
2043
Analyzer->Handler .handleExpectFewerUnderlyingMutexes (
2043
2044
Exp->getExprLoc (), D->getLocation (), Scope->toString (),
2044
- b.value ().getKind (), b.value ().toString ());
2045
+ b.value ().getKind (), b.value ().toString (), true );
2045
2046
} else if (!b.has_value ()) {
2046
2047
Analyzer->Handler .handleExpectMoreUnderlyingMutexes (
2047
2048
Exp->getExprLoc (), D->getLocation (), Scope->toString (),
2048
- a.value ().getKind (), a.value ().toString ());
2049
+ a.value ().getKind (), a.value ().toString (), true );
2049
2050
} else if (!a.value ().matches (b.value ())) {
2050
2051
Analyzer->Handler .handleUnmatchedUnderlyingMutexes (
2051
2052
Exp->getExprLoc (), D->getLocation (), Scope->toString (),
2052
- a.value ().getKind (), a.value ().toString (), b.value ().toString ());
2053
+ a.value ().getKind (), a.value ().toString (), b.value ().toString (),
2054
+ true );
2053
2055
break ;
2054
2056
}
2055
2057
}
@@ -2294,6 +2296,25 @@ void BuildLockset::VisitMaterializeTemporaryExpr(
2294
2296
}
2295
2297
}
2296
2298
2299
+ static bool checkRecordTypeForScopedCapability (QualType Ty) {
2300
+ const RecordType *RT = Ty->getAs <RecordType>();
2301
+
2302
+ if (!RT)
2303
+ return false ;
2304
+
2305
+ if (RT->getDecl ()->hasAttr <ScopedLockableAttr>())
2306
+ return true ;
2307
+
2308
+ // Else check if any base classes have the attribute.
2309
+ if (const auto *CRD = dyn_cast<CXXRecordDecl>(RT->getDecl ())) {
2310
+ if (!CRD->forallBases ([](const CXXRecordDecl *Base) {
2311
+ return !Base->hasAttr <ScopedLockableAttr>();
2312
+ }))
2313
+ return true ;
2314
+ }
2315
+ return false ;
2316
+ }
2317
+
2297
2318
void BuildLockset::VisitReturnStmt (const ReturnStmt *S) {
2298
2319
if (Analyzer->CurrentFunction == nullptr )
2299
2320
return ;
@@ -2316,6 +2337,49 @@ void BuildLockset::VisitReturnStmt(const ReturnStmt *S) {
2316
2337
ReturnType->getPointeeType ().isConstQualified () ? AK_Read : AK_Written,
2317
2338
POK_ReturnPointer);
2318
2339
}
2340
+
2341
+ if (!checkRecordTypeForScopedCapability (ReturnType))
2342
+ return ;
2343
+
2344
+ if (const auto *CBTE = dyn_cast<ExprWithCleanups>(RetVal))
2345
+ RetVal = CBTE->getSubExpr ();
2346
+ RetVal = RetVal->IgnoreCasts ();
2347
+ if (const auto *CBTE = dyn_cast<CXXBindTemporaryExpr>(RetVal))
2348
+ RetVal = CBTE->getSubExpr ();
2349
+ CapabilityExpr Cp;
2350
+ if (auto Object = Analyzer->ConstructedObjects .find (RetVal);
2351
+ Object != Analyzer->ConstructedObjects .end ()) {
2352
+ Cp = CapabilityExpr (Object->second , StringRef (), false );
2353
+ Analyzer->ConstructedObjects .erase (Object);
2354
+ }
2355
+ if (!Cp.shouldIgnore ()) {
2356
+ const FactEntry *Fact = FSet.findLock (Analyzer->FactMan , Cp);
2357
+ if (const ScopedLockableFactEntry *Scope =
2358
+ cast_or_null<ScopedLockableFactEntry>(Fact)) {
2359
+ CapExprSet LocksInReturnVal = Scope->getUnderlyingMutexes ();
2360
+ for (const auto &[a, b] : zip_longest (
2361
+ Analyzer->ExpectedReturnedCapabilities , LocksInReturnVal)) {
2362
+ if (!a.has_value ()) {
2363
+ Analyzer->Handler .handleExpectFewerUnderlyingMutexes (
2364
+ RetVal->getExprLoc (), Analyzer->CurrentFunction ->getLocation (),
2365
+ Scope->toString (), b.value ().getKind (), b.value ().toString (),
2366
+ false );
2367
+ } else if (!b.has_value ()) {
2368
+ Analyzer->Handler .handleExpectMoreUnderlyingMutexes (
2369
+ RetVal->getExprLoc (), Analyzer->CurrentFunction ->getLocation (),
2370
+ Scope->toString (), a.value ().getKind (), a.value ().toString (),
2371
+ false );
2372
+ break ;
2373
+ } else if (!a.value ().matches (b.value ())) {
2374
+ Analyzer->Handler .handleUnmatchedUnderlyingMutexes (
2375
+ RetVal->getExprLoc (), Analyzer->CurrentFunction ->getLocation (),
2376
+ Scope->toString (), a.value ().getKind (), a.value ().toString (),
2377
+ b.value ().toString (), false );
2378
+ break ;
2379
+ }
2380
+ }
2381
+ }
2382
+ }
2319
2383
}
2320
2384
2321
2385
// / Given two facts merging on a join point, possibly warn and decide whether to
@@ -2480,11 +2544,22 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
2480
2544
CapExprSet SharedLocksToAdd;
2481
2545
2482
2546
SourceLocation Loc = D->getLocation ();
2547
+ bool ReturnsScopedCapability;
2548
+ if (CurrentFunction)
2549
+ ReturnsScopedCapability = checkRecordTypeForScopedCapability (
2550
+ CurrentFunction->getReturnType ().getCanonicalType ());
2551
+ else if (auto CurrentMethod = dyn_cast<ObjCMethodDecl>(D))
2552
+ ReturnsScopedCapability = checkRecordTypeForScopedCapability (
2553
+ CurrentMethod->getReturnType ().getCanonicalType ());
2554
+ else
2555
+ llvm_unreachable (" Unknown function kind" );
2483
2556
for (const auto *Attr : D->attrs ()) {
2484
2557
Loc = Attr->getLocation ();
2485
2558
if (const auto *A = dyn_cast<RequiresCapabilityAttr>(Attr)) {
2486
2559
getMutexIDs (A->isShared () ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2487
2560
nullptr , D);
2561
+ if (ReturnsScopedCapability)
2562
+ getMutexIDs (ExpectedReturnedCapabilities, A, nullptr , D);
2488
2563
} else if (const auto *A = dyn_cast<ReleaseCapabilityAttr>(Attr)) {
2489
2564
// UNLOCK_FUNCTION() is used to hide the underlying lock implementation.
2490
2565
// We must ignore such methods.
@@ -2493,12 +2568,19 @@ void ThreadSafetyAnalyzer::runAnalysis(AnalysisDeclContext &AC) {
2493
2568
getMutexIDs (A->isShared () ? SharedLocksToAdd : ExclusiveLocksToAdd, A,
2494
2569
nullptr , D);
2495
2570
getMutexIDs (LocksReleased, A, nullptr , D);
2571
+ if (ReturnsScopedCapability)
2572
+ getMutexIDs (ExpectedReturnedCapabilities, A, nullptr , D);
2496
2573
} else if (const auto *A = dyn_cast<AcquireCapabilityAttr>(Attr)) {
2497
2574
if (A->args_size () == 0 )
2498
2575
return ;
2499
2576
getMutexIDs (A->isShared () ? SharedLocksAcquired
2500
2577
: ExclusiveLocksAcquired,
2501
2578
A, nullptr , D);
2579
+ if (ReturnsScopedCapability)
2580
+ getMutexIDs (ExpectedReturnedCapabilities, A, nullptr , D);
2581
+ } else if (const auto *A = dyn_cast<LocksExcludedAttr>(Attr)) {
2582
+ if (ReturnsScopedCapability)
2583
+ getMutexIDs (ExpectedReturnedCapabilities, A, nullptr , D);
2502
2584
} else if (isa<ExclusiveTrylockFunctionAttr>(Attr)) {
2503
2585
// Don't try to check trylock functions for now.
2504
2586
return ;
0 commit comments