================ @@ -5028,3 +5050,376 @@ void AutoType::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) { Profile(ID, Context, getDeducedType(), getKeyword(), isDependentType(), getTypeConstraintConcept(), getTypeConstraintArguments()); } + +FunctionEffect::Kind FunctionEffect::oppositeKind() const { + switch (kind()) { + case Kind::NonBlocking: + return Kind::Blocking; + case Kind::Blocking: + return Kind::NonBlocking; + case Kind::NonAllocating: + return Kind::Allocating; + case Kind::Allocating: + return Kind::NonAllocating; + case Kind::None: + return Kind::None; + } + llvm_unreachable("unknown effect kind"); +} + +StringRef FunctionEffect::name() const { + switch (kind()) { + case Kind::NonBlocking: + return "nonblocking"; + case Kind::NonAllocating: + return "nonallocating"; + case Kind::Blocking: + return "blocking"; + case Kind::Allocating: + return "allocating"; + case Kind::None: + break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffectDiff::shouldDiagnoseConversion( + QualType SrcType, const FunctionEffectsRef &SrcFX, QualType DstType, + const FunctionEffectsRef &DstFX) const { + + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: + // nonallocating can't be added (spoofed) during a conversion, unless we + // have nonblocking + if (DiffKind == Kind::Added) { + for (const auto &CFE : SrcFX) { + if (CFE.Effect.kind() == FunctionEffect::Kind::NonBlocking) + return false; + } + } + [[fallthrough]]; + case FunctionEffect::Kind::NonBlocking: + // nonblocking can't be added (spoofed) during a conversion. + switch (DiffKind) { + case Kind::Added: + return true; + case Kind::Removed: + return false; + case Kind::ConditionMismatch: + return true; // TODO: ??? + } + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: + return false; + case FunctionEffect::Kind::None: + break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffectDiff::shouldDiagnoseRedeclaration( + const FunctionDecl &OldFunction, const FunctionEffectsRef &OldFX, + const FunctionDecl &NewFunction, const FunctionEffectsRef &NewFX) const { + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: + case FunctionEffect::Kind::NonBlocking: + // nonblocking/nonallocating can't be removed in a redeclaration + switch (DiffKind) { + case Kind::Added: + return false; // No diagnostic. + case Kind::Removed: + return true; // Issue diagnostic + case Kind::ConditionMismatch: + // All these forms of mismatches are diagnosed. + return true; + } + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: + return false; + case FunctionEffect::Kind::None: + break; + } + llvm_unreachable("unknown effect kind"); +} + +FunctionEffectDiff::OverrideResult +FunctionEffectDiff::shouldDiagnoseMethodOverride( + const CXXMethodDecl &OldMethod, const FunctionEffectsRef &OldFX, + const CXXMethodDecl &NewMethod, const FunctionEffectsRef &NewFX) const { + switch (EffectKind) { + case FunctionEffect::Kind::NonAllocating: + case FunctionEffect::Kind::NonBlocking: + switch (DiffKind) { + + // If added on an override, that's fine and not diagnosed. + case Kind::Added: + return OverrideResult::NoAction; + + // If missing from an override (removed), propagate from base to derived. + case Kind::Removed: + return OverrideResult::Merge; + + // If there's a mismatch involving the effect's polarity or condition, + // issue a warning. + case Kind::ConditionMismatch: + return OverrideResult::Warn; + } + + case FunctionEffect::Kind::Blocking: + case FunctionEffect::Kind::Allocating: + return OverrideResult::NoAction; + + case FunctionEffect::Kind::None: + break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffect::canInferOnFunction(const Decl &Callee) const { + switch (kind()) { + case Kind::NonAllocating: + case Kind::NonBlocking: { + FunctionEffectsRef CalleeFX; + if (auto *FD = Callee.getAsFunction()) + CalleeFX = FD->getFunctionEffects(); + else if (auto *BD = dyn_cast<BlockDecl>(&Callee)) + CalleeFX = BD->getFunctionEffects(); + else + return false; + for (const FunctionEffectWithCondition &CalleeEC : CalleeFX) { + // nonblocking/nonallocating cannot call allocating + if (CalleeEC.Effect.kind() == Kind::Allocating) + return false; + // nonblocking cannot call blocking + if (kind() == Kind::NonBlocking && + CalleeEC.Effect.kind() == Kind::Blocking) + return false; + } + } + return true; + + case Kind::Allocating: + case Kind::Blocking: + return false; + + case Kind::None: + break; + } + llvm_unreachable("unknown effect kind"); +} + +bool FunctionEffect::shouldDiagnoseFunctionCall( + bool Direct, ArrayRef<FunctionEffect> CalleeFX) const { + switch (kind()) { + case Kind::NonAllocating: + case Kind::NonBlocking: { + const Kind CallerKind = kind(); + for (const auto &Effect : CalleeFX) { + const Kind EK = Effect.kind(); + // Does callee have same or stronger constraint? + if (EK == CallerKind || + (CallerKind == Kind::NonAllocating && EK == Kind::NonBlocking)) { + return false; // no diagnostic + } + } + return true; // warning + } + case Kind::Allocating: + case Kind::Blocking: + return false; + case Kind::None: + break; + } + llvm_unreachable("unknown effect kind"); +} + +// ===== + +void FunctionEffectsRef::Profile(llvm::FoldingSetNodeID &ID) const { + const bool HasConds = !Conditions.empty(); + + ID.AddInteger(size() | (HasConds << 31u)); + for (unsigned Idx = 0, Count = Effects.size(); Idx != Count; ++Idx) { + ID.AddInteger(Effects[Idx].toOpaqueInt32()); + if (HasConds) + ID.AddPointer(Conditions[Idx].expr()); + } +} + +void FunctionEffectSet::insert(FunctionEffect Effect, Expr *Cond) { + // lower_bound would be overkill + unsigned Idx = 0; + for (unsigned Count = Effects.size(); Idx != Count; ++Idx) { + const auto &IterEffect = Effects[Idx]; + if (IterEffect.kind() == Effect.kind()) { + // It's possible here to have incompatible combinations of polarity + // (asserted/denied) and condition; for now, we keep whichever came + // though this should be improved. + return; + } + if (Effect.kind() < IterEffect.kind()) + break; + } + + if (Cond) { + if (Conditions.empty() && !Effects.empty()) + Conditions.resize(Effects.size()); + Conditions.insert(Conditions.begin() + Idx, Cond); + } + Effects.insert(Effects.begin() + Idx, Effect); +} + +void FunctionEffectSet::insert(const FunctionEffectsRef &Set) { + for (const auto &Item : Set) + insert(Item.Effect, Item.Cond.expr()); +} + +void FunctionEffectSet::insertIgnoringConditions( + const FunctionEffectsRef &Set) { + for (const auto &Item : Set) + insert(Item.Effect, nullptr); +} + +void FunctionEffectSet::replaceItem(unsigned Idx, + const FunctionEffectWithCondition &Item) { + assert(Idx < Conditions.size()); + Effects[Idx] = Item.Effect; + Conditions[Idx] = Item.Cond; + + // Maintain invariant: If all conditions are null, the vector should be empty. + if (std::all_of(Conditions.begin(), Conditions.end(), + [](const FunctionEffectCondition &C) { + return C.expr() == nullptr; + })) { + Conditions.clear(); + } +} + +void FunctionEffectSet::erase(unsigned Idx) { + assert(Idx < Effects.size()); + Effects.erase(Effects.begin() + Idx); + if (!Conditions.empty()) { + assert(Idx < Conditions.size()); + Conditions.erase(Conditions.begin() + Idx); + } +} + +FunctionEffectSet FunctionEffectSet::getUnion(FunctionEffectsRef LHS, + FunctionEffectsRef RHS) { + // Optimize for either of the two sets being empty (very common). + if (LHS.empty()) + return FunctionEffectSet(RHS); + + FunctionEffectSet Result(LHS); + Result.insert(RHS); + return Result; +} + +FunctionEffectSet::Differences +FunctionEffectSet::differences(const FunctionEffectsRef &Old, + const FunctionEffectsRef &New) { + + FunctionEffectSet::Differences Result; + + FunctionEffectsRef::iterator POld = Old.begin(); + FunctionEffectsRef::iterator OldEnd = Old.end(); + FunctionEffectsRef::iterator PNew = New.begin(); + FunctionEffectsRef::iterator NewEnd = New.end(); + + while (true) { + int cmp = 0; + if (POld == OldEnd) { + if (PNew == NewEnd) + break; + cmp = 1; + } else if (PNew == NewEnd) + cmp = -1; + else { + FunctionEffectWithCondition Old = *POld; + FunctionEffectWithCondition New = *PNew; + if (Old.Effect.kind() < New.Effect.kind()) + cmp = -1; + else if (New.Effect.kind() < Old.Effect.kind()) + cmp = 1; + else { + cmp = 0; + if (Old.Cond.expr() != New.Cond.expr()) { + // TODO: Cases where the expressions are equivalent but + // don't have the same identity. + Result.push_back(FunctionEffectDiff{ + Old.Effect.kind(), FunctionEffectDiff::Kind::ConditionMismatch, + Old, New}); + } + } + } + + if (cmp < 0) { + // removal + FunctionEffectWithCondition Old = *POld; + Result.push_back(FunctionEffectDiff{ + Old.Effect.kind(), FunctionEffectDiff::Kind::Removed, Old, {}}); + ++POld; + } else if (cmp > 0) { + // addition + FunctionEffectWithCondition New = *PNew; + Result.push_back(FunctionEffectDiff{ + New.Effect.kind(), FunctionEffectDiff::Kind::Added, {}, New}); + ++PNew; + } else { + ++POld; + ++PNew; + } + } + return Result; +} + +LLVM_DUMP_METHOD void FunctionEffectsRef::dump(llvm::raw_ostream &OS) const { + OS << "Effects{"; + bool First = true; + for (const auto &CFE : *this) { + if (!First) + OS << ", "; + else + First = false; + OS << CFE.Effect.name(); + if (Expr *E = CFE.Cond.expr()) { + OS << '('; + E->dump(); + OS << ')'; + } + } + OS << "}"; +} + +LLVM_DUMP_METHOD void FunctionEffectSet::dump(llvm::raw_ostream &OS) const { + FunctionEffectsRef(*this).dump(OS); +} + +// TODO: inline? +FunctionEffectsRef FunctionEffectsRef::get(QualType QT) { + if (QT->isReferenceType()) + QT = QT.getNonReferenceType(); + if (QT->isPointerType()) + QT = QT->getPointeeType(); + + // TODO: Why aren't these included in isPointerType()? + if (const auto *BT = QT->getAs<BlockPointerType>()) { + QT = BT->getPointeeType(); + } else if (const auto *MP = QT->getAs<MemberPointerType>()) { + if (MP->isMemberFunctionPointer()) { + QT = MP->getPointeeType(); + } + } ---------------- dougsonos wrote:
Aha, thanks! 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