================ @@ -4429,6 +4433,218 @@ class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { } }; +// ------------------------------------------------------------------------------ + +// TODO: Should FunctionEffect be located elsewhere, where Decl is not +// forward-declared? +class Decl; +class CXXMethodDecl; +class FunctionEffectSet; +class TypeSourceInfo; + +/// Represents an abstract function effect. +class FunctionEffect { +public: + /// Identifies the particular type of effect. + enum class Type { + None = 0, + NonBlocking, + NonAllocating, + }; + + /// Flags describing behaviors of the effect. + // (Why not a struct with bitfields? There's one function that would like to + // test a caller-specified bit. There are some potential optimizations that + // would OR together the bits of multiple effects.) + using Flags = unsigned; + enum FlagBit : unsigned { + // 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, + Propagate // Base method's effects are merged with those of the override. + }; + +private: + // For uniqueness, currently only Type_ is significant. + + LLVM_PREFERRED_TYPE(Type) + unsigned Type_ : 2; + Flags Flags_ : 8; // A constant function of Type but cached here. + + // Expansion: for hypothetical TCB+types, there could be one Type for TCB, + // then ~16(?) bits "Subtype" to map to a specific named TCB. Subtype would + // be considered for uniqueness. + + // Since this struct is serialized as if it were a uint32_t, it's important + // to pad and explicitly zero the extra bits. + [[maybe_unused]] unsigned Padding : 22; + +public: + using CalleeDeclOrType = + llvm::PointerUnion<const Decl *, const FunctionProtoType *>; + + FunctionEffect() : Type_(unsigned(Type::None)), Flags_(0), Padding(0) {} + + explicit FunctionEffect(Type T); + + /// The type of the effect. + Type type() const { return Type(Type_); } + + /// Flags describing behaviors of the effect. + Flags flags() const { return Flags_; } + + /// The description printed in diagnostics, e.g. 'nonblocking'. + StringRef name() const; + + /// A hashable representation. + uint32_t opaqueRepr() const { return Type_ | (Flags_ << 2u); } + + /// Return true if adding or removing the effect as part of a type conversion + /// should generate a diagnostic. + 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. + 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. + OverrideResult 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 a Decl of the + /// specified type (generally a FunctionProtoType but TypeSourceInfo is + /// provided so any AttributedType sugar can be examined). TSI can be null + /// on an implicit function like a default constructor. + /// + /// 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(QualType QT, const TypeSourceInfo *FType) 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 diagnoseFunctionCall(bool Direct, FunctionEffectSet CalleeFX) const; + + friend bool operator==(const FunctionEffect &LHS, const FunctionEffect &RHS) { + return LHS.Type_ == RHS.Type_; + } + friend bool operator!=(const FunctionEffect &LHS, const FunctionEffect &RHS) { + return LHS.Type_ != RHS.Type_; + } + friend bool operator<(const FunctionEffect &LHS, const FunctionEffect &RHS) { + return LHS.Type_ < RHS.Type_; + } +}; + +// 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<FunctionEffect, 4> { +public: + using SmallVector::insert; + using SmallVector::SmallVector; + + MutableFunctionEffectSet(const FunctionEffect &Effect); + using Base = SmallVector<FunctionEffect, 4>; + + /// Maintains order/uniquenesss. + void insert(const FunctionEffect &Effect); + + MutableFunctionEffectSet &operator|=(FunctionEffectSet RHS); + + operator llvm::ArrayRef<FunctionEffect>() const { return {data(), size()}; } +}; + +/// A constant, uniqued set of FunctionEffect instances. +// These sets will tend to be very small (0-1 elements), so represent them as +// sorted spans, which are compatible with the STL set algorithms. +class FunctionEffectSet { +private: + friend class ASTContext; // so it can call the private constructor + + explicit FunctionEffectSet(llvm::ArrayRef<FunctionEffect> Array) + : Impl(Array) {} + + // Points to a separately allocated array, uniqued. + llvm::ArrayRef<FunctionEffect> Impl; + +public: + using Differences = SmallVector<std::pair<FunctionEffect, /*added=*/bool>>; + + FunctionEffectSet() = default; + + const void *getOpaqueValue() const { return Impl.data(); } + llvm::ArrayRef<uint32_t> serializable() const { + static_assert(sizeof(FunctionEffect) == sizeof(uint32_t)); + const uint32_t *ptr = reinterpret_cast<const uint32_t *>(Impl.data()); + return {const_cast<uint32_t *>(ptr), Impl.size()}; ---------------- dougsonos wrote:
I guess the UB could be resolved by having the guts of FunctionEffect be a union of a uint32_t and a struct containing the bits. But let's resolve the higher-level issues first... 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