================ @@ -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 { ---------------- Sirraide wrote:
> Now I think that `FunctionProtoType` can just hold trailing `FunctionEffect`s > and `ComputedFunctionEffect`s Yeah, to me, that also looks like it’d be the right approach here. https://github.com/llvm/llvm-project/pull/84983 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits