[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