[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
via cfe-commits
cfe-commits at lists.llvm.org
Sat May 4 09:40:32 PDT 2024
================
@@ -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();
+ }
+ }
----------------
Sirraide wrote:
```suggestion
if (QualType Pointee = QT->getPointeeType())
QT = Pointee;
```
My bad if I’m only just noticing/remembering this now, but `Type::getPointeeType()` already does all of this.
https://github.com/llvm/llvm-project/pull/84983
More information about the cfe-commits
mailing list