[clang] nonblocking/nonallocating attributes: 2nd pass caller/callee analysis (PR #99656)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 28 05:52:27 PDT 2024
================
@@ -1187,13 +1234,261 @@ Analyzer::AnalysisMap::~AnalysisMap() {
namespace clang {
-void performEffectAnalysis(Sema &S, TranslationUnitDecl *TU) {
- if (S.hasUncompilableErrorOccurred() || S.Diags.getIgnoreAllWarnings())
- // exit if having uncompilable errors or ignoring all warnings:
+bool Sema::diagnoseConflictingFunctionEffect(
+ const FunctionEffectsRef &FX, const FunctionEffectWithCondition &NewEC,
+ SourceLocation NewAttrLoc) {
+ // If the new effect has a condition, we can't detect conflicts until the
+ // condition is resolved.
+ if (NewEC.Cond.getCondition() != nullptr)
+ return false;
+
+ // Diagnose the new attribute as incompatible with a previous one.
+ auto Incompatible = [&](const FunctionEffectWithCondition &PrevEC) {
+ Diag(NewAttrLoc, diag::err_attributes_are_not_compatible)
+ << ("'" + NewEC.description() + "'")
+ << ("'" + PrevEC.description() + "'") << false;
+ // We don't necessarily have the location of the previous attribute,
+ // so no note.
+ return true;
+ };
+
+ // Compare against previous attributes.
+ FunctionEffect::Kind NewKind = NewEC.Effect.kind();
+
+ for (const FunctionEffectWithCondition &PrevEC : FX) {
+ // Again, can't check yet when the effect is conditional.
+ if (PrevEC.Cond.getCondition() != nullptr)
+ continue;
+
+ FunctionEffect::Kind PrevKind = PrevEC.Effect.kind();
+ // Note that we allow PrevKind == NewKind; it's redundant and ignored.
+
+ if (PrevEC.Effect.oppositeKind() == NewKind)
+ return Incompatible(PrevEC);
+
+ // A new allocating is incompatible with a previous nonblocking.
+ if (PrevKind == FunctionEffect::Kind::NonBlocking &&
+ NewKind == FunctionEffect::Kind::Allocating)
+ return Incompatible(PrevEC);
+
+ // A new nonblocking is incompatible with a previous allocating.
+ if (PrevKind == FunctionEffect::Kind::Allocating &&
+ NewKind == FunctionEffect::Kind::NonBlocking)
+ return Incompatible(PrevEC);
+ }
+
+ return false;
+}
+
+void Sema::diagnoseFunctionEffectMergeConflicts(
+ const FunctionEffectSet::Conflicts &Errs, SourceLocation NewLoc,
+ SourceLocation OldLoc) {
+ for (const FunctionEffectSet::Conflict &Conflict : Errs) {
+ Diag(NewLoc, diag::warn_conflicting_func_effects)
+ << Conflict.Kept.description() << Conflict.Rejected.description();
+ Diag(OldLoc, diag::note_previous_declaration);
+ }
+}
+
+// Should only be called when getFunctionEffects() returns a non-empty set.
+// Decl should be a FunctionDecl or BlockDecl.
+void Sema::maybeAddDeclWithEffects(const Decl *D,
+ const FunctionEffectsRef &FX) {
+ if (!D->hasBody()) {
+ if (const auto *FD = D->getAsFunction(); FD && !FD->willHaveBody())
+ return;
+ }
+
+ if (Diags.getIgnoreAllWarnings() ||
+ (Diags.getSuppressSystemWarnings() &&
+ SourceMgr.isInSystemHeader(D->getLocation())))
+ return;
+
+ if (hasUncompilableErrorOccurred())
+ return;
+
+ // For code in dependent contexts, we'll do this at instantiation time.
+ // Without this check, we would analyze the function based on placeholder
+ // template parameters, and potentially generate spurious diagnostics.
+ if (cast<DeclContext>(D)->isDependentContext())
+ return;
+
+ addDeclWithEffects(D, FX);
+}
+
+void Sema::addDeclWithEffects(const Decl *D, const FunctionEffectsRef &FX) {
+ // To avoid the possibility of conflict, don't add effects which are
+ // not FE_InferrableOnCallees and therefore not verified; this removes
+ // blocking/allocating but keeps nonblocking/nonallocating.
+ // Also, ignore any conditions when building the list of effects.
+ bool AnyVerifiable = false;
+ for (const FunctionEffectWithCondition &EC : FX)
+ if (EC.Effect.flags() & FunctionEffect::FE_InferrableOnCallees) {
+ AllEffectsToVerify.insert(EC.Effect);
+ AnyVerifiable = true;
+ }
+
+ // Record the declaration for later analysis.
+ if (AnyVerifiable)
+ DeclsWithEffectsToVerify.push_back(D);
+}
+
+void Sema::performFunctionEffectAnalysis(TranslationUnitDecl *TU) {
+ if (hasUncompilableErrorOccurred() || Diags.getIgnoreAllWarnings())
return;
if (TU == nullptr)
return;
- Analyzer{S}.run(*TU);
+ Analyzer{*this}.run(*TU);
+}
+
+Sema::FunctionEffectDifferences::FunctionEffectDifferences(
+ const FunctionEffectsRef &Old, const FunctionEffectsRef &New) {
+
+ 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.getCondition() != New.Cond.getCondition()) {
+ // FIXME: Cases where the expressions are equivalent but
+ // don't have the same identity.
+ push_back(FunctionEffectDiff{
+ Old.Effect.kind(), FunctionEffectDiff::Kind::ConditionMismatch,
+ Old, New});
+ }
----------------
Sirraide wrote:
I don’t think you can really do anything here if the expressions are still dependent, yeah.
https://github.com/llvm/llvm-project/pull/99656
More information about the cfe-commits
mailing list