@@ -256,22 +256,27 @@ static bool checkRecordDeclForAttr(const RecordDecl *RD) {
256
256
return false ;
257
257
}
258
258
259
- static bool checkRecordTypeForCapability (Sema &S, QualType Ty) {
259
+ static std::optional<TypeDecl *> checkRecordTypeForCapability (Sema &S,
260
+ QualType Ty) {
260
261
const RecordType *RT = getRecordType (Ty);
261
262
262
263
if (!RT)
263
- return false ;
264
+ return std::nullopt ;
264
265
265
266
// Don't check for the capability if the class hasn't been defined yet.
266
267
if (RT->isIncompleteType ())
267
- return true ;
268
+ return { nullptr } ;
268
269
269
270
// Allow smart pointers to be used as capability objects.
270
271
// FIXME -- Check the type that the smart pointer points to.
271
272
if (threadSafetyCheckIsSmartPointer (S, RT))
272
- return true ;
273
+ return { nullptr } ;
273
274
274
- return checkRecordDeclForAttr<CapabilityAttr>(RT->getDecl ());
275
+ RecordDecl *RD = RT->getDecl ();
276
+ if (checkRecordDeclForAttr<CapabilityAttr>(RD))
277
+ return {RD};
278
+
279
+ return std::nullopt;
275
280
}
276
281
277
282
static bool checkRecordTypeForScopedCapability (Sema &S, QualType Ty) {
@@ -287,51 +292,76 @@ static bool checkRecordTypeForScopedCapability(Sema &S, QualType Ty) {
287
292
return checkRecordDeclForAttr<ScopedLockableAttr>(RT->getDecl ());
288
293
}
289
294
290
- static bool checkTypedefTypeForCapability (QualType Ty) {
295
+ static std::optional<TypeDecl *> checkTypedefTypeForCapability (QualType Ty) {
291
296
const auto *TD = Ty->getAs <TypedefType>();
292
297
if (!TD)
293
- return false ;
298
+ return std::nullopt ;
294
299
295
300
TypedefNameDecl *TN = TD->getDecl ();
296
301
if (!TN)
297
- return false ;
302
+ return std::nullopt ;
298
303
299
- return TN->hasAttr <CapabilityAttr>();
300
- }
301
-
302
- static bool typeHasCapability (Sema &S, QualType Ty) {
303
- if (checkTypedefTypeForCapability (Ty))
304
- return true ;
304
+ if (TN->hasAttr <CapabilityAttr>())
305
+ return {TN};
305
306
306
- if ( checkRecordTypeForCapability (S, Ty))
307
- return true ;
307
+ return std::nullopt;
308
+ }
308
309
309
- return false ;
310
+ // / Returns capability TypeDecl if defined, nullptr if not yet defined (maybe
311
+ // / capability), and nullopt if it definitely is not a capability.
312
+ static std::optional<TypeDecl *> checkTypeForCapability (Sema &S, QualType Ty) {
313
+ if (auto TD = checkTypedefTypeForCapability (Ty))
314
+ return TD;
315
+ if (auto TD = checkRecordTypeForCapability (S, Ty))
316
+ return TD;
317
+ return std::nullopt;
310
318
}
311
319
312
- static bool isCapabilityExpr (Sema &S, const Expr *Ex) {
320
+ static bool validateCapabilityExpr (Sema &S, const ParsedAttr &AL,
321
+ const Expr *Ex, bool Neg = false ) {
313
322
// Capability expressions are simple expressions involving the boolean logic
314
323
// operators &&, || or !, a simple DeclRefExpr, CastExpr or a ParenExpr. Once
315
324
// a DeclRefExpr is found, its type should be checked to determine whether it
316
325
// is a capability or not.
317
326
318
327
if (const auto *E = dyn_cast<CastExpr>(Ex))
319
- return isCapabilityExpr (S, E->getSubExpr ());
328
+ return validateCapabilityExpr (S, AL, E->getSubExpr (), Neg );
320
329
else if (const auto *E = dyn_cast<ParenExpr>(Ex))
321
- return isCapabilityExpr (S, E->getSubExpr ());
330
+ return validateCapabilityExpr (S, AL, E->getSubExpr (), Neg );
322
331
else if (const auto *E = dyn_cast<UnaryOperator>(Ex)) {
323
- if (E->getOpcode () == UO_LNot || E->getOpcode () == UO_AddrOf ||
324
- E->getOpcode () == UO_Deref)
325
- return isCapabilityExpr (S, E->getSubExpr ());
326
- return false ;
332
+ switch (E->getOpcode ()) {
333
+ case UO_LNot:
334
+ Neg = !Neg;
335
+ [[fallthrough]];
336
+ case UO_AddrOf:
337
+ case UO_Deref:
338
+ return validateCapabilityExpr (S, AL, E->getSubExpr (), Neg);
339
+ default :
340
+ return false ;
341
+ }
327
342
} else if (const auto *E = dyn_cast<BinaryOperator>(Ex)) {
328
343
if (E->getOpcode () == BO_LAnd || E->getOpcode () == BO_LOr)
329
- return isCapabilityExpr (S, E->getLHS ()) &&
330
- isCapabilityExpr (S, E->getRHS ());
344
+ return validateCapabilityExpr (S, AL, E->getLHS (), Neg ) &&
345
+ validateCapabilityExpr (S, AL, E->getRHS (), Neg );
331
346
return false ;
347
+ } else if (const auto *E = dyn_cast<CXXOperatorCallExpr>(Ex)) {
348
+ if (E->getOperator () == OO_Exclaim && E->getNumArgs () == 1 ) {
349
+ // operator!(this) - return type is the expression to check below.
350
+ Neg = !Neg;
351
+ }
332
352
}
333
353
334
- return typeHasCapability (S, Ex->getType ());
354
+ // Base case: check the inner type for capability.
355
+ QualType Ty = Ex->getType ();
356
+ if (auto TD = checkTypeForCapability (S, Ty)) {
357
+ if (Neg && *TD != nullptr && (*TD)->hasAttr <ReentrantCapabilityAttr>()) {
358
+ S.Diag (AL.getLoc (), diag::warn_thread_reentrant_with_negative_capability)
359
+ << Ty.getUnqualifiedType ();
360
+ }
361
+ return true ;
362
+ }
363
+
364
+ return false ;
335
365
}
336
366
337
367
// / Checks that all attribute arguments, starting from Sidx, resolve to
@@ -420,11 +450,12 @@ static void checkAttrArgsAreCapabilityObjs(Sema &S, Decl *D,
420
450
}
421
451
}
422
452
423
- // If the type does not have a capability, see if the components of the
424
- // expression have capabilities. This allows for writing C code where the
453
+ // If ArgTy is not a capability, this also checks if components of the
454
+ // expression are capabilities. This allows for writing C code where the
425
455
// capability may be on the type, and the expression is a capability
426
456
// boolean logic expression. Eg) requires_capability(A || B && !C)
427
- if (!typeHasCapability (S, ArgTy) && !isCapabilityExpr (S, ArgExp))
457
+ if (!validateCapabilityExpr (S, AL, ArgExp) &&
458
+ !checkTypeForCapability (S, ArgTy))
428
459
S.Diag (AL.getLoc (), diag::warn_thread_attribute_argument_not_lockable)
429
460
<< AL << ArgTy;
430
461
@@ -496,7 +527,7 @@ static bool checkAcquireOrderAttrCommon(Sema &S, Decl *D, const ParsedAttr &AL,
496
527
497
528
// Check that this attribute only applies to lockable types.
498
529
QualType QT = cast<ValueDecl>(D)->getType ();
499
- if (!QT->isDependentType () && !typeHasCapability (S, QT)) {
530
+ if (!QT->isDependentType () && !checkTypeForCapability (S, QT)) {
500
531
S.Diag (AL.getLoc (), diag::warn_thread_attribute_decl_not_lockable) << AL;
501
532
return false ;
502
533
}
0 commit comments