[clang] [ASTReader] Remove assert in GetExternalDeclStmt (PR #143739)

via cfe-commits cfe-commits at lists.llvm.org
Wed Jun 11 08:56:38 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-modules

Author: Henrik G. Olsson (hnrklssn)

<details>
<summary>Changes</summary>

This assert was reportedly added to "Defensively ensure that GetExternalDeclStmt protects itself from nested deserialization". In the tests for https://github.com/swiftlang/swift/pull/81859 I was able to trigger this assert without nested deserialization. In `FinishedDeserializing` we have this:
```
void ASTReader::FinishedDeserializing() {
  assert(NumCurrentElementsDeserializing &&
         "FinishedDeserializing not paired with StartedDeserializing");
  if (NumCurrentElementsDeserializing == 1) {
    // We decrease NumCurrentElementsDeserializing only after pending actions
    // are finished, to avoid recursively re-calling finishPendingActions().
    finishPendingActions();
  }
  --NumCurrentElementsDeserializing;
```
where `NumCurrentElementsDeserializing` is clearly 1 when calling `finishPendingActions`. Through this we end up in `loadDeclUpdateRecords`, which has:
```
in loadDeclUpdateRecords we have:
    // Check if this decl was interesting to the consumer. If we just loaded
    // the declaration, then we know it was interesting and we skip the call
    // to isConsumerInterestedIn because it is unsafe to call in the
    // current ASTReader state.
    bool WasInteresting = Record.JustLoaded || isConsumerInterestedIn(D);
```
In this case `Record.JustLoaded` is false, so we end up calling `isConsumerInterestedIn`.
In isConsumerInterestedIn we have:
```
  // An ImportDecl or VarDecl imported from a module map module will get
  // emitted when we import the relevant module.
  if (isPartOfPerModuleInitializer(D)) {
    auto *M = D->getImportedOwningModule();
    if (M && M->isModuleMapModule() &&
        getContext().DeclMustBeEmitted(D))
      return false;
  }
```
in DeclMustBeEmitted we have:
```
  // Variables that have initialization with side-effects are required.
  if (VD->getInit() && VD->getInit()->HasSideEffects(*this) &&
      // We can get a value-dependent initializer during error recovery.
      (VD->getInit()->isValueDependent() || !VD->evaluateValue()))
    return true;
```
in VarDecl::getInit we have:
```
  auto *Eval = getEvaluatedStmt();

  return cast<Expr>(Eval->Value.get(
      Eval->Value.isOffset() ? getASTContext().getExternalSource() : nullptr));
```
which ends up calling `ASTReader::GetExternalDeclStmt`.

At first I considered whether `bool WasInteresting = Record.JustLoaded || isConsumerInterestedIn(D);` should guard against calling `isConsumerInterestedIn` in even more cases, but I didn't know what that would be. Instead I tried removing the assert, and my test passed, so I assume calling `isConsumerInterestedIn` was safe in this case.

rdar://153085264

---
Full diff: https://github.com/llvm/llvm-project/pull/143739.diff


1 Files Affected:

- (modified) clang/lib/Serialization/ASTReader.cpp (-2) 


``````````diff
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 70b54b7296882..aaa64b06e1cee 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -8310,8 +8310,6 @@ Stmt *ASTReader::GetExternalDeclStmt(uint64_t Offset) {
     Error(std::move(Err));
     return nullptr;
   }
-  assert(NumCurrentElementsDeserializing == 0 &&
-         "should not be called while already deserializing");
   Deserializing D(this);
   return ReadStmtFromStream(*Loc.F);
 }

``````````

</details>


https://github.com/llvm/llvm-project/pull/143739


More information about the cfe-commits mailing list