[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