================ @@ -5016,3 +5024,254 @@ void AutoType::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) { Profile(ID, Context, getDeducedType(), getKeyword(), isDependentType(), getTypeConstraintConcept(), getTypeConstraintArguments()); } + +FunctionEffect::FunctionEffect(Type T) + : Type_(static_cast<unsigned>(T)), Flags_(0), Padding(0) { + switch (T) { + case Type::NonBlocking: + Flags_ = FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeStaticLocalVars | + FE_ExcludeThreadLocalVars; + break; + + case Type::NonAllocating: + // Same as NonBlocking, except without FE_ExcludeStaticLocalVars + Flags_ = FE_InferrableOnCallees | FE_ExcludeThrow | FE_ExcludeCatch | + FE_ExcludeObjCMessageSend | FE_ExcludeThreadLocalVars; + break; + default: + break; + } +} + +StringRef FunctionEffect::name() const { + switch (type()) { + default: + return ""; + case Type::NonBlocking: + return "nonblocking"; + case Type::NonAllocating: + return "nonallocating"; + } +} + +bool FunctionEffect::diagnoseConversion(bool Adding, QualType OldType, + FunctionEffectSet OldFX, + QualType NewType, + FunctionEffectSet NewFX) const { + + switch (type()) { + case Type::NonAllocating: + // nonallocating can't be added (spoofed) during a conversion, unless we + // have nonblocking + if (Adding) { + for (const auto &Effect : OldFX) { + if (Effect.type() == Type::NonBlocking) + return false; + } + } + [[fallthrough]]; + case Type::NonBlocking: + // nonblocking can't be added (spoofed) during a conversion + return Adding; + default: + break; + } + return false; +} + +bool FunctionEffect::diagnoseRedeclaration(bool Adding, + const FunctionDecl &OldFunction, + FunctionEffectSet OldFX, + const FunctionDecl &NewFunction, + FunctionEffectSet NewFX) const { + switch (type()) { + case Type::NonAllocating: + case Type::NonBlocking: + // nonblocking/nonallocating can't be removed in a redeclaration + // adding -> false, removing -> true (diagnose) + return !Adding; + default: + break; + } + return false; +} + +FunctionEffect::OverrideResult FunctionEffect::diagnoseMethodOverride( + bool Adding, const CXXMethodDecl &OldMethod, FunctionEffectSet OldFX, + const CXXMethodDecl &NewMethod, FunctionEffectSet NewFX) const { + switch (type()) { + case Type::NonAllocating: + case Type::NonBlocking: + // if added on an override, that's fine and not diagnosed. + // if missing from an override (removed), propagate from base to derived. + return Adding ? OverrideResult::Ignore : OverrideResult::Propagate; + default: + break; + } + return OverrideResult::Ignore; +} + +bool FunctionEffect::canInferOnFunction(QualType QT, + const TypeSourceInfo *FType) const { + switch (type()) { + case Type::NonAllocating: + case Type::NonBlocking: { + // Does the sugar have nonblocking(false) / nonallocating(false) ? + if (QT->hasAttr(type() == Type::NonBlocking ? attr::Kind::Blocking + : attr::Kind::Allocating)) { + return false; + } + + return true; + } + + default: + break; + } + return false; +} + +bool FunctionEffect::diagnoseFunctionCall(bool Direct, + FunctionEffectSet CalleeFX) const { + switch (type()) { + case Type::NonAllocating: + case Type::NonBlocking: { + const Type CallerType = type(); + for (const auto &Effect : CalleeFX) { + const Type ET = Effect.type(); + // Does callee have same or stronger constraint? + if (ET == CallerType || + (CallerType == Type::NonAllocating && ET == Type::NonBlocking)) { + return false; // no diagnostic + } + } + return true; // warning + } + default: + break; + } + return false; +} + +// ===== + +MutableFunctionEffectSet::MutableFunctionEffectSet( + const FunctionEffect &effect) { + push_back(effect); +} + +void MutableFunctionEffectSet::insert(const FunctionEffect &Effect) { + const auto &Iter = std::lower_bound(begin(), end(), Effect); + if (Iter == end() || *Iter != Effect) { + insert(Iter, Effect); + } +} + +MutableFunctionEffectSet & +MutableFunctionEffectSet::operator|=(FunctionEffectSet RHS) { + // TODO: For large RHS sets, use set_union or a custom insert-in-place + for (const auto &Effect : RHS) { + insert(Effect); + } + return *this; +} + +FunctionEffectSet FunctionEffectSet::getUnion(ASTContext &C, + const FunctionEffectSet &LHS, + const FunctionEffectSet &RHS) { + // Optimize for one of the two sets being empty + if (LHS.empty()) + return RHS; + if (RHS.empty()) + return LHS; + + // Optimize the case where the two sets are identical + if (LHS == RHS) + return LHS; + + MutableFunctionEffectSet Vec; + Vec.reserve(LHS.size() + RHS.size()); + std::set_union(LHS.begin(), LHS.end(), RHS.begin(), RHS.end(), + std::back_inserter(Vec)); + // The result of a set operation is an ordered/unique set. + return C.getUniquedFunctionEffectSet(Vec); +} + +MutableFunctionEffectSet +FunctionEffectSet::operator&(const FunctionEffectSet &RHS) const { + const FunctionEffectSet &LHS = *this; + if (LHS.empty() || RHS.empty()) { + return {}; + } + + MutableFunctionEffectSet Vec; + std::set_intersection(LHS.begin(), LHS.end(), RHS.begin(), RHS.end(), + std::back_inserter(Vec)); + // The result of a set operation is an ordered/unique set. + return Vec; +} + +// TODO: inline? +FunctionEffectSet FunctionEffectSet::get(QualType QT) { + if (QT->isReferenceType()) + QT = QT.getNonReferenceType(); + if (QT->isPointerType()) + QT = QT->getPointeeType(); + + if (const auto *BT = QT->getAs<BlockPointerType>()) { + QT = BT->getPointeeType(); + } else if (const auto *MP = QT->getAs<MemberPointerType>()) { + if (MP->isMemberFunctionPointer()) { + QT = MP->getPointeeType(); + } + } + + if (const auto *FPT = QT->getAs<FunctionProtoType>()) + return FPT->getFunctionEffects(); ---------------- Sirraide wrote:
*All of this* seems a bit brittle to me; I think just requiring the caller to instead dig out the `FunctionProtoType` themselves and then call `getFunctionEffects()` would probably be a better idea. Because this function seems to be for the case of ‘I have this random thing that I know contains a function type *somewhere*, but I have no idea where or what it is beyond that’, which seems a bit unlikely—is there any case where we actually need this and can’t just access the `FunctionProtoType` directly? 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