[clang] nonblocking/nonallocating attributes (was: nolock/noalloc) (PR #84983)
via cfe-commits
cfe-commits at lists.llvm.org
Sat May 4 22:48:59 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;
----------------
Sirraide wrote:
> One idea would be to make this method receive a function pointer which it calls when it detects errors.
You could pass an `llvm::function_ref` for error handling, though I don’t think we usually do that.
Another idea would be to have this function return a `bool` indicating whether insertion succeeded (`true` on error, as ever); this would have to be propagated to e.g. `getUnion()`, which calls `insert()`, from what I can tell (it could e.g. return an optional). The caller could then either assert (ASTContext) or perform error handling (Sema). Because the ASTContext isn’t really supposed to issue diagnostics...
I would personally prefer the latter approach, but if it turns out to be a lot more cumbersome or otherwise infeasible, then I’d say the former is also an option.
https://github.com/llvm/llvm-project/pull/84983
More information about the cfe-commits
mailing list