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