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

Doug Wyatt via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 15 18:18:21 PDT 2024


================
@@ -4429,6 +4434,284 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode {
   }
 };
 
+// ------------------------------------------------------------------------------
+
+class Decl;
+class CXXMethodDecl;
+class FunctionTypeEffectsRef;
+class FunctionTypeEffectSet;
+
+/*
+  TODO: Idea about how to move most of the FunctionEffect business out of
+  Type.h, thus removing these forward declarations.
+
+  - Keep FunctionEffect itself here but make it more minimal. Don't define flags
+  or any behaviors, just the Kind and an accessor.
+  - Keep FunctionEffectCondExpr here.
+  - Make FunctionProtoType and ExtProtoInfo use only ArrayRef<FunctionEffect>
+  and ArrayRef<FunctionEffectCondExpr>.
+  - Somewhere in Sema, define ExtFunctionEffect, which holds a FunctionEffect
+    and has all the behavior-related methods.
+  - There too, define the containers. FunctionTypeEffectsRef can have a
+  constructor or factory method that initializes itself from a
+  FunctionProtoType.
+*/
+
+/// Represents an abstract function effect, using just an enumeration describing
+/// its kind.
+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 = Kind::None;
+
+  // 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() = default;
+
+  explicit FunctionEffect(Kind K) : FKind(K) {}
+
+  /// The kind of the effect.
+  Kind kind() const { return FKind; }
+
+  /// 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 FunctionTypeEffectsRef &OldFX,
+                                QualType NewType,
+                                const FunctionTypeEffectsRef &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 FunctionTypeEffectsRef &OldFX,
+                                   const FunctionDecl &NewFunction,
+                                   const FunctionTypeEffectsRef &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 FunctionTypeEffectsRef &OldFX,
+                               const CXXMethodDecl &NewMethod,
+                               const FunctionTypeEffectsRef &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,
+                                  ArrayRef<FunctionEffect> 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 == RHS);
+  }
+  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;
+  }
+};
+
+/// Wrap a function effect's condition expression in another struct so
+/// that FunctionProtoType's TrailingObjects can treat it separately.
+struct FunctionEffectCondExpr {
+  const Expr *Cond = nullptr; // if null, unconditional
+
----------------
dougsonos wrote:

This struct only exists so that `FunctionProtoType` can have two independent groups of trailing objects of type `Expr*`, one that's already there (exception-related?), and a new one -- wrapped in this struct -- for conditional function effects. This device leaks out into AST serialization and some of the users of the effects but it seemed worth avoiding mushing the two unrelated arrays together.

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


More information about the cfe-commits mailing list