Skip to content

[Modules] fix assert in hasInitWithSideEffects #146468

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 3, 2025

Conversation

hnrklssn
Copy link
Member

@hnrklssn hnrklssn commented Jul 1, 2025

This fixes another instance of Assertion failed: (NumCurrentElementsDeserializing == 0 && "should not be called while already deserializing"). I ran into it while importing clang modules from Swift, but I haven't been able to reproduce it in a test case yet. The error seems to be that an initializer expression can be deserialized and contain a call to a function whose function body is not yet deserialized. When evaluateValue() is called the constexpr evaluation triggers deserialization of the function body.

rdar://154717930

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Jul 1, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 1, 2025

@llvm/pr-subscribers-clang-modules

@llvm/pr-subscribers-clang

Author: Henrik G. Olsson (hnrklssn)

Changes

This fixes another instance of Assertion failed: (NumCurrentElementsDeserializing == 0 && "should not be called while already deserializing"). I ran into it while importing clang modules from Swift, but I haven't been able to reproduce it in a test case yet. The error seems to be that an initializer expression can be deserialized and contain a call to a function whose function body is not yet deserialized. When evaluateValue() is called the constexpr evaluation triggers deserialization of the function body.

rdar://154717930


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

1 Files Affected:

  • (modified) clang/lib/AST/Decl.cpp (+3-9)
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 5cdf75d71e4d7..5e91566e87f74 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2444,20 +2444,14 @@ bool VarDecl::hasInitWithSideEffects() const {
   if (!hasInit())
     return false;
 
-  // Check if we can get the initializer without deserializing
-  const Expr *E = nullptr;
+  // Check if we can get the initializer directly without an external source
   if (auto *S = dyn_cast<Stmt *>(Init)) {
-    E = cast<Expr>(S);
-  } else {
-    E = cast_or_null<Expr>(getEvaluatedStmt()->Value.getWithoutDeserializing());
-  }
-
-  if (E)
+    const Expr *E = cast<Expr>(S);
     return E->HasSideEffects(getASTContext()) &&
            // We can get a value-dependent initializer during error recovery.
            (E->isValueDependent() || !evaluateValue());
+  }
 
-  assert(getEvaluatedStmt()->Value.isOffset());
   // ASTReader tracks this without having to deserialize the initializer
   if (auto Source = getASTContext().getExternalSource())
     return Source->hasInitializerWithSideEffects(this);

Copy link
Member

@ChuanqiXu9 ChuanqiXu9 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change LGTM but I want a test to avoid further regression.

@hnrklssn
Copy link
Member Author

hnrklssn commented Jul 1, 2025

The change LGTM but I want a test to avoid further regression.

Yup, working on it

@hnrklssn hnrklssn force-pushed the fix-hasInitWithSideEffects branch from 49b4689 to 610416f Compare July 3, 2025 00:42
@llvmbot llvmbot added the clang:modules C++20 modules and Clang Header Modules label Jul 3, 2025
@hnrklssn hnrklssn changed the title WIP: fix assert in hasInitWithSideEffects [Modules] fix assert in hasInitWithSideEffects Jul 3, 2025
@hnrklssn
Copy link
Member Author

hnrklssn commented Jul 3, 2025

Found a reduced test case, at last

@hnrklssn hnrklssn force-pushed the fix-hasInitWithSideEffects branch from 610416f to 39fa1a4 Compare July 3, 2025 17:31
Copy link

github-actions bot commented Jul 3, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

All deserialized VarDecl initializers are EvaluatedStmt, but not all
EvaluatedStmt initializers are from a PCH. Calling
`VarDecl::hasInitWithSideEffects` can trigger constant evaluation, but
it's hard to know ahead of time whether that will trigger
deserialization - even if the initializer is fully deserialized, it may
contain a call to a constructor whose body is not deserialized. By
caching the result of `VarDecl::hasInitWithSideEffects` and populating
that cache during deserialization we can guarantee that calling it won't
trigger deserialization regardless of the state of the initializer.
This also reduces memory usage by removing the `InitSideEffectVars` set
in `ASTReader`.

rdar://154717930
@hnrklssn hnrklssn force-pushed the fix-hasInitWithSideEffects branch from 39fa1a4 to c7d7419 Compare July 3, 2025 17:34
@hnrklssn
Copy link
Member Author

hnrklssn commented Jul 3, 2025

@ChuanqiXu9 After investigating the test failures for my previous fix I realised I'd misunderstood EvaluatedStmt. That fix caused test failures because it essentially ignored EvaluatedStmt initializers if they didn't originate in a deserialized AST and were known to have side effects. If the defining module had an EvaluatedStmt initializer it would say it didn't have side effects since there would be no ASTReader claiming to know about side effects for that VarDecl. I rewrote the approach completely to cache the result of VarDecl::hasInitWithSideEffects in EvaluatedStmt. Since all deserialized VarDecl initializers are EvaluatedStmt that works great.

@hnrklssn hnrklssn merged commit 2910c24 into llvm:main Jul 3, 2025
9 checks passed
hnrklssn added a commit to hnrklssn/llvm-project that referenced this pull request Jul 3, 2025
All deserialized VarDecl initializers are EvaluatedStmt, but not all
EvaluatedStmt initializers are from a PCH. Calling
`VarDecl::hasInitWithSideEffects` can trigger constant evaluation, but
it's hard to know ahead of time whether that will trigger
deserialization - even if the initializer is fully deserialized, it may
contain a call to a constructor whose body is not deserialized. By
caching the result of `VarDecl::hasInitWithSideEffects` and populating
that cache during deserialization we can guarantee that calling it won't
trigger deserialization regardless of the state of the initializer.
This also reduces memory usage by removing the `InitSideEffectVars` set
in `ASTReader`.

rdar://154717930
(cherry picked from commit 2910c24)
@llvm-ci
Copy link
Collaborator

llvm-ci commented Jul 3, 2025

LLVM Buildbot has detected a new failure on builder lldb-aarch64-ubuntu running on linaro-lldb-aarch64-ubuntu while building clang at step 6 "test".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/59/builds/20434

Here is the relevant piece of the build log for the reference
Step 6 (test) failure: build (failure)
...
PASS: lldb-api :: functionalities/thread/concurrent_events/TestConcurrentDelaySignalBreak.py (646 of 2281)
PASS: lldb-api :: functionalities/thread/concurrent_events/TestConcurrentDelaySignalWatch.py (647 of 2281)
PASS: lldb-api :: functionalities/thread/concurrent_events/TestConcurrentDelayWatchBreak.py (648 of 2281)
PASS: lldb-api :: functionalities/thread/concurrent_events/TestConcurrentDelayedCrashWithBreakpointSignal.py (649 of 2281)
PASS: lldb-api :: functionalities/thread/concurrent_events/TestConcurrentDelayedCrashWithBreakpointWatchpoint.py (650 of 2281)
PASS: lldb-api :: functionalities/thread/concurrent_events/TestConcurrentManyBreakpoints.py (651 of 2281)
PASS: lldb-api :: functionalities/thread/concurrent_events/TestConcurrentSignalBreak.py (652 of 2281)
PASS: lldb-api :: functionalities/thread/concurrent_events/TestConcurrentNWatchNBreak.py (653 of 2281)
PASS: lldb-api :: functionalities/thread/concurrent_events/TestConcurrentManyCrash.py (654 of 2281)
PASS: lldb-api :: functionalities/thread/concurrent_events/TestConcurrentManySignals.py (655 of 2281)
FAIL: lldb-api :: functionalities/thread/concurrent_events/TestConcurrentManyWatchpoints.py (656 of 2281)
******************** TEST 'lldb-api :: functionalities/thread/concurrent_events/TestConcurrentManyWatchpoints.py' FAILED ********************
Script:
--
/usr/bin/python3.10 /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/dotest.py -u CXXFLAGS -u CFLAGS --env LLVM_LIBS_DIR=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./lib --env LLVM_INCLUDE_DIR=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/include --env LLVM_TOOLS_DIR=/home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin --arch aarch64 --build-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/lldb-test-build.noindex --lldb-module-cache-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/lldb-test-build.noindex/module-cache-lldb/lldb-api --clang-module-cache-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/lldb-test-build.noindex/module-cache-clang/lldb-api --executable /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin/lldb --compiler /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin/clang --dsymutil /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin/dsymutil --make /usr/bin/gmake --llvm-tools-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./bin --lldb-obj-root /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/tools/lldb --lldb-libs-dir /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/build/./lib --cmake-build-type Release /home/tcwg-buildbot/worker/lldb-aarch64-ubuntu/llvm-project/lldb/test/API/functionalities/thread/concurrent_events -p TestConcurrentManyWatchpoints.py
--
Exit Code: 1

Command Output (stdout):
--
lldb version 21.0.0git (https://github.com/llvm/llvm-project.git revision 2910c24638fcbc3dec02be072e6026d01012d946)
  clang revision 2910c24638fcbc3dec02be072e6026d01012d946
  llvm revision 2910c24638fcbc3dec02be072e6026d01012d946

Watchpoint 1 hit:
old value: 0
new value: 1

Watchpoint 1 hit:
old value: 1
new value: 1

Watchpoint 1 hit:
old value: 1
new value: 1

Watchpoint 1 hit:
old value: 1
new value: 1

Watchpoint 1 hit:
old value: 1
new value: 1

Watchpoint 1 hit:
old value: 1
new value: 1

Watchpoint 1 hit:

@llvm-ci
Copy link
Collaborator

llvm-ci commented Jul 4, 2025

LLVM Buildbot has detected a new failure on builder lldb-x86_64-win running on as-builder-10 while building clang at step 8 "test-check-lldb-unit".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/211/builds/216

Here is the relevant piece of the build log for the reference
Step 8 (test-check-lldb-unit) failure: Test just built components: check-lldb-unit completed (failure)
******************** TEST 'lldb-unit :: Host/./HostTests.exe/31/87' FAILED ********************
Script(shard):
--
GTEST_OUTPUT=json:C:\buildbot\as-builder-10\lldb-x86-64\build\tools\lldb\unittests\Host\.\HostTests.exe-lldb-unit-2716-31-87.json GTEST_SHUFFLE=0 GTEST_TOTAL_SHARDS=87 GTEST_SHARD_INDEX=31 C:\buildbot\as-builder-10\lldb-x86-64\build\tools\lldb\unittests\Host\.\HostTests.exe
--

Script:
--
C:\buildbot\as-builder-10\lldb-x86-64\build\tools\lldb\unittests\Host\.\HostTests.exe --gtest_filter=MainLoopTest.NoSpuriousPipeReads
--
C:\buildbot\as-builder-10\lldb-x86-64\llvm-project\lldb\unittests\Host\MainLoopTest.cpp(141): error: Expected equality of these values:
  1u
    Which is: 1
  callback_count
    Which is: 2


C:\buildbot\as-builder-10\lldb-x86-64\llvm-project\lldb\unittests\Host\MainLoopTest.cpp:141
Expected equality of these values:
  1u
    Which is: 1
  callback_count
    Which is: 2



********************


hnrklssn added a commit to hnrklssn/llvm-project that referenced this pull request Jul 7, 2025
All deserialized VarDecl initializers are EvaluatedStmt, but not all
EvaluatedStmt initializers are from a PCH. Calling
`VarDecl::hasInitWithSideEffects` can trigger constant evaluation, but
it's hard to know ahead of time whether that will trigger
deserialization - even if the initializer is fully deserialized, it may
contain a call to a constructor whose body is not deserialized. By
caching the result of `VarDecl::hasInitWithSideEffects` and populating
that cache during deserialization we can guarantee that calling it won't
trigger deserialization regardless of the state of the initializer.
This also reduces memory usage by removing the `InitSideEffectVars` set
in `ASTReader`.

rdar://154717930
(cherry picked from commit 2910c24)
hokein pushed a commit to hokein/llvm-project that referenced this pull request Jul 10, 2025
All deserialized VarDecl initializers are EvaluatedStmt, but not all
EvaluatedStmt initializers are from a PCH. Calling
`VarDecl::hasInitWithSideEffects` can trigger constant evaluation, but
it's hard to know ahead of time whether that will trigger
deserialization - even if the initializer is fully deserialized, it may
contain a call to a constructor whose body is not deserialized. By
caching the result of `VarDecl::hasInitWithSideEffects` and populating
that cache during deserialization we can guarantee that calling it won't
trigger deserialization regardless of the state of the initializer.
This also reduces memory usage by removing the `InitSideEffectVars` set
in `ASTReader`.

rdar://154717930
hokein pushed a commit to hokein/llvm-project that referenced this pull request Jul 16, 2025
All deserialized VarDecl initializers are EvaluatedStmt, but not all
EvaluatedStmt initializers are from a PCH. Calling
`VarDecl::hasInitWithSideEffects` can trigger constant evaluation, but
it's hard to know ahead of time whether that will trigger
deserialization - even if the initializer is fully deserialized, it may
contain a call to a constructor whose body is not deserialized. By
caching the result of `VarDecl::hasInitWithSideEffects` and populating
that cache during deserialization we can guarantee that calling it won't
trigger deserialization regardless of the state of the initializer.
This also reduces memory usage by removing the `InitSideEffectVars` set
in `ASTReader`.

rdar://154717930
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:modules C++20 modules and Clang Header Modules clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants