[clang] nolock/noalloc attributes (PR #84983)

Doug Wyatt via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 19 13:46:27 PDT 2024


================
@@ -4912,3 +4922,279 @@ void AutoType::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) {
   Profile(ID, Context, getDeducedType(), getKeyword(), isDependentType(),
           getTypeConstraintConcept(), getTypeConstraintArguments());
 }
+
+FunctionEffect::~FunctionEffect() = default;
+
+bool FunctionEffect::diagnoseConversion(bool Adding, QualType OldType,
+                                        FunctionEffectSet OldFX,
+                                        QualType NewType,
+                                        FunctionEffectSet NewFX) const {
+  return false;
+}
+
+bool FunctionEffect::diagnoseRedeclaration(bool Adding,
+                                           const FunctionDecl &OldFunction,
+                                           FunctionEffectSet OldFX,
+                                           const FunctionDecl &NewFunction,
+                                           FunctionEffectSet NewFX) const {
+  return false;
+}
+
+bool FunctionEffect::diagnoseMethodOverride(bool Adding,
+                                            const CXXMethodDecl &OldMethod,
+                                            FunctionEffectSet OldFX,
+                                            const CXXMethodDecl &NewMethod,
+                                            FunctionEffectSet NewFX) const {
+  return false;
+}
+
+bool FunctionEffect::canInferOnDecl(const Decl *Caller,
+                                    FunctionEffectSet CallerFX) const {
+  return false;
+}
+
+bool FunctionEffect::diagnoseFunctionCall(bool Direct, const Decl *Caller,
+                                          FunctionEffectSet CallerFX,
+                                          CalleeDeclOrType Callee,
+                                          FunctionEffectSet CalleeFX) const {
+  return false;
+}
+
+const NoLockNoAllocEffect &NoLockNoAllocEffect::nolock_instance() {
+  static NoLockNoAllocEffect global(kNoLockTrue, "nolock");
+  return global;
+}
+
+const NoLockNoAllocEffect &NoLockNoAllocEffect::noalloc_instance() {
+  static NoLockNoAllocEffect global(kNoAllocTrue, "noalloc");
+  return global;
+}
+
+// TODO: Separate flags for noalloc
+NoLockNoAllocEffect::NoLockNoAllocEffect(EffectType Ty, const char *Name)
+    : FunctionEffect(Ty,
+                     kRequiresVerification | kVerifyCalls |
+                         kInferrableOnCallees | kExcludeThrow | kExcludeCatch |
+                         kExcludeObjCMessageSend | kExcludeStaticLocalVars |
+                         kExcludeThreadLocalVars,
+                     Name) {}
+
+NoLockNoAllocEffect::~NoLockNoAllocEffect() = default;
+
+std::string NoLockNoAllocEffect::attribute() const {
+  return std::string{"__attribute__((clang_"} + name().str() + "))";
+}
+
+bool NoLockNoAllocEffect::diagnoseConversion(bool Adding, QualType OldType,
+                                             FunctionEffectSet OldFX,
+                                             QualType NewType,
+                                             FunctionEffectSet NewFX) const {
+  // noalloc can't be added (spoofed) during a conversion, unless we have nolock
+  if (Adding) {
+    if (!isNoLock()) {
+      for (const auto *Effect : OldFX) {
+        if (Effect->type() == kNoLockTrue)
+          return false;
+      }
+    }
+    // nolock can't be added (spoofed) during a conversion.
+    return true;
+  }
+  return false;
+}
+
+bool NoLockNoAllocEffect::diagnoseRedeclaration(bool Adding,
+                                                const FunctionDecl &OldFunction,
+                                                FunctionEffectSet OldFX,
+                                                const FunctionDecl &NewFunction,
+                                                FunctionEffectSet NewFX) const {
+  // nolock/noalloc can't be removed in a redeclaration
+  // adding -> false, removing -> true (diagnose)
+  return !Adding;
+}
+
+bool NoLockNoAllocEffect::diagnoseMethodOverride(
+    bool Adding, const CXXMethodDecl &OldMethod, FunctionEffectSet OldFX,
+    const CXXMethodDecl &NewMethod, FunctionEffectSet NewFX) const {
+  // nolock/noalloc can't be removed from an override
+  return !Adding;
+}
+
+bool NoLockNoAllocEffect::canInferOnDecl(const Decl *Caller,
+                                         FunctionEffectSet CallerFX) const {
+  // Does the Decl have nolock(false) / noalloc(false) ?
+  QualType QT;
+  if (isa<BlockDecl>(Caller)) {
+    const auto *TSI = cast<BlockDecl>(Caller)->getSignatureAsWritten();
+    QT = TSI->getType();
+  } else if (isa<ValueDecl>(Caller)) {
+    QT = cast<ValueDecl>(Caller)->getType();
+  } else {
+    return false;
+  }
+  if (QT->hasAttr(isNoLock() ? attr::Kind::NoLock : attr::Kind::NoAlloc)) {
+    return false;
+  }
+
+  return true;
+}
+
+// TODO: Notice that we don't care about some of the parameters. Is the
+// interface overly general?
+bool NoLockNoAllocEffect::diagnoseFunctionCall(
+    bool Direct, const Decl *Caller, FunctionEffectSet CallerFX,
+    CalleeDeclOrType Callee, FunctionEffectSet CalleeFX) const {
+  const EffectType CallerType = type();
+  for (const auto *Effect : CalleeFX) {
+    const EffectType ET = Effect->type();
+    if (ET == CallerType || (CallerType == kNoAllocTrue && ET == kNoLockTrue)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+// =====
+
+void MutableFunctionEffectSet::insert(const FunctionEffect *Effect) {
+  auto Iter = std::lower_bound(begin(), end(), Effect);
+  if (*Iter != Effect) {
+    insert(Iter, Effect);
+  }
+}
+
+MutableFunctionEffectSet &
+MutableFunctionEffectSet::operator|=(FunctionEffectSet RHS) {
+  // TODO: For large RHS sets, use set_union or a custom insert-in-place
+  for (const auto *Effect : RHS) {
+    insert(Effect);
+  }
+  return *this;
+}
+
+// This could be simpler if there were a simple set container that could be
+// queried by ArrayRef but which stored something else. Possibly a DenseMap with
+// void values?
+FunctionEffectSet
+FunctionEffectSet::create(llvm::ArrayRef<const FunctionEffect *> Items) {
+  if (Items.empty()) {
+    return FunctionEffectSet{};
+  }
+  if (Items.size() == 1) {
+    return FunctionEffectSet{Items[0]};
+  }
+
+  UniquedAndSortedFX NewSet(Items); // just copies the ArrayRef
+
+  // SmallSet only has contains(), so it provides no way to obtain the uniqued
+  // value.
+  static std::set<UniquedAndSortedFX> uniquedFXSets;
----------------
dougsonos wrote:

This is now a DenseSet in the ASTContext. `FunctionEffectSet::create()` is replaced with `ASTContext::getUniquedFunctionEffectSet()`.

https://github.com/llvm/llvm-project/pull/84983


More information about the cfe-commits mailing list