[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)

Doug Wyatt via cfe-commits cfe-commits at lists.llvm.org
Thu Apr 11 14:34:55 PDT 2024


================
@@ -4429,6 +4433,210 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode {
   }
 };
 
+// ------------------------------------------------------------------------------
+
+class Decl;
+class CXXMethodDecl;
+class FunctionEffectSet;
+
+/// Represents an abstract function effect, using just an enumeration describing
+/// its type. Encapsulates its semantic behaviors.
+class FunctionEffect {
+public:
+  /// Identifies the particular effect.
+  enum class Kind : uint8_t {
+    None,
+    NonBlocking,
+    NonAllocating,
+  };
+
+  /// Flags describing some behaviors of the effect.
+  using Flags = unsigned;
+  enum FlagBit : Flags {
+    // Can verification inspect callees' implementations? (e.g. nonblocking:
+    // yes, tcb+types: no)
+    FE_InferrableOnCallees = 0x1,
+
+    // Language constructs which effects can diagnose as disallowed.
+    FE_ExcludeThrow = 0x2,
+    FE_ExcludeCatch = 0x4,
+    FE_ExcludeObjCMessageSend = 0x8,
+    FE_ExcludeStaticLocalVars = 0x10,
+    FE_ExcludeThreadLocalVars = 0x20
+  };
+
+  /// Describes the result of effects differing between a base class's virtual
+  /// method and an overriding method in a subclass.
+  enum class OverrideResult {
+    Ignore,
+    Warn,
+    Merge // Base method's effects are merged with those of the override.
+  };
+
+private:
+  Kind FKind;
+
+  // Expansion: for hypothetical TCB+types, there could be one Kind for TCB,
+  // then ~16(?) bits "SubKind" to map to a specific named TCB. SubKind would
+  // be considered for uniqueness.
+
+public:
+  FunctionEffect() : FKind(Kind::None) {}
+
+  explicit FunctionEffect(Kind T) : FKind(T) {}
+
+  /// The kind of the effect.
+  Kind kind() const { return FKind; }
+
+  /// Return an opaque integer, as a serializable representation.
+  uint32_t getAsOpaqueValue() const { return llvm::to_underlying(FKind); }
+
+  /// Construct from a serialized representation.
+  static FunctionEffect getFromOpaqueValue(uint32_t V) {
+    return FunctionEffect(static_cast<Kind>(V));
+  }
+
+  /// Flags describing some behaviors of the effect.
+  Flags flags() const {
+    switch (FKind) {
+    case Kind::NonBlocking:
+      return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch |
+             FE_ExcludeObjCMessageSend | FE_ExcludeStaticLocalVars |
+             FE_ExcludeThreadLocalVars;
+    case Kind::NonAllocating:
+      // Same as NonBlocking, except without FE_ExcludeStaticLocalVars
+      return FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch |
+             FE_ExcludeObjCMessageSend | FE_ExcludeThreadLocalVars;
+    case Kind::None:
+      break;
+    }
+    llvm_unreachable("unknown effect kind");
+  }
+
+  /// The description printed in diagnostics, e.g. 'nonblocking'.
+  StringRef name() const;
+
+  /// Return true if adding or removing the effect as part of a type conversion
+  /// should generate a diagnostic.
+  bool shouldDiagnoseConversion(bool Adding, QualType OldType,
+                                const FunctionEffectSet &OldFX,
+                                QualType NewType,
+                                const FunctionEffectSet &NewFX) const;
+
+  /// Return true if adding or removing the effect in a redeclaration should
+  /// generate a diagnostic.
+  bool shouldDiagnoseRedeclaration(bool Adding, const FunctionDecl &OldFunction,
+                                   const FunctionEffectSet &OldFX,
+                                   const FunctionDecl &NewFunction,
+                                   const FunctionEffectSet &NewFX) const;
+
+  /// Return true if adding or removing the effect in a C++ virtual method
+  /// override should generate a diagnostic.
+  OverrideResult
+  shouldDiagnoseMethodOverride(bool Adding, const CXXMethodDecl &OldMethod,
+                               const FunctionEffectSet &OldFX,
+                               const CXXMethodDecl &NewMethod,
+                               const FunctionEffectSet &NewFX) const;
+
+  /// Return true if the effect is allowed to be inferred on the callee,
+  /// which is either a FunctionDecl or BlockDecl.
+  /// This is only used if the effect has FE_InferrableOnCallees flag set.
+  /// Example: This allows nonblocking(false) to prevent inference for the
+  /// function.
+  bool canInferOnFunction(const Decl &Callee) const;
+
+  // Return false for success. When true is returned for a direct call, then the
+  // FE_InferrableOnCallees 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).
+  bool shouldDiagnoseFunctionCall(bool Direct,
+                                  const FunctionEffectSet &CalleeFX) const;
+
+  friend bool operator==(const FunctionEffect &LHS, const FunctionEffect &RHS) {
+    return LHS.FKind == RHS.FKind;
+  }
+  friend bool operator!=(const FunctionEffect &LHS, const FunctionEffect &RHS) {
+    return LHS.FKind != RHS.FKind;
+  }
+  friend bool operator<(const FunctionEffect &LHS, const FunctionEffect &RHS) {
+    return LHS.FKind < RHS.FKind;
+  }
+};
+
+/// A value type, representing a set of FunctionEffects. To reduce superfluous
+/// retain/release, however, prefer to pass by reference.
+class FunctionEffectSet {
----------------
dougsonos wrote:

This seems less overly elaborate now, without the uniqued sets and without a separate `MutableFunctionEffectSet`.

I'm trying to decide whether `FunctionEffectSet` should hold the "computed effects" too, or whether `FunctionProtoType` should hold that separately.

In any case, something other than `FunctionProtoType` needs to handle merging the computed effects (once their expressions are non-dependent), because that operation would use `Sema::PerformContextuallyConvertToBool()`, and `FunctionProtoType` doesn't have a `Sema&` or precedent for using one.

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


More information about the cfe-commits mailing list