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

via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 12 14:06:16 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-modules

Author: Doug Wyatt (dougsonos)

<details>
<summary>Changes</summary>

Rough first draft. This is an early PR to solicit comments on the overall approach and a number of outstanding questions. Some of the larger ones, "earlier" in the flow (from parse -> Sema):

- Does the FunctionEffect / FunctionEffectSet abstraction make sense (Type.h)? The idea is that an abstract effect system for functions could easily support things like "TCB with types" or adding "nowait" to the "nolock/noalloc" group of effects.
- FunctionEffect/FunctionEffectSet need to be serialized as part of FunctionProtoType
- Conversions between functions / function pointers with and without the attribute work in C++ but not in C and I'm a bit lost (there's a bunch of debug logging around this e.g. Sema::IsFunctionConversion and Sema::ImpCastExprToType)


---

Patch is 116.62 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/84983.diff


26 Files Affected:

- (modified) clang/include/clang/AST/Decl.h (+9) 
- (modified) clang/include/clang/AST/Type.h (+294-3) 
- (modified) clang/include/clang/AST/TypeProperties.td (+5) 
- (modified) clang/include/clang/Basic/Attr.td (+23) 
- (modified) clang/include/clang/Basic/AttrDocs.td (+17) 
- (modified) clang/include/clang/Basic/DiagnosticGroups.td (+4) 
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+95) 
- (modified) clang/include/clang/Sema/Sema.h (+16) 
- (modified) clang/lib/AST/ASTContext.cpp (+26-5) 
- (modified) clang/lib/AST/Decl.cpp (+9) 
- (modified) clang/lib/AST/Type.cpp (+286) 
- (modified) clang/lib/AST/TypePrinter.cpp (+12) 
- (modified) clang/lib/Sema/AnalysisBasedWarnings.cpp (+1237) 
- (modified) clang/lib/Sema/Sema.cpp (+23) 
- (modified) clang/lib/Sema/SemaDecl.cpp (+82) 
- (modified) clang/lib/Sema/SemaDeclAttr.cpp (+28) 
- (modified) clang/lib/Sema/SemaDeclCXX.cpp (+21) 
- (modified) clang/lib/Sema/SemaExpr.cpp (+4) 
- (modified) clang/lib/Sema/SemaExprCXX.cpp (+7-1) 
- (modified) clang/lib/Sema/SemaLambda.cpp (+5) 
- (modified) clang/lib/Sema/SemaOverload.cpp (+27) 
- (modified) clang/lib/Sema/SemaType.cpp (+125-1) 
- (added) clang/test/Sema/attr-nolock-wip.cpp (+19) 
- (added) clang/test/Sema/attr-nolock.cpp (+78) 
- (added) clang/test/Sema/attr-nolock2.cpp (+84) 
- (added) clang/test/Sema/attr-nolock3.cpp (+139) 


``````````diff
diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index a5879591f4c659..0460f30ce8a8b4 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -3008,6 +3008,13 @@ class FunctionDecl : public DeclaratorDecl,
   /// computed and stored.
   unsigned getODRHash() const;
 
+  FunctionEffectSet getFunctionEffects() const {
+    const auto *FPT = getType()->getAs<FunctionProtoType>();
+    if (FPT)
+      return FPT->getFunctionEffects();
+    return {};
+  }
+
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) {
@@ -4633,6 +4640,8 @@ class BlockDecl : public Decl, public DeclContext {
 
   SourceRange getSourceRange() const override LLVM_READONLY;
 
+  FunctionEffectSet getFunctionEffects() const;
+
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classofKind(Kind K) { return K == Block; }
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 1942b0e67f65a3..de21561ce5dcad 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -4047,8 +4047,12 @@ class FunctionType : public Type {
     LLVM_PREFERRED_TYPE(bool)
     unsigned HasArmTypeAttributes : 1;
 
+    LLVM_PREFERRED_TYPE(bool)
+    unsigned HasFunctionEffects : 1;
+
     FunctionTypeExtraBitfields()
-        : NumExceptionType(0), HasArmTypeAttributes(false) {}
+        : NumExceptionType(0), HasArmTypeAttributes(false),
+          HasFunctionEffects(false) {}
   };
 
   /// The AArch64 SME ACLE (Arm C/C++ Language Extensions) define a number
@@ -4181,6 +4185,131 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode {
   }
 };
 
+class FunctionEffect;
+class FunctionEffectSet;
+
+// It is the user's responsibility to keep this in set form: elements are
+// ordered and unique.
+// We could hide the mutating methods which are capable of breaking the
+// invariant, but they're needed and safe when used with STL set algorithms.
+class MutableFunctionEffectSet : public SmallVector<const FunctionEffect *, 4> {
+public:
+  using SmallVector::insert;
+  using SmallVector::SmallVector;
+
+  /// Maintains order/uniquenesss.
+  void insert(const FunctionEffect *effect);
+
+  MutableFunctionEffectSet &operator|=(FunctionEffectSet rhs);
+};
+
+class FunctionEffectSet {
+public:
+  // These sets will tend to be very small (1 element), so represent them as
+  // sorted vectors, which are compatible with the STL set algorithms. Using an
+  // array or vector also means the elements are contiguous, keeping iterators
+  // simple.
+
+private:
+  // 'Uniqued' refers to the set itself being uniqued. Storage is allocated
+  // separately. Use ArrayRef for its iterators. Subclass so as to be able to
+  // compare (it seems ArrayRef would silently convert itself to a vector for
+  // comparison?!).
+  class UniquedAndSortedFX : public llvm::ArrayRef<const FunctionEffect *> {
+  public:
+    using Base = llvm::ArrayRef<const FunctionEffect *>;
+
+    UniquedAndSortedFX(Base Array) : Base(Array) {}
+    UniquedAndSortedFX(const FunctionEffect **Ptr, size_t Len)
+        : Base(Ptr, Len) {}
+
+    bool operator<(const UniquedAndSortedFX &rhs) const;
+  };
+
+  // Could have used a TinyPtrVector if it were unique-able.
+  // Empty set has a null Impl.
+  llvm::PointerUnion<const FunctionEffect *, const UniquedAndSortedFX *> Impl;
+
+  explicit FunctionEffectSet(const FunctionEffect *Single) : Impl(Single) {}
+  explicit FunctionEffectSet(const UniquedAndSortedFX *Multi) : Impl(Multi) {}
+
+public:
+  using Differences =
+      SmallVector<std::pair<const FunctionEffect *, bool /*added*/>>;
+
+  FunctionEffectSet() : Impl(nullptr) {}
+
+  void *getOpaqueValue() const { return Impl.getOpaqueValue(); }
+
+  explicit operator bool() const { return !empty(); }
+  bool empty() const {
+    if (Impl.isNull())
+      return true;
+    if (const UniquedAndSortedFX *Vec =
+            dyn_cast_if_present<const UniquedAndSortedFX *>(Impl))
+      return Vec->empty();
+    return false;
+  }
+  size_t size() const {
+    if (empty())
+      return 0;
+    if (isa<const FunctionEffect *>(Impl))
+      return 1;
+    return cast<const UniquedAndSortedFX *>(Impl)->size();
+  }
+
+  using iterator = const FunctionEffect *const *;
+
+  iterator begin() const {
+    if (isa<const FunctionEffect *>(Impl))
+      return Impl.getAddrOfPtr1();
+    return cast<const UniquedAndSortedFX *>(Impl)->begin();
+  }
+
+  iterator end() const {
+    if (isa<const FunctionEffect *>(Impl))
+      return begin() + (Impl.isNull() ? 0 : 1);
+    return cast<const UniquedAndSortedFX *>(Impl)->end();
+  }
+
+  ArrayRef<const FunctionEffect *> items() const { return {begin(), end()}; }
+
+  // Since iterators are non-trivial and sets are very often empty,
+  // encourage short-circuiting loops for the empty set.
+  // void for_each(llvm::function_ref<void(const FunctionEffect*)> func) const;
+
+  bool operator==(const FunctionEffectSet &other) const {
+    return Impl == other.Impl;
+  }
+  bool operator!=(const FunctionEffectSet &other) const {
+    return Impl != other.Impl;
+  }
+  bool operator<(const FunctionEffectSet &other) const;
+
+  void dump(llvm::raw_ostream &OS) const;
+
+  /// Factory functions: return instances with uniqued implementations.
+  static FunctionEffectSet create(const FunctionEffect &single) {
+    return FunctionEffectSet{&single};
+  }
+  static FunctionEffectSet create(llvm::ArrayRef<const FunctionEffect *> items);
+
+  /// Union. Caller should check for incompatible effects.
+  FunctionEffectSet operator|(const FunctionEffectSet &rhs) const;
+  /// Intersection.
+  MutableFunctionEffectSet operator&(const FunctionEffectSet &rhs) const;
+  /// Difference.
+  MutableFunctionEffectSet operator-(const FunctionEffectSet &rhs) const;
+
+  /// Caller should short-circuit by checking for equality first.
+  static Differences differences(const FunctionEffectSet &Old,
+                                 const FunctionEffectSet &New);
+
+  /// Extract the effects from a Type if it is a BlockType or FunctionProtoType,
+  /// or pointer to one.
+  static FunctionEffectSet get(const Type &TyRef);
+};
+
 /// Represents a prototype with parameter type info, e.g.
 /// 'int foo(int)' or 'int foo(void)'.  'void' is represented as having no
 /// parameters, not as having a single void parameter. Such a type can have
@@ -4195,7 +4324,8 @@ class FunctionProtoType final
           FunctionProtoType, QualType, SourceLocation,
           FunctionType::FunctionTypeExtraBitfields,
           FunctionType::FunctionTypeArmAttributes, FunctionType::ExceptionType,
-          Expr *, FunctionDecl *, FunctionType::ExtParameterInfo, Qualifiers> {
+          Expr *, FunctionDecl *, FunctionType::ExtParameterInfo,
+          FunctionEffectSet, Qualifiers> {
   friend class ASTContext; // ASTContext creates these.
   friend TrailingObjects;
 
@@ -4226,6 +4356,9 @@ class FunctionProtoType final
   //   an ExtParameterInfo for each of the parameters. Present if and
   //   only if hasExtParameterInfos() is true.
   //
+  // * Optionally, a FunctionEffectSet. Present if and only if
+  //   hasFunctionEffects() is true.
+  //
   // * Optionally a Qualifiers object to represent extra qualifiers that can't
   //   be represented by FunctionTypeBitfields.FastTypeQuals. Present if and only
   //   if hasExtQualifiers() is true.
@@ -4284,6 +4417,7 @@ class FunctionProtoType final
     ExceptionSpecInfo ExceptionSpec;
     const ExtParameterInfo *ExtParameterInfos = nullptr;
     SourceLocation EllipsisLoc;
+    FunctionEffectSet FunctionEffects;
 
     ExtProtoInfo()
         : Variadic(false), HasTrailingReturn(false),
@@ -4301,7 +4435,8 @@ class FunctionProtoType final
 
     bool requiresFunctionProtoTypeExtraBitfields() const {
       return ExceptionSpec.Type == EST_Dynamic ||
-             requiresFunctionProtoTypeArmAttributes();
+             requiresFunctionProtoTypeArmAttributes() ||
+             FunctionEffects;
     }
 
     bool requiresFunctionProtoTypeArmAttributes() const {
@@ -4349,6 +4484,10 @@ class FunctionProtoType final
     return hasExtParameterInfos() ? getNumParams() : 0;
   }
 
+  unsigned numTrailingObjects(OverloadToken<FunctionEffectSet>) const {
+    return hasFunctionEffects();
+  }
+
   /// Determine whether there are any argument types that
   /// contain an unexpanded parameter pack.
   static bool containsAnyUnexpandedParameterPack(const QualType *ArgArray,
@@ -4450,6 +4589,7 @@ class FunctionProtoType final
     EPI.RefQualifier = getRefQualifier();
     EPI.ExtParameterInfos = getExtParameterInfosOrNull();
     EPI.AArch64SMEAttributes = getAArch64SMEAttributes();
+    EPI.FunctionEffects = getFunctionEffects();
     return EPI;
   }
 
@@ -4661,6 +4801,18 @@ class FunctionProtoType final
     return false;
   }
 
+  bool hasFunctionEffects() const {
+    if (!hasExtraBitfields())
+      return false;
+    return getTrailingObjects<FunctionTypeExtraBitfields>()->HasFunctionEffects;
+  }
+
+  FunctionEffectSet getFunctionEffects() const {
+    if (hasFunctionEffects())
+      return *getTrailingObjects<FunctionEffectSet>();
+    return {};
+  }
+
   bool isSugared() const { return false; }
   QualType desugar() const { return QualType(this, 0); }
 
@@ -7754,6 +7906,145 @@ QualType DecayedType::getPointeeType() const {
 void FixedPointValueToString(SmallVectorImpl<char> &Str, llvm::APSInt Val,
                              unsigned Scale);
 
+// ------------------------------------------------------------------------------
+
+// TODO: Should FunctionEffect be located elsewhere, where Decl is not
+// forward-declared?
+class Decl;
+class CXXMethodDecl;
+
+/// Represents an abstract function effect.
+class FunctionEffect {
+public:
+  enum EffectType {
+    kGeneric,
+    kNoLockTrue,
+    kNoAllocTrue,
+  };
+
+  /// Flags describing behaviors of the effect.
+  using Flags = unsigned;
+  enum FlagBit : unsigned {
+    // Some effects require verification, e.g. nolock(true); others might not?
+    // (no example yet)
+    kRequiresVerification = 0x1,
+
+    // Does this effect want to verify all function calls originating in
+    // functions having this effect?
+    kVerifyCalls = 0x2,
+
+    // Can verification inspect callees' implementations? (e.g. nolock: yes,
+    // tcb+types: no)
+    kInferrableOnCallees = 0x4,
+
+    // Language constructs which effects can diagnose as disallowed.
+    kExcludeThrow = 0x8,
+    kExcludeCatch = 0x10,
+    kExcludeObjCMessageSend = 0x20,
+    kExcludeStaticLocalVars = 0x40,
+    kExcludeThreadLocalVars = 0x80
+  };
+
+private:
+  const EffectType Type_;
+  const Flags Flags_;
+  const char *Name;
+
+public:
+  using CalleeDeclOrType =
+      llvm::PointerUnion<const Decl *, const FunctionProtoType *>;
+
+  FunctionEffect(EffectType T, Flags F, const char *Name)
+      : Type_(T), Flags_(F), Name(Name) {}
+  virtual ~FunctionEffect();
+
+  /// The type of the effect.
+  EffectType type() const { return Type_; }
+
+  /// Flags describing behaviors of the effect.
+  Flags flags() const { return Flags_; }
+
+  /// The description printed in diagnostics, e.g. 'nolock'.
+  StringRef name() const { return Name; }
+
+  /// The description used by TypePrinter, e.g. __attribute__((clang_nolock))
+  virtual std::string attribute() const = 0;
+
+  /// Return true if adding or removing the effect as part of a type conversion
+  /// should generate a diagnostic.
+  virtual bool diagnoseConversion(bool Adding, QualType OldType,
+                                  FunctionEffectSet OldFX, QualType NewType,
+                                  FunctionEffectSet NewFX) const;
+
+  /// Return true if adding or removing the effect in a redeclaration should
+  /// generate a diagnostic.
+  virtual bool diagnoseRedeclaration(bool Adding,
+                                     const FunctionDecl &OldFunction,
+                                     FunctionEffectSet OldFX,
+                                     const FunctionDecl &NewFunction,
+                                     FunctionEffectSet NewFX) const;
+
+  /// Return true if adding or removing the effect in a C++ virtual method 
+  /// override should generate a diagnostic.
+  virtual bool diagnoseMethodOverride(bool Adding,
+                                      const CXXMethodDecl &OldMethod,
+                                      FunctionEffectSet OldFX,
+                                      const CXXMethodDecl &NewMethod,
+                                      FunctionEffectSet NewFX) const;
+
+  /// Return true if the effect is allowed to be inferred on the specified Decl
+  /// (may be a FunctionDecl or BlockDecl). Only used if the effect has
+  /// kInferrableOnCallees flag set. Example: This allows nolock(false) to 
+  /// prevent inference for the function.
+  virtual bool canInferOnDecl(const Decl *Caller,
+                              FunctionEffectSet CallerFX) const;
+
+  // Called if kVerifyCalls flag is set; return false for success. When true is
+  // returned for a direct call, then the kInferrableOnCallees flag may trigger
+  // inference rather than an immediate diagnostic. Caller should be assumed to
+  // have the effect (it may not have it explicitly when inferring).
+  virtual bool diagnoseFunctionCall(bool Direct, const Decl *Caller,
+                                    FunctionEffectSet CallerFX,
+                                    CalleeDeclOrType Callee,
+                                    FunctionEffectSet CalleeFX) const;
+};
+
+/// FunctionEffect subclass for nolock and noalloc (whose behaviors are close
+/// to identical).
+class NoLockNoAllocEffect : public FunctionEffect {
+  bool isNoLock() const { return type() == kNoLockTrue; }
+
+public:
+  static const NoLockNoAllocEffect &nolock_instance();
+  static const NoLockNoAllocEffect &noalloc_instance();
+
+  NoLockNoAllocEffect(EffectType Type, const char *Name);
+  ~NoLockNoAllocEffect() override;
+
+  std::string attribute() const override;
+
+  bool diagnoseConversion(bool Adding, QualType OldType,
+                          FunctionEffectSet OldFX, QualType NewType,
+                          FunctionEffectSet NewFX) const override;
+
+  bool diagnoseRedeclaration(bool Adding, const FunctionDecl &OldFunction,
+                             FunctionEffectSet OldFX,
+                             const FunctionDecl &NewFunction,
+                             FunctionEffectSet NewFX) const override;
+
+  bool diagnoseMethodOverride(bool Adding, const CXXMethodDecl &OldMethod,
+                              FunctionEffectSet OldFX,
+                              const CXXMethodDecl &NewMethod,
+                              FunctionEffectSet NewFX) const override;
+
+  bool canInferOnDecl(const Decl *Caller,
+                      FunctionEffectSet CallerFX) const override;
+
+  bool diagnoseFunctionCall(bool Direct, const Decl *Caller,
+                            FunctionEffectSet CallerFX, CalleeDeclOrType Callee,
+                            FunctionEffectSet CalleeFX) const override;
+};
+
 } // namespace clang
 
 #endif // LLVM_CLANG_AST_TYPE_H
diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td
index 0ba172a4035fdb..1d5d8f696977e7 100644
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -326,6 +326,10 @@ let Class = FunctionProtoType in {
   def : Property<"AArch64SMEAttributes", UInt32> {
     let Read = [{ node->getAArch64SMEAttributes() }];
   }
+  /* TODO: How to serialize FunctionEffect / FunctionEffectSet?
+  def : Property<"functionEffects", FunctionEffectSet> {
+    let Read = [{ node->getFunctionEffects() }];
+  }*/
 
   def : Creator<[{
     auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm,
@@ -342,6 +346,7 @@ let Class = FunctionProtoType in {
     epi.ExtParameterInfos =
       extParameterInfo.empty() ? nullptr : extParameterInfo.data();
     epi.AArch64SMEAttributes = AArch64SMEAttributes;
+    //epi.FunctionEffects = functionEffects;
     return ctx.getFunctionType(returnType, parameters, epi);
   }]>;
 }
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index fd7970d0451acd..4033b9efb86f39 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -1402,6 +1402,29 @@ def CXX11NoReturn : InheritableAttr {
   let Documentation = [CXX11NoReturnDocs];
 }
 
+def NoLock : DeclOrTypeAttr {
+  let Spellings = [CXX11<"clang", "nolock">,
+                   C23<"clang", "nolock">,
+                   GNU<"clang_nolock">];
+
+  // Subjects - not needed?
+  //let Subjects = SubjectList<[FunctionLike, Block, TypedefName], ErrorDiag>;
+  let Args = [DefaultBoolArgument<"Cond", /*default*/1>];
+  let HasCustomParsing = 0;
+  let Documentation = [NoLockNoAllocDocs];
+}
+
+def NoAlloc : DeclOrTypeAttr {
+  let Spellings = [CXX11<"clang", "noalloc">,
+                   C23<"clang", "noalloc">,
+                   GNU<"clang_noalloc">];
+  // Subjects - not needed?
+  //let Subjects = SubjectList<[FunctionLike, Block, TypedefName], ErrorDiag>;
+  let Args = [DefaultBoolArgument<"Cond", /*default*/1>];
+  let HasCustomParsing = 0;
+  let Documentation = [NoLockNoAllocDocs];
+}
+
 // Similar to CUDA, OpenCL attributes do not receive a [[]] spelling because
 // the specification does not expose them with one currently.
 def OpenCLKernel : InheritableAttr {
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 2c07cd09b0d5b7..bb897c708ebbea 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -7973,3 +7973,20 @@ requirement:
   }
   }];
 }
+
+def NoLockNoAllocDocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+The ``nolock`` and ``noalloc`` attributes can be attached to functions, blocks, 
+function pointers, lambdas, and member functions. The attributes identify code
+which must not allocate memory or lock, and the compiler uses the attributes to
+verify these requirements.
+
+Like ``noexcept``, ``nolock`` and ``noalloc`` have an optional argument, a
+compile-time constant boolean expression. By default, the argument is true, so
+``[[clang::nolock(true)]]`` is equivalent to ``[[clang::nolock]]``, and declares
+the function type as never locking.
+
+TODO: how much of the RFC to include here? Make it a separate page?
+  }];
+}
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 3f14167d6b8469..0d6e9f0289f6bc 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1507,3 +1507,7 @@ def ReadOnlyPlacementChecks : DiagGroup<"read-only-types">;
 // Warnings and fixes to support the "safe buffers" programming model.
 def UnsafeBufferUsageInContainer : DiagGroup<"unsafe-buffer-usage-in-container">;
 def UnsafeBufferUsage : DiagGroup<"unsafe-buffer-usage", [UnsafeBufferUsageInContainer]>;
+
+// Warnings and notes related to the function effects system underlying
+// the nolock and noalloc attributes.
+def FunctionEffects : DiagGroup<"function-effects">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c54105507753eb..df553a47a8dbe7 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10778,6 +10778,101 @@ def warn_imp_cast_drops_unaligned : Warning<
   "implicit cast from type %0 to type %1 drops __unaligned qualifier">,
   InGroup<DiagGroup<"unaligned-qualifier-implicit-cast">>;
 
+def warn_func_effect_allocates : Warning<
+  "'%0' function '%1' must not allocate or deallocate memory">,
+  InGroup<FunctionEffects>;
+
+def note_func_effect_allocates : Note<
+  "'%1' cannot be inferred '%0' because it allocates/deallocates memory">;
+
+def warn_func_effect_throws_or_catches : Warning<
+  "'%0' function '%1' must not throw or catch exceptions">,
+  InGroup<FunctionEffects>;
+
+def note_func_effect_throws_or_catches : Note<
+  "'%1' cannot be inferred '%0' because it throws or catches exceptions">;
+
+def warn_func_effect_has_static_local : Warning<
+  "'%0' function '%1' must not have sta...
[truncated]

``````````

</details>


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


More information about the cfe-commits mailing list