[clang] [clang] Accept mtime change for input files of relocatable PCH (PR #196057)
Arseniy Zaostrovnykh via cfe-commits
cfe-commits at lists.llvm.org
Wed May 6 07:30:59 PDT 2026
https://github.com/necto updated https://github.com/llvm/llvm-project/pull/196057
>From b43ad5369e2a905c10b13104e81f3e1485099295 Mon Sep 17 00:00:00 2001
From: Arseniy Zaostrovnykh <necto.ne at gmail.com>
Date: Wed, 6 May 2026 14:27:32 +0200
Subject: [PATCH 1/3] [clang] relocatable PCH do not support files with
different mtime
---
clang/test/PCH/relocatable-pch-mtime.c | 78 ++++++++++++++++++++++++++
1 file changed, 78 insertions(+)
create mode 100644 clang/test/PCH/relocatable-pch-mtime.c
diff --git a/clang/test/PCH/relocatable-pch-mtime.c b/clang/test/PCH/relocatable-pch-mtime.c
new file mode 100644
index 0000000000000..70eedf9eb2635
--- /dev/null
+++ b/clang/test/PCH/relocatable-pch-mtime.c
@@ -0,0 +1,78 @@
+// Test the mtime validation behavior of a relocatable PCH when source files
+// are relocated and their timestamps change. Without content validation any
+// mtime difference is a hard error. With -fvalidate-ast-input-files-content
+// an mtime mismatch falls back to a content hash comparison, which tolerates
+// mtime drift but still catches genuine content changes.
+
+// RUN: rm -rf %t
+// RUN: mkdir -p %t/sysroot_orig/usr/include
+
+// Create a simple header in the original sysroot.
+// RUN: echo '#ifndef TEST_H' > %t/sysroot_orig/usr/include/test.h
+// RUN: echo '#define TEST_H' >> %t/sysroot_orig/usr/include/test.h
+// RUN: echo 'int relocated_val = 42;' >> %t/sysroot_orig/usr/include/test.h
+// RUN: echo '#endif' >> %t/sysroot_orig/usr/include/test.h
+
+// Generate a relocatable PCH against the original sysroot. Include
+// -fvalidate-ast-input-files-content so that content hashes are stored in
+// the PCH and can be compared on subsequent loads.
+// RUN: %clang_cc1 -x c-header -relocatable-pch -emit-pch \
+// RUN: -isysroot %t/sysroot_orig -fvalidate-ast-input-files-content \
+// RUN: -o %t/test.pch %t/sysroot_orig/usr/include/test.h
+
+// Baseline: loading with the original sysroot succeeds.
+// RUN: %clang_cc1 -include-pch %t/test.pch \
+// RUN: -isysroot %t/sysroot_orig \
+// RUN: -fsyntax-only %s
+
+// Set up a new sysroot by moving the header there (simulating workspace relocation).
+// RUN: mkdir -p %t/sysroot_new/usr/include
+// RUN: mv %t/sysroot_orig/usr/include/test.h %t/sysroot_new/usr/include/test.h
+
+// Loading the relocatable PCH with the new sysroot and with mtime preserved works.
+// RUN: %clang_cc1 -include-pch %t/test.pch \
+// RUN: -isysroot %t/sysroot_new \
+// RUN: -fsyntax-only %s
+
+// Advance mtime of the relocated header by 1 hour to simulate timestamp drift
+// that naturally occurs when files are relocated, without changing content.
+// RUN: %python -c "import os,sys; t=os.path.getmtime(sys.argv[1])+3600; os.utime(sys.argv[1],(t,t))" \
+// RUN: %t/sysroot_new/usr/include/test.h
+
+// Mtime changed but content is identical: the relocatable PCH is rejected
+// RUN: not %clang_cc1 -include-pch %t/test.pch \
+// RUN: -isysroot %t/sysroot_new \
+// RUN: -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK-MTIME,CHECK
+
+// With content validation the mtime mismatch falls back to a content hash
+// comparison; since the content is unchanged the PCH loads successfully.
+// RUN: %clang_cc1 -include-pch %t/test.pch \
+// RUN: -isysroot %t/sysroot_new -fvalidate-ast-input-files-content \
+// RUN: -fsyntax-only %s
+
+// Overwrite the header with different content of the same size (42 -> 69).
+// RUN: echo '#ifndef TEST_H' > %t/sysroot_new/usr/include/test.h
+// RUN: echo '#define TEST_H' >> %t/sysroot_new/usr/include/test.h
+// RUN: echo 'int relocated_val = 69;' >> %t/sysroot_new/usr/include/test.h
+// RUN: echo '#endif' >> %t/sysroot_new/usr/include/test.h
+
+// Advance the mtime again to ensure it differs from the value stored in the PCH.
+// On a fast machine, the newly written file might have the same mtime as the original.
+// RUN: %python -c "import os,sys; t=os.path.getmtime(sys.argv[1])+3600; os.utime(sys.argv[1],(t,t))" \
+// RUN: %t/sysroot_new/usr/include/test.h
+
+// Without content validation the mtime mismatch is reported.
+// RUN: not %clang_cc1 -include-pch %t/test.pch \
+// RUN: -isysroot %t/sysroot_new \
+// RUN: -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK-MTIME,CHECK
+
+// With content validation the change is caught even though size is the same.
+// RUN: not %clang_cc1 -include-pch %t/test.pch \
+// RUN: -isysroot %t/sysroot_new -fvalidate-ast-input-files-content \
+// RUN: -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK-CONTENT,CHECK
+
+// CHECK: fatal error: file '{{.*}}test.h' has been modified since the precompiled header '{{.*}}test.pch' was built
+// CHECK-CONTENT: note: content changed
+// CHECK-MTIME: note: mtime changed
+
+int get_val(void) { return relocated_val; }
>From c234c837530750594fc98b691deb3ebabe1d1e76 Mon Sep 17 00:00:00 2001
From: Arseniy Zaostrovnykh <necto.ne at gmail.com>
Date: Wed, 6 May 2026 15:39:40 +0200
Subject: [PATCH 2/3] [clang] Accept mtime change for input files of
relocatable PCH
Relocatable PCH are expected to tolerate a change in the absolute path of input files.
It is natural that the files with different absolute paths can often have different mtime,
so mtime should not be used as a proxy for file change when loading a relocatable PCH.
--
CPP-8274
---
clang/lib/Serialization/ASTReader.cpp | 6 ++++++
clang/test/PCH/relocatable-pch-mtime.c | 22 +++++++++++-----------
2 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 2c0b908314fa5..82e44037ea1d2 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -2974,6 +2974,12 @@ InputFile ASTReader::getInputFile(ModuleFile &F, unsigned ID, bool Complain) {
FileChange.Kind == Change::None)
FileChange = HasInputContentChanged(FileChange);
+ // Ignore modification time of relocated inputs:
+ // files at different absolute paths are likely to have mismatching mtime,
+ // even if their contents match.
+ if (FileChange.Kind == Change::ModTime && F.RelocatablePCH)
+ FileChange = Change{Change::None};
+
// For an overridden file, there is nothing to validate.
if (!Overridden && FileChange.Kind != Change::None) {
if (Complain) {
diff --git a/clang/test/PCH/relocatable-pch-mtime.c b/clang/test/PCH/relocatable-pch-mtime.c
index 70eedf9eb2635..4961e50ce754d 100644
--- a/clang/test/PCH/relocatable-pch-mtime.c
+++ b/clang/test/PCH/relocatable-pch-mtime.c
@@ -1,8 +1,8 @@
// Test the mtime validation behavior of a relocatable PCH when source files
-// are relocated and their timestamps change. Without content validation any
-// mtime difference is a hard error. With -fvalidate-ast-input-files-content
-// an mtime mismatch falls back to a content hash comparison, which tolerates
-// mtime drift but still catches genuine content changes.
+// are relocated and their timestamps change. Mtime drift is expected after
+// relocation, so a bare mtime mismatch is tolerated. With
+// -fvalidate-ast-input-files-content an mtime mismatch falls back to a
+// content hash comparison, which still catches genuine content changes.
// RUN: rm -rf %t
// RUN: mkdir -p %t/sysroot_orig/usr/include
@@ -39,10 +39,10 @@
// RUN: %python -c "import os,sys; t=os.path.getmtime(sys.argv[1])+3600; os.utime(sys.argv[1],(t,t))" \
// RUN: %t/sysroot_new/usr/include/test.h
-// Mtime changed but content is identical: the relocatable PCH is rejected
-// RUN: not %clang_cc1 -include-pch %t/test.pch \
+// Mtime changed but content is identical: the relocatable PCH is accepted.
+// RUN: %clang_cc1 -include-pch %t/test.pch \
// RUN: -isysroot %t/sysroot_new \
-// RUN: -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK-MTIME,CHECK
+// RUN: -fsyntax-only %s
// With content validation the mtime mismatch falls back to a content hash
// comparison; since the content is unchanged the PCH loads successfully.
@@ -61,10 +61,11 @@
// RUN: %python -c "import os,sys; t=os.path.getmtime(sys.argv[1])+3600; os.utime(sys.argv[1],(t,t))" \
// RUN: %t/sysroot_new/usr/include/test.h
-// Without content validation the mtime mismatch is reported.
-// RUN: not %clang_cc1 -include-pch %t/test.pch \
+// Without content validation, mtime drift is tolerated and the content change
+// goes undetected.
+// RUN: %clang_cc1 -include-pch %t/test.pch \
// RUN: -isysroot %t/sysroot_new \
-// RUN: -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK-MTIME,CHECK
+// RUN: -fsyntax-only %s
// With content validation the change is caught even though size is the same.
// RUN: not %clang_cc1 -include-pch %t/test.pch \
@@ -73,6 +74,5 @@
// CHECK: fatal error: file '{{.*}}test.h' has been modified since the precompiled header '{{.*}}test.pch' was built
// CHECK-CONTENT: note: content changed
-// CHECK-MTIME: note: mtime changed
int get_val(void) { return relocated_val; }
>From 046717e56ddb7984a85429149004190f00ecd9eb Mon Sep 17 00:00:00 2001
From: Arseniy Zaostrovnykh <necto.ne at gmail.com>
Date: Wed, 6 May 2026 16:29:59 +0200
Subject: [PATCH 3/3] [NFC] Simplify FileCheck invocation
---
clang/test/PCH/relocatable-pch-mtime.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/test/PCH/relocatable-pch-mtime.c b/clang/test/PCH/relocatable-pch-mtime.c
index 4961e50ce754d..df07a8deab248 100644
--- a/clang/test/PCH/relocatable-pch-mtime.c
+++ b/clang/test/PCH/relocatable-pch-mtime.c
@@ -70,9 +70,9 @@
// With content validation the change is caught even though size is the same.
// RUN: not %clang_cc1 -include-pch %t/test.pch \
// RUN: -isysroot %t/sysroot_new -fvalidate-ast-input-files-content \
-// RUN: -fsyntax-only %s 2>&1 | FileCheck %s --check-prefix=CHECK-CONTENT,CHECK
+// RUN: -fsyntax-only %s 2>&1 | FileCheck %s
// CHECK: fatal error: file '{{.*}}test.h' has been modified since the precompiled header '{{.*}}test.pch' was built
-// CHECK-CONTENT: note: content changed
+// CHECK: note: content changed
int get_val(void) { return relocated_val; }
More information about the cfe-commits
mailing list