================ @@ -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:
I do think this should be diagnosed; no-one in their right mind is going to write `foo() [[clang::blocking]] [[clang::nonblocking]]`, of course, but I feel like this might happen if templated code and/or type aliases are involved. Seeing as I’ve suggested moving this class into Sema anyway, since at least your comment states that it’s only used there, it should be straight-forward to diagnose this once that’s done. 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