@@ -256,22 +256,27 @@ static bool checkRecordDeclForAttr(const RecordDecl *RD) {
256256 return false ;
257257}
258258
259- static bool checkRecordTypeForCapability (Sema &S, QualType Ty) {
259+ static std::optional<TypeDecl *> checkRecordTypeForCapability (Sema &S,
260+ QualType Ty) {
260261 const RecordType *RT = getRecordType (Ty);
261262
262263 if (!RT)
263- return false ;
264+ return std:: nullopt ;
264265
265266 // Don't check for the capability if the class hasn't been defined yet.
266267 if (RT->isIncompleteType ())
267- return true ;
268+ return { nullptr } ;
268269
269270 // Allow smart pointers to be used as capability objects.
270271 // FIXME -- Check the type that the smart pointer points to.
271272 if (threadSafetyCheckIsSmartPointer (S, RT))
272- return true ;
273+ return { nullptr } ;
273274
274- return checkRecordDeclForAttr<CapabilityAttr>(RT->getDecl ());
275+ RecordDecl *RD = RT->getDecl ();
276+ if (checkRecordDeclForAttr<CapabilityAttr>(RD))
277+ return {RD};
278+
279+ return std::nullopt ;
275280}
276281
277282static bool checkRecordTypeForScopedCapability (Sema &S, QualType Ty) {
@@ -287,51 +292,76 @@ static bool checkRecordTypeForScopedCapability(Sema &S, QualType Ty) {
287292 return checkRecordDeclForAttr<ScopedLockableAttr>(RT->getDecl ());
288293}
289294
290- static bool checkTypedefTypeForCapability (QualType Ty) {
295+ static std::optional<TypeDecl *> checkTypedefTypeForCapability (QualType Ty) {
291296 const auto *TD = Ty->getAs <TypedefType>();
292297 if (!TD)
293- return false ;
298+ return std:: nullopt ;
294299
295300 TypedefNameDecl *TN = TD->getDecl ();
296301 if (!TN)
297- return false ;
302+ return std:: nullopt ;
298303
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};
305306
306- if ( checkRecordTypeForCapability (S, Ty))
307- return true ;
307+ return std:: nullopt ;
308+ }
308309
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 ;
310318}
311319
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 ) {
313322 // Capability expressions are simple expressions involving the boolean logic
314323 // operators &&, || or !, a simple DeclRefExpr, CastExpr or a ParenExpr. Once
315324 // a DeclRefExpr is found, its type should be checked to determine whether it
316325 // is a capability or not.
317326
318327 if (const auto *E = dyn_cast<CastExpr>(Ex))
319- return isCapabilityExpr (S, E->getSubExpr ());
328+ return validateCapabilityExpr (S, AL, E->getSubExpr (), Neg );
320329 else if (const auto *E = dyn_cast<ParenExpr>(Ex))
321- return isCapabilityExpr (S, E->getSubExpr ());
330+ return validateCapabilityExpr (S, AL, E->getSubExpr (), Neg );
322331 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+ }
327342 } else if (const auto *E = dyn_cast<BinaryOperator>(Ex)) {
328343 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 );
331346 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+ }
332352 }
333353
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 ;
335365}
336366
337367// / Checks that all attribute arguments, starting from Sidx, resolve to
@@ -420,11 +450,12 @@ static void checkAttrArgsAreCapabilityObjs(Sema &S, Decl *D,
420450 }
421451 }
422452
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
425455 // capability may be on the type, and the expression is a capability
426456 // 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))
428459 S.Diag (AL.getLoc (), diag::warn_thread_attribute_argument_not_lockable)
429460 << AL << ArgTy;
430461
@@ -496,7 +527,7 @@ static bool checkAcquireOrderAttrCommon(Sema &S, Decl *D, const ParsedAttr &AL,
496527
497528 // Check that this attribute only applies to lockable types.
498529 QualType QT = cast<ValueDecl>(D)->getType ();
499- if (!QT->isDependentType () && !typeHasCapability (S, QT)) {
530+ if (!QT->isDependentType () && !checkTypeForCapability (S, QT)) {
500531 S.Diag (AL.getLoc (), diag::warn_thread_attribute_decl_not_lockable) << AL;
501532 return false ;
502533 }
0 commit comments