[clang] D41416: [modules] [pch] Do not deserialize all lazy template specializations when looking for one. (PR #83108)

Chuanqi Xu via cfe-commits cfe-commits at lists.llvm.org
Fri Nov 8 01:07:15 PST 2024


https://github.com/ChuanqiXu9 updated https://github.com/llvm/llvm-project/pull/83108

>From 6e0716367fc369f6e57ffd2b20b7a4c49230bac2 Mon Sep 17 00:00:00 2001
From: Chuanqi Xu <yedeng.yd at linux.alibaba.com>
Date: Fri, 8 Nov 2024 10:56:44 +0800
Subject: [PATCH 1/2] [Serialization] Downgrade inconsistent flags from erros
 to warnings

---
 clang/docs/ReleaseNotes.rst                   |  2 +
 clang/include/clang/Basic/DiagnosticGroups.td |  1 +
 .../Basic/DiagnosticSerializationKinds.td     | 10 ++--
 clang/lib/Serialization/ASTReader.cpp         | 39 +++++--------
 .../Modules/explicit-build-missing-files.cpp  |  2 +-
 clang/test/Modules/load_failure.c             |  6 +-
 clang/test/Modules/mismatch-diagnostics.cpp   | 56 +++++++++++++------
 clang/test/Modules/module-feature.m           |  6 +-
 clang/test/Modules/pr62359.cppm               |  9 ++-
 .../test/Modules/prebuilt-implicit-modules.m  |  2 +-
 clang/test/PCH/arc.m                          |  6 +-
 clang/test/PCH/no-validate-pch.cl             |  2 +-
 clang/test/PCH/pch-dir.c                      | 10 ++--
 13 files changed, 86 insertions(+), 65 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 30bcb6313b6ade..65aa8acd7b8263 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -511,6 +511,8 @@ Improvements to Clang's diagnostics
 
 - Clang now diagnoses ``[[deprecated]]`` attribute usage on local variables (#GH90073).
 
+- Clang now downgrades the inconsistent language options between modules to warnings instead of errors.
+
 Improvements to Clang's time-trace
 ----------------------------------
 
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 72eada50a56cc9..ba42ddfc37f35a 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -555,6 +555,7 @@ def ModuleLock : DiagGroup<"module-lock">;
 def ModuleBuild : DiagGroup<"module-build">;
 def ModuleImport : DiagGroup<"module-import">;
 def ModuleConflict : DiagGroup<"module-conflict">;
+def ModuleMismatchedOption : DiagGroup<"module-mismatched-option">;
 def ModuleFileExtension : DiagGroup<"module-file-extension">;
 def ModuleIncludeDirectiveTranslation : DiagGroup<"module-include-translation">;
 def RoundTripCC1Args : DiagGroup<"round-trip-cc1-args">;
diff --git a/clang/include/clang/Basic/DiagnosticSerializationKinds.td b/clang/include/clang/Basic/DiagnosticSerializationKinds.td
index 3914d3930bec79..4e1aff20617963 100644
--- a/clang/include/clang/Basic/DiagnosticSerializationKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSerializationKinds.td
@@ -36,10 +36,12 @@ def err_ast_file_targetopt_feature_mismatch : Error<
     "%select{AST file '%1' was|current translation unit is}0 compiled with the target "
     "feature '%2' but the %select{current translation unit is|AST file '%1' was}0 "
     "not">;
-def err_ast_file_langopt_mismatch : Error<"%0 was %select{disabled|enabled}1 in "
-    "AST file '%3' but is currently %select{disabled|enabled}2">;
-def err_ast_file_langopt_value_mismatch : Error<
-  "%0 differs in AST file '%1' vs. current file">;
+def warn_ast_file_langopt_mismatch : Warning<"%0 was %select{disabled|enabled}1 in "
+    "AST file '%3' but is currently %select{disabled|enabled}2">,
+    InGroup<ModuleMismatchedOption>;
+def warn_ast_file_langopt_value_mismatch : Warning<
+  "%0 differs in AST file '%1' vs. current file">,
+  InGroup<ModuleMismatchedOption>;
 def err_ast_file_diagopt_mismatch : Error<"%0 is currently enabled, but was not in "
   "the AST file '%1'">;
 def err_ast_file_modulecache_mismatch : Error<"AST file '%2' was compiled with module cache "
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 79615dc3c018ea..1c94e8e9a212a9 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -279,9 +279,7 @@ ASTReaderListener::~ASTReaderListener() = default;
 /// \param Diags If non-NULL, diagnostics will be emitted via this engine.
 /// \param AllowCompatibleDifferences If true, differences between compatible
 ///        language options will be permitted.
-///
-/// \returns true if the languagae options mis-match, false otherwise.
-static bool checkLanguageOptions(const LangOptions &LangOpts,
+static void checkLanguageOptions(const LangOptions &LangOpts,
                                  const LangOptions &ExistingLangOpts,
                                  StringRef ModuleFilename,
                                  DiagnosticsEngine *Diags,
@@ -290,30 +288,27 @@ static bool checkLanguageOptions(const LangOptions &LangOpts,
   if (ExistingLangOpts.Name != LangOpts.Name) {                                \
     if (Diags) {                                                               \
       if (Bits == 1)                                                           \
-        Diags->Report(diag::err_ast_file_langopt_mismatch)                     \
+        Diags->Report(diag::warn_ast_file_langopt_mismatch)                    \
             << Description << LangOpts.Name << ExistingLangOpts.Name           \
             << ModuleFilename;                                                 \
       else                                                                     \
-        Diags->Report(diag::err_ast_file_langopt_value_mismatch)               \
+        Diags->Report(diag::warn_ast_file_langopt_value_mismatch)              \
             << Description << ModuleFilename;                                  \
     }                                                                          \
-    return true;                                                               \
   }
 
 #define VALUE_LANGOPT(Name, Bits, Default, Description)                        \
   if (ExistingLangOpts.Name != LangOpts.Name) {                                \
     if (Diags)                                                                 \
-      Diags->Report(diag::err_ast_file_langopt_value_mismatch)                 \
+      Diags->Report(diag::warn_ast_file_langopt_value_mismatch)                \
           << Description << ModuleFilename;                                    \
-    return true;                                                               \
   }
 
 #define ENUM_LANGOPT(Name, Type, Bits, Default, Description)                   \
   if (ExistingLangOpts.get##Name() != LangOpts.get##Name()) {                  \
     if (Diags)                                                                 \
-      Diags->Report(diag::err_ast_file_langopt_value_mismatch)                 \
+      Diags->Report(diag::warn_ast_file_langopt_value_mismatch)                \
           << Description << ModuleFilename;                                    \
-    return true;                                                               \
   }
 
 #define COMPATIBLE_LANGOPT(Name, Bits, Default, Description)  \
@@ -335,24 +330,21 @@ static bool checkLanguageOptions(const LangOptions &LangOpts,
 
   if (ExistingLangOpts.ModuleFeatures != LangOpts.ModuleFeatures) {
     if (Diags)
-      Diags->Report(diag::err_ast_file_langopt_value_mismatch)
+      Diags->Report(diag::warn_ast_file_langopt_value_mismatch)
           << "module features" << ModuleFilename;
-    return true;
   }
 
   if (ExistingLangOpts.ObjCRuntime != LangOpts.ObjCRuntime) {
     if (Diags)
-      Diags->Report(diag::err_ast_file_langopt_value_mismatch)
+      Diags->Report(diag::warn_ast_file_langopt_value_mismatch)
           << "target Objective-C runtime" << ModuleFilename;
-    return true;
   }
 
   if (ExistingLangOpts.CommentOpts.BlockCommandNames !=
       LangOpts.CommentOpts.BlockCommandNames) {
     if (Diags)
-      Diags->Report(diag::err_ast_file_langopt_value_mismatch)
+      Diags->Report(diag::warn_ast_file_langopt_value_mismatch)
           << "block command names" << ModuleFilename;
-    return true;
   }
 
   // Sanitizer feature mismatches are treated as compatible differences. If
@@ -378,11 +370,8 @@ static bool checkLanguageOptions(const LangOptions &LangOpts,
   }
 #include "clang/Basic/Sanitizers.def"
       }
-      return true;
     }
   }
-
-  return false;
 }
 
 /// Compare the given set of target options against an existing set of
@@ -459,9 +448,10 @@ bool PCHValidator::ReadLanguageOptions(const LangOptions &LangOpts,
                                        StringRef ModuleFilename, bool Complain,
                                        bool AllowCompatibleDifferences) {
   const LangOptions &ExistingLangOpts = PP.getLangOpts();
-  return checkLanguageOptions(LangOpts, ExistingLangOpts, ModuleFilename,
-                              Complain ? &Reader.Diags : nullptr,
-                              AllowCompatibleDifferences);
+  checkLanguageOptions(LangOpts, ExistingLangOpts, ModuleFilename,
+                       Complain ? &Reader.Diags : nullptr,
+                       AllowCompatibleDifferences);
+  return false;
 }
 
 bool PCHValidator::ReadTargetOptions(const TargetOptions &TargetOpts,
@@ -5401,8 +5391,9 @@ namespace {
     bool ReadLanguageOptions(const LangOptions &LangOpts,
                              StringRef ModuleFilename, bool Complain,
                              bool AllowCompatibleDifferences) override {
-      return checkLanguageOptions(ExistingLangOpts, LangOpts, ModuleFilename,
-                                  nullptr, AllowCompatibleDifferences);
+      checkLanguageOptions(ExistingLangOpts, LangOpts, ModuleFilename, nullptr,
+                           AllowCompatibleDifferences);
+      return false;
     }
 
     bool ReadTargetOptions(const TargetOptions &TargetOpts,
diff --git a/clang/test/Modules/explicit-build-missing-files.cpp b/clang/test/Modules/explicit-build-missing-files.cpp
index 3ea881d34c6b28..4682ede5e08089 100644
--- a/clang/test/Modules/explicit-build-missing-files.cpp
+++ b/clang/test/Modules/explicit-build-missing-files.cpp
@@ -33,7 +33,7 @@
 // RUN: %clang_cc1 -fmodules -I %t -fmodule-file=%t/b.pcm %s
 // RUN: not %clang_cc1 -fmodules -I %t -fmodule-file=%t/a.pcm %s -DERRORS 2>&1 | FileCheck %s --check-prefix=MISSING-B
 // RUN: %clang_cc1 -fmodules -I %t -fmodule-file=%t/a.pcm -fmodule-map-file=%t/modulemap.moved %s
-// RUN: not %clang_cc1 -fmodules -I %t -fmodule-file=%t/a.pcm -fmodule-map-file=%t/modulemap.moved -std=c++1z %s
+// RUN: %clang_cc1 -fmodules -I %t -fmodule-file=%t/a.pcm -fmodule-map-file=%t/modulemap.moved -std=c++1z %s
 // RUN: %clang_cc1 -fmodules -I %t -fmodule-file=%t/a.pcm -fmodule-map-file=%t/modulemap.moved -std=c++1z -Wno-module-file-config-mismatch %s -Db=a
 // RUN: rm %t/a.h
 // RUN: %clang_cc1 -fmodules -I %t -fmodule-file=%t/a.pcm %s -verify
diff --git a/clang/test/Modules/load_failure.c b/clang/test/Modules/load_failure.c
index 662b39b6f1874f..bc0abb46bdc2bc 100644
--- a/clang/test/Modules/load_failure.c
+++ b/clang/test/Modules/load_failure.c
@@ -11,11 +11,11 @@
 // RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -x objective-c -fmodules-cache-path=%t -I %S/Inputs -fdisable-module-hash %s -DNONEXISTENT 2>&1 | FileCheck -check-prefix=CHECK-NONEXISTENT %s
 // CHECK-NONEXISTENT: load_failure.c:2:9: fatal error: module 'load_nonexistent' not found
 
-// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -x objective-c -fmodules-cache-path=%t -I %S/Inputs -fdisable-module-hash %s -DFAILURE 2> %t.out
-// RUN: FileCheck -check-prefix=CHECK-FAILURE %s < %t.out
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -x objective-c -fmodules-cache-path=%t -I %S/Inputs -fdisable-module-hash %s -DFAILURE 2> %t.out
+// RUN: FileCheck -check-prefix=CHECK-WARN %s < %t.out
 
 // FIXME: Clean up diagnostic text below and give it a location
-// CHECK-FAILURE: error: C99 was disabled in AST file '{{.*}}load_failure.pcm' but is currently enabled
+// CHECK-WARN: warning: C99 was disabled in AST file '{{.*}}load_failure.pcm' but is currently enabled
 // FIXME: When we have a syntax for modules in C, use that.
 
 
diff --git a/clang/test/Modules/mismatch-diagnostics.cpp b/clang/test/Modules/mismatch-diagnostics.cpp
index dffd4b46a678e5..745d6ee802f8f9 100644
--- a/clang/test/Modules/mismatch-diagnostics.cpp
+++ b/clang/test/Modules/mismatch-diagnostics.cpp
@@ -3,31 +3,55 @@
 // RUN: split-file %s %t
 // RUN: mkdir -p %t/prebuilt_modules
 //
-// RUN: %clang_cc1 -triple %itanium_abi_triple                          \
-// RUN:     -std=c++20 -fprebuilt-module-path=%t/prebuilt-modules       \
-// RUN:     -emit-module-interface -pthread -DBUILD_MODULE              \
-// RUN:     %t/mismatching_module.cppm -o                               \
+// RUN: %clang_cc1 -triple %itanium_abi_triple                           \
+// RUN:     -std=c++20 -fprebuilt-module-path=%t/prebuilt-modules        \
+// RUN:     -emit-module-interface -pthread -DBUILD_MODULE               \
+// RUN:     %t/mismatching_module.cppm -o                                \
 // RUN:     %t/prebuilt_modules/mismatching_module.pcm
 //
-// RUN: not %clang_cc1 -triple %itanium_abi_triple -std=c++20           \
-// RUN:     -fprebuilt-module-path=%t/prebuilt_modules -DCHECK_MISMATCH \
-// RUN:     %t/use.cpp 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20                \
+// RUN:     -fprebuilt-module-path=%t/prebuilt_modules -DCHECK_MISMATCH  \
+// RUN:     %t/use.cpp 2>&1 | FileCheck %t/use.cpp
 
 // Test again with reduced BMI.
-// RUN: %clang_cc1 -triple %itanium_abi_triple                          \
-// RUN:     -std=c++20 -fprebuilt-module-path=%t/prebuilt-modules       \
-// RUN:     -emit-reduced-module-interface -pthread -DBUILD_MODULE              \
-// RUN:     %t/mismatching_module.cppm -o                               \
+// RUN: %clang_cc1 -triple %itanium_abi_triple                           \
+// RUN:     -std=c++20 -fprebuilt-module-path=%t/prebuilt-modules        \
+// RUN:     -emit-reduced-module-interface -pthread -DBUILD_MODULE       \
+// RUN:     %t/mismatching_module.cppm -o                                \
 // RUN:     %t/prebuilt_modules/mismatching_module.pcm
 //
-// RUN: not %clang_cc1 -triple %itanium_abi_triple -std=c++20           \
-// RUN:     -fprebuilt-module-path=%t/prebuilt_modules -DCHECK_MISMATCH \
-// RUN:     %t/use.cpp 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20                \
+// RUN:     -fprebuilt-module-path=%t/prebuilt_modules -DCHECK_MISMATCH  \
+// RUN:     %t/use.cpp 2>&1 | FileCheck %t/use.cpp
+//
+// RUN: %clang_cc1 -triple %itanium_abi_triple                           \
+// RUN:     -std=c++20 -fprebuilt-module-path=%t/prebuilt-modules        \
+// RUN:     -emit-module-interface -pthread -DBUILD_MODULE               \
+// RUN:     %t/mismatching_module.cppm -o                                \
+// RUN:     %t/prebuilt_modules/mismatching_module.pcm
+//
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20                \
+// RUN:     -fprebuilt-module-path=%t/prebuilt_modules -DCHECK_MISMATCH  \
+// RUN:     -Wno-module-mismatched-option %t/use.cpp 2>&1 | FileCheck %t/use.cpp \
+// RUN:     --check-prefix=NOWARN --allow-empty
+
+// Test again with reduced BMI.
+// RUN: %clang_cc1 -triple %itanium_abi_triple                           \
+// RUN:     -std=c++20 -fprebuilt-module-path=%t/prebuilt-modules        \
+// RUN:     -emit-reduced-module-interface -pthread -DBUILD_MODULE       \
+// RUN:     %t/mismatching_module.cppm -o                                \
+// RUN:     %t/prebuilt_modules/mismatching_module.pcm
+//
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20                \
+// RUN:     -fprebuilt-module-path=%t/prebuilt_modules -DCHECK_MISMATCH  \
+// RUN:     -Wno-module-mismatched-option %t/use.cpp 2>&1 | FileCheck %t/use.cpp \
+// RUN:     --check-prefix=NOWARN --allow-empty
 
 //--- mismatching_module.cppm
 export module mismatching_module;
 
 //--- use.cpp
 import mismatching_module;
-// CHECK: error: POSIX thread support was enabled in AST file '{{.*[/|\\\\]}}mismatching_module.pcm' but is currently disabled
-// CHECK-NEXT: module file {{.*[/|\\\\]}}mismatching_module.pcm cannot be loaded due to a configuration mismatch with the current compilation
+// CHECK: warning: POSIX thread support was enabled in AST file '{{.*[/|\\\\]}}mismatching_module.pcm' but is currently disabled
+
+// NOWARN-NOT: warning
diff --git a/clang/test/Modules/module-feature.m b/clang/test/Modules/module-feature.m
index 4926d26515f860..bbc9b0220f761c 100644
--- a/clang/test/Modules/module-feature.m
+++ b/clang/test/Modules/module-feature.m
@@ -6,9 +6,9 @@
 // RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t -fimplicit-module-maps -fmodule-feature f2 -fmodule-feature f1 -F %S/Inputs %s -Rmodule-build 2>&1 | FileCheck %s -allow-empty -check-prefix=ALREADY_BUILT
 // ALREADY_BUILT-NOT: building module
 
-// Errors if we try to force the load.
+// Warns if we try to force the load.
 // RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t.nohash -fimplicit-module-maps -fdisable-module-hash -fmodule-feature f1 -fmodule-feature f2 -F %S/Inputs %s -verify -Rmodule-build
-// RUN: not %clang_cc1 -fmodules -fmodules-cache-path=%t.nohash -fimplicit-module-maps -fdisable-module-hash -fmodule-feature f2 -F %S/Inputs %s 2>&1 | FileCheck %s -check-prefix=DIFFERS
-// DIFFERS: error: module features differs
+// RUN: %clang_cc1 -fmodules -fmodules-cache-path=%t.nohash -fimplicit-module-maps -fdisable-module-hash -fmodule-feature f2 -F %S/Inputs %s 2>&1 | FileCheck %s -check-prefix=DIFFERS
+// DIFFERS: warning: module features differs
 
 @import Module; // expected-remark {{building module 'Module'}} expected-remark {{finished}}
diff --git a/clang/test/Modules/pr62359.cppm b/clang/test/Modules/pr62359.cppm
index 7d9d3eec26cca7..f459d46f378618 100644
--- a/clang/test/Modules/pr62359.cppm
+++ b/clang/test/Modules/pr62359.cppm
@@ -3,9 +3,9 @@
 // RUN: split-file %s %t
 //
 // RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/Hello.cppm -o %t/Hello.pcm
-// RUN: not %clang_cc1 -std=c++20 -fopenmp %t/use.cpp -fmodule-file=hello=%t/Hello.pcm -fsyntax-only \
+// RUN: %clang_cc1 -std=c++20 -fopenmp %t/use.cpp -fmodule-file=hello=%t/Hello.pcm -fsyntax-only \
 // RUN:     2>&1 | FileCheck %t/use.cpp
-// RUN: not %clang_cc1 -std=c++20 -fopenmp %t/use2.cpp -fmodule-file=hello=%t/Hello.pcm -fsyntax-only \
+// RUN: %clang_cc1 -std=c++20 -fopenmp %t/use2.cpp -fmodule-file=hello=%t/Hello.pcm -fsyntax-only \
 // RUN:     2>&1 | FileCheck %t/use2.cpp
 //
 // RUN: %clang_cc1 -std=c++20 -fopenmp -emit-module-interface %t/Hello.cppm -o %t/Hello.pcm
@@ -18,9 +18,9 @@
 // RUN: split-file %s %t
 //
 // RUN: %clang_cc1 -std=c++20 -emit-reduced-module-interface %t/Hello.cppm -o %t/Hello.pcm
-// RUN: not %clang_cc1 -std=c++20 -fopenmp %t/use.cpp -fmodule-file=hello=%t/Hello.pcm -fsyntax-only \
+// RUN: %clang_cc1 -std=c++20 -fopenmp %t/use.cpp -fmodule-file=hello=%t/Hello.pcm -fsyntax-only \
 // RUN:     2>&1 | FileCheck %t/use.cpp
-// RUN: not %clang_cc1 -std=c++20 -fopenmp %t/use2.cpp -fmodule-file=hello=%t/Hello.pcm -fsyntax-only \
+// RUN: %clang_cc1 -std=c++20 -fopenmp %t/use2.cpp -fmodule-file=hello=%t/Hello.pcm -fsyntax-only \
 // RUN:     2>&1 | FileCheck %t/use2.cpp
 //
 // RUN: %clang_cc1 -std=c++20 -fopenmp -emit-reduced-module-interface %t/Hello.cppm -o %t/Hello.pcm
@@ -56,4 +56,3 @@ int use2() {
 }
 
 // CHECK: OpenMP{{.*}}differs in AST file '{{.*}}Hello.pcm' vs. current file
-// CHECK: use of undeclared identifier 'pragma'
diff --git a/clang/test/Modules/prebuilt-implicit-modules.m b/clang/test/Modules/prebuilt-implicit-modules.m
index dc4fb55cb17a55..1bae214eb901f7 100644
--- a/clang/test/Modules/prebuilt-implicit-modules.m
+++ b/clang/test/Modules/prebuilt-implicit-modules.m
@@ -25,7 +25,7 @@
 // RUN: mkdir -p %t2
 // RUN: %clang_cc1 -x objective-c -fmodules %S/Inputs/prebuilt-implicit-module/module.modulemap -emit-module -fmodule-name=module_a -fmodules-cache-path=%t
 // RUN: %clang_cc1 -x objective-c -fmodules %S/Inputs/prebuilt-implicit-module/module.modulemap -emit-module -fmodule-name=module_a -o %t/module_a.pcm -fno-signed-char
-// RUN: not %clang_cc1 -x objective-c %s -I%S/Inputs/prebuilt-implicit-module -fmodules -fmodule-map-file=%S/Inputs/prebuilt-implicit-module/module.modulemap -fprebuilt-implicit-modules -fprebuilt-module-path=%t -fmodules-cache-path=%t2
+// RUN: %clang_cc1 -x objective-c %s -I%S/Inputs/prebuilt-implicit-module -fmodules -fmodule-map-file=%S/Inputs/prebuilt-implicit-module/module.modulemap -fprebuilt-implicit-modules -fprebuilt-module-path=%t -fmodules-cache-path=%t2
 // RUN: find %t2 -name "module_a*.pcm" | not grep module_a
 
 // expected-no-diagnostics
diff --git a/clang/test/PCH/arc.m b/clang/test/PCH/arc.m
index e4ad71a469b956..d714512b48d7d7 100644
--- a/clang/test/PCH/arc.m
+++ b/clang/test/PCH/arc.m
@@ -6,10 +6,10 @@
 // RUN: %clang_cc1 -emit-pch -fblocks -triple x86_64-apple-darwin11 -fobjc-arc -x objective-c-header -o %t %S/Inputs/arc.h
 // RUN: %clang_cc1 -fblocks -triple x86_64-apple-darwin11 -fobjc-arc -include-pch %t -emit-llvm-only %s 
 
-// Test error when pch's -fobjc-arc state is different.
-// RUN: not %clang_cc1 -fblocks -triple x86_64-apple-darwin11 -include-pch %t -emit-llvm-only %s 2>&1 | FileCheck -check-prefix=CHECK-ERR1 %s 
+// Test warning when pch's -fobjc-arc state is different.
+// RUN: %clang_cc1 -fblocks -triple x86_64-apple-darwin11 -include-pch %t -emit-llvm-only %s 2>&1 | FileCheck -check-prefix=CHECK-ERR1 %s 
 // RUN: %clang_cc1 -emit-pch -fblocks -triple x86_64-apple-darwin11 -x objective-c-header -o %t %S/Inputs/arc.h
-// RUN: not %clang_cc1 -fblocks -triple x86_64-apple-darwin11 -fobjc-arc -include-pch %t -emit-llvm-only %s 2>&1 | FileCheck -check-prefix=CHECK-ERR2 %s
+// RUN: %clang_cc1 -fblocks -triple x86_64-apple-darwin11 -fobjc-arc -include-pch %t -emit-llvm-only %s 2>&1 | FileCheck -check-prefix=CHECK-ERR2 %s
 
 array0 a0;
 array1 a1;
diff --git a/clang/test/PCH/no-validate-pch.cl b/clang/test/PCH/no-validate-pch.cl
index aa228ee2052192..c5a500103091f6 100644
--- a/clang/test/PCH/no-validate-pch.cl
+++ b/clang/test/PCH/no-validate-pch.cl
@@ -16,7 +16,7 @@
 // CHECK: note: previous definition is here
 // CHECK: #define X 4
 
-// CHECK-VAL: error: __OPTIMIZE__ predefined macro was enabled in AST file '{{.*}}' but is currently disabled
+// CHECK-VAL: warning: __OPTIMIZE__ predefined macro was enabled in AST file '{{.*}}' but is currently disabled
 // CHECK-VAL: error: definition of macro 'X' differs between the AST file '{{.*}}' ('4') and the command line ('5')
 
 void test(void) {
diff --git a/clang/test/PCH/pch-dir.c b/clang/test/PCH/pch-dir.c
index fd7d24f9f83ff4..e67d28b1e8567e 100644
--- a/clang/test/PCH/pch-dir.c
+++ b/clang/test/PCH/pch-dir.c
@@ -11,11 +11,11 @@
 // RUN: %clang -x c++ -include %t.h -std=c++98 -fsyntax-only %s -Xclang -print-stats 2> %t.cpplog
 // RUN: FileCheck -check-prefix=CHECK-CPP %s < %t.cpplog
 
-// RUN: not %clang -x c++ -std=c++11 -include %t.h -fsyntax-only %s 2> %t.cpp11log
-// RUN: FileCheck -check-prefix=CHECK-NO-SUITABLE %s < %t.cpp11log
+// RUN: %clang -x c++ -std=c++11 -include %t.h -fsyntax-only %s 2> %t.cpp11log
+// RUN: FileCheck %s --check-prefix=CHECK-OPT-DIFF < %t.cpp11log
 
-// RUN: not %clang -include %t.h -fsyntax-only %s 2> %t.missinglog2
-// RUN: FileCheck -check-prefix=CHECK-NO-SUITABLE %s < %t.missinglog2
+// RUN: %clang -include %t.h -fsyntax-only %s 2> %t.missinglog2
+// RUN: FileCheck --check-prefix=CHECK-OPT-DIFF %s < %t.missinglog2
 // RUN: not %clang -include %t.h -DFOO=foo -DBAR=bar -fsyntax-only %s 2> %t.missinglog2
 // RUN: FileCheck -check-prefix=CHECK-NO-SUITABLE %s < %t.missinglog2
 
@@ -41,6 +41,8 @@ int get(void) {
 #endif
 }
 
+// CHECK-OPT-DIFF: warning: {{.*}} was disabled in AST file{{.*}} but is currently enabled
+
 // CHECK-NO-SUITABLE: no suitable precompiled header file found in directory
 
 // CHECK-IGNORED-DIR: precompiled header directory '{{.*}}pch-dir.c.tmp.x.h.gch' was ignored because it contains no clang PCH files

>From a5265801086897dfe0770c9552acd6d70b41bd92 Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <v.g.vassilev at gmail.com>
Date: Sun, 7 Jan 2018 15:16:11 +0200
Subject: [PATCH 2/2] D41416: [modules] [pch] Do not deserialize all lazy
 template specializations when looking for one.

---
 clang/include/clang/AST/DeclTemplate.h    | 36 ++++++++-
 clang/lib/AST/DeclTemplate.cpp            | 98 +++++++++++++++++------
 clang/lib/AST/ODRHash.cpp                 | 15 ++++
 clang/lib/Serialization/ASTReader.cpp     | 25 ++++--
 clang/lib/Serialization/ASTReaderDecl.cpp | 44 ++++++----
 clang/lib/Serialization/ASTWriter.cpp     | 21 ++++-
 clang/lib/Serialization/ASTWriterDecl.cpp | 80 ++++++++++++++----
 clang/test/Modules/cxx-templates.cpp      |  8 +-
 clang/test/Modules/odr_hash.cpp           |  4 +-
 9 files changed, 258 insertions(+), 73 deletions(-)

diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index e4bf54c3d77b7f..7d707ed45cf5f6 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -262,6 +262,9 @@ class TemplateArgumentList final
   TemplateArgumentList(const TemplateArgumentList &) = delete;
   TemplateArgumentList &operator=(const TemplateArgumentList &) = delete;
 
+  /// Create hash for the given arguments.
+  static unsigned ComputeODRHash(ArrayRef<TemplateArgument> Args);
+
   /// Create a new template argument list that copies the given set of
   /// template arguments.
   static TemplateArgumentList *CreateCopy(ASTContext &Context,
@@ -735,6 +738,26 @@ class RedeclarableTemplateDecl : public TemplateDecl,
   }
 
   void anchor() override;
+  struct LazySpecializationInfo {
+    GlobalDeclID DeclID = GlobalDeclID();
+    unsigned ODRHash = ~0U;
+    bool IsPartial = false;
+    LazySpecializationInfo(GlobalDeclID ID, unsigned Hash = ~0U,
+                           bool Partial = false)
+        : DeclID(ID), ODRHash(Hash), IsPartial(Partial) {}
+    LazySpecializationInfo() {}
+    bool operator<(const LazySpecializationInfo &Other) const {
+      return DeclID < Other.DeclID;
+    }
+    bool operator==(const LazySpecializationInfo &Other) const {
+      assert((DeclID != Other.DeclID || ODRHash == Other.ODRHash) &&
+             "Hashes differ!");
+      assert((DeclID != Other.DeclID || IsPartial == Other.IsPartial) &&
+             "Both must be the same kinds!");
+      return DeclID == Other.DeclID;
+    }
+  };
+
 protected:
   template <typename EntryType> struct SpecEntryTraits {
     using DeclType = EntryType;
@@ -775,7 +798,12 @@ class RedeclarableTemplateDecl : public TemplateDecl,
     return SpecIterator<EntryType>(isEnd ? Specs.end() : Specs.begin());
   }
 
-  void loadLazySpecializationsImpl() const;
+  void loadLazySpecializationsImpl(bool OnlyPartial = false) const;
+
+  void loadLazySpecializationsImpl(llvm::ArrayRef<TemplateArgument> Args,
+                                   TemplateParameterList *TPL = nullptr) const;
+
+  Decl *loadLazySpecializationImpl(LazySpecializationInfo &LazySpecInfo) const;
 
   template <class EntryType, typename ...ProfileArguments>
   typename SpecEntryTraits<EntryType>::DeclType*
@@ -802,7 +830,7 @@ class RedeclarableTemplateDecl : public TemplateDecl,
     ///
     /// The first value in the array is the number of specializations/partial
     /// specializations that follow.
-    GlobalDeclID *LazySpecializations = nullptr;
+    LazySpecializationInfo *LazySpecializations = nullptr;
   };
 
   /// Pointer to the common data shared by all declarations of this
@@ -2283,7 +2311,7 @@ class ClassTemplateDecl : public RedeclarableTemplateDecl {
   friend class TemplateDeclInstantiator;
 
   /// Load any lazily-loaded specializations from the external source.
-  void LoadLazySpecializations() const;
+  void LoadLazySpecializations(bool OnlyPartial = false) const;
 
   /// Get the underlying class declarations of the template.
   CXXRecordDecl *getTemplatedDecl() const {
@@ -3033,7 +3061,7 @@ class VarTemplateDecl : public RedeclarableTemplateDecl {
   friend class ASTDeclWriter;
 
   /// Load any lazily-loaded specializations from the external source.
-  void LoadLazySpecializations() const;
+  void LoadLazySpecializations(bool OnlyPartial = false) const;
 
   /// Get the underlying variable declarations of the template.
   VarDecl *getTemplatedDecl() const {
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index a221d619672b38..d0e16c30881be2 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -16,7 +16,9 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclarationName.h"
 #include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
 #include "clang/AST/ExternalASTSource.h"
+#include "clang/AST/ODRHash.h"
 #include "clang/AST/TemplateBase.h"
 #include "clang/AST/TemplateName.h"
 #include "clang/AST/Type.h"
@@ -351,17 +353,44 @@ RedeclarableTemplateDecl::CommonBase *RedeclarableTemplateDecl::getCommonPtr() c
   return Common;
 }
 
-void RedeclarableTemplateDecl::loadLazySpecializationsImpl() const {
+void RedeclarableTemplateDecl::loadLazySpecializationsImpl(
+    bool OnlyPartial /*=false*/) const {
   // Grab the most recent declaration to ensure we've loaded any lazy
   // redeclarations of this template.
   CommonBase *CommonBasePtr = getMostRecentDecl()->getCommonPtr();
-  if (CommonBasePtr->LazySpecializations) {
-    ASTContext &Context = getASTContext();
-    GlobalDeclID *Specs = CommonBasePtr->LazySpecializations;
-    CommonBasePtr->LazySpecializations = nullptr;
-    unsigned SpecSize = (*Specs++).getRawValue();
-    for (unsigned I = 0; I != SpecSize; ++I)
-      (void)Context.getExternalSource()->GetExternalDecl(Specs[I]);
+  if (auto *Specs = CommonBasePtr->LazySpecializations) {
+    if (!OnlyPartial)
+      CommonBasePtr->LazySpecializations = nullptr;
+    unsigned N = Specs[0].DeclID.getRawValue();
+    for (unsigned I = 0; I != N; ++I) {
+      // Skip over already loaded specializations.
+      if (!Specs[I + 1].ODRHash)
+        continue;
+      if (!OnlyPartial || Specs[I + 1].IsPartial)
+        (void)loadLazySpecializationImpl(Specs[I + 1]);
+    }
+  }
+}
+
+Decl *RedeclarableTemplateDecl::loadLazySpecializationImpl(
+    LazySpecializationInfo &LazySpecInfo) const {
+  GlobalDeclID ID = LazySpecInfo.DeclID;
+  assert(ID.isValid() && "Loading already loaded specialization!");
+  // Note that we loaded the specialization.
+  LazySpecInfo.DeclID = GlobalDeclID();
+  LazySpecInfo.ODRHash = LazySpecInfo.IsPartial = 0;
+  return getASTContext().getExternalSource()->GetExternalDecl(ID);
+}
+
+void RedeclarableTemplateDecl::loadLazySpecializationsImpl(
+    ArrayRef<TemplateArgument> Args, TemplateParameterList *TPL) const {
+  CommonBase *CommonBasePtr = getMostRecentDecl()->getCommonPtr();
+  if (auto *Specs = CommonBasePtr->LazySpecializations) {
+    unsigned Hash = TemplateArgumentList::ComputeODRHash(Args);
+    unsigned N = Specs[0].DeclID.getRawValue();
+    for (unsigned I = 0; I != N; ++I)
+      if (Specs[I + 1].ODRHash && Specs[I + 1].ODRHash == Hash)
+        (void)loadLazySpecializationImpl(Specs[I + 1]);
   }
 }
 
@@ -372,6 +401,8 @@ RedeclarableTemplateDecl::findSpecializationImpl(
     ProfileArguments&&... ProfileArgs) {
   using SETraits = SpecEntryTraits<EntryType>;
 
+  loadLazySpecializationsImpl(std::forward<ProfileArguments>(ProfileArgs)...);
+
   llvm::FoldingSetNodeID ID;
   EntryType::Profile(ID, std::forward<ProfileArguments>(ProfileArgs)...,
                      getASTContext());
@@ -387,10 +418,14 @@ void RedeclarableTemplateDecl::addSpecializationImpl(
 
   if (InsertPos) {
 #ifndef NDEBUG
+    auto Args = SETraits::getTemplateArgs(Entry);
+    // Due to hash collisions, it can happen that we load another template
+    // specialization with the same hash. This is fine, as long as the next
+    // call to findSpecializationImpl does not find a matching Decl for the
+    // template arguments.
+    loadLazySpecializationsImpl(Args);
     void *CorrectInsertPos;
-    assert(!findSpecializationImpl(Specializations,
-                                   CorrectInsertPos,
-                                   SETraits::getTemplateArgs(Entry)) &&
+    assert(!findSpecializationImpl(Specializations, CorrectInsertPos, Args) &&
            InsertPos == CorrectInsertPos &&
            "given incorrect InsertPos for specialization");
 #endif
@@ -448,12 +483,14 @@ FunctionTemplateDecl::getSpecializations() const {
 FunctionDecl *
 FunctionTemplateDecl::findSpecialization(ArrayRef<TemplateArgument> Args,
                                          void *&InsertPos) {
-  return findSpecializationImpl(getSpecializations(), InsertPos, Args);
+  auto *Common = getCommonPtr();
+  return findSpecializationImpl(Common->Specializations, InsertPos, Args);
 }
 
 void FunctionTemplateDecl::addSpecialization(
       FunctionTemplateSpecializationInfo *Info, void *InsertPos) {
-  addSpecializationImpl<FunctionTemplateDecl>(getSpecializations(), Info,
+  auto *Common = getCommonPtr();
+  addSpecializationImpl<FunctionTemplateDecl>(Common->Specializations, Info,
                                               InsertPos);
 }
 
@@ -513,8 +550,9 @@ ClassTemplateDecl *ClassTemplateDecl::CreateDeserialized(ASTContext &C,
                                        DeclarationName(), nullptr, nullptr);
 }
 
-void ClassTemplateDecl::LoadLazySpecializations() const {
-  loadLazySpecializationsImpl();
+void ClassTemplateDecl::LoadLazySpecializations(
+    bool OnlyPartial /*=false*/) const {
+  loadLazySpecializationsImpl(OnlyPartial);
 }
 
 llvm::FoldingSetVector<ClassTemplateSpecializationDecl> &
@@ -525,7 +563,7 @@ ClassTemplateDecl::getSpecializations() const {
 
 llvm::FoldingSetVector<ClassTemplatePartialSpecializationDecl> &
 ClassTemplateDecl::getPartialSpecializations() const {
-  LoadLazySpecializations();
+  LoadLazySpecializations(/*PartialOnly = */ true);
   return getCommonPtr()->PartialSpecializations;
 }
 
@@ -539,12 +577,15 @@ ClassTemplateDecl::newCommon(ASTContext &C) const {
 ClassTemplateSpecializationDecl *
 ClassTemplateDecl::findSpecialization(ArrayRef<TemplateArgument> Args,
                                       void *&InsertPos) {
-  return findSpecializationImpl(getSpecializations(), InsertPos, Args);
+  auto *Common = getCommonPtr();
+  return findSpecializationImpl(Common->Specializations, InsertPos, Args);
 }
 
 void ClassTemplateDecl::AddSpecialization(ClassTemplateSpecializationDecl *D,
                                           void *InsertPos) {
-  addSpecializationImpl<ClassTemplateDecl>(getSpecializations(), D, InsertPos);
+  auto *Common = getCommonPtr();
+  addSpecializationImpl<ClassTemplateDecl>(Common->Specializations, D,
+                                           InsertPos);
 }
 
 ClassTemplatePartialSpecializationDecl *
@@ -897,6 +938,14 @@ TemplateArgumentList::CreateCopy(ASTContext &Context,
   return new (Mem) TemplateArgumentList(Args);
 }
 
+unsigned TemplateArgumentList::ComputeODRHash(ArrayRef<TemplateArgument> Args) {
+  ODRHash Hasher;
+  for (TemplateArgument TA : Args)
+    Hasher.AddTemplateArgument(TA);
+
+  return Hasher.CalculateHash();
+}
+
 FunctionTemplateSpecializationInfo *FunctionTemplateSpecializationInfo::Create(
     ASTContext &C, FunctionDecl *FD, FunctionTemplateDecl *Template,
     TemplateSpecializationKind TSK, TemplateArgumentList *TemplateArgs,
@@ -1262,8 +1311,9 @@ VarTemplateDecl *VarTemplateDecl::CreateDeserialized(ASTContext &C,
                                      DeclarationName(), nullptr, nullptr);
 }
 
-void VarTemplateDecl::LoadLazySpecializations() const {
-  loadLazySpecializationsImpl();
+void VarTemplateDecl::LoadLazySpecializations(
+    bool OnlyPartial /*=false*/) const {
+  loadLazySpecializationsImpl(OnlyPartial);
 }
 
 llvm::FoldingSetVector<VarTemplateSpecializationDecl> &
@@ -1274,7 +1324,7 @@ VarTemplateDecl::getSpecializations() const {
 
 llvm::FoldingSetVector<VarTemplatePartialSpecializationDecl> &
 VarTemplateDecl::getPartialSpecializations() const {
-  LoadLazySpecializations();
+  LoadLazySpecializations(/*PartialOnly = */ true);
   return getCommonPtr()->PartialSpecializations;
 }
 
@@ -1288,12 +1338,14 @@ VarTemplateDecl::newCommon(ASTContext &C) const {
 VarTemplateSpecializationDecl *
 VarTemplateDecl::findSpecialization(ArrayRef<TemplateArgument> Args,
                                     void *&InsertPos) {
-  return findSpecializationImpl(getSpecializations(), InsertPos, Args);
+  auto *Common = getCommonPtr();
+  return findSpecializationImpl(Common->Specializations, InsertPos, Args);
 }
 
 void VarTemplateDecl::AddSpecialization(VarTemplateSpecializationDecl *D,
                                         void *InsertPos) {
-  addSpecializationImpl<VarTemplateDecl>(getSpecializations(), D, InsertPos);
+  auto *Common = getCommonPtr();
+  addSpecializationImpl<VarTemplateDecl>(Common->Specializations, D, InsertPos);
 }
 
 VarTemplatePartialSpecializationDecl *
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index 1929314363817a..a8a3a5200d61ed 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -828,6 +828,21 @@ void ODRHash::AddDecl(const Decl *D) {
     for (const TemplateArgument &TA : List.asArray())
       AddTemplateArgument(TA);
   }
+
+  // If this was a specialization we should take into account its template
+  // arguments. This helps to reduce collisions coming when visiting template
+  // specialization types (eg. when processing type template arguments).
+  ArrayRef<TemplateArgument> Args;
+  if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D))
+    Args = CTSD->getTemplateArgs().asArray();
+  else if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(D))
+    Args = VTSD->getTemplateArgs().asArray();
+  else if (auto *FD = dyn_cast<FunctionDecl>(D))
+    if (FD->getTemplateSpecializationArgs())
+      Args = FD->getTemplateSpecializationArgs()->asArray();
+
+  for (auto &TA : Args)
+    AddTemplateArgument(TA);
 }
 
 namespace {
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 1c94e8e9a212a9..8bba98ef33302b 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -7640,14 +7640,23 @@ void ASTReader::CompleteRedeclChain(const Decl *D) {
     }
   }
 
-  if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D))
-    CTSD->getSpecializedTemplate()->LoadLazySpecializations();
-  if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(D))
-    VTSD->getSpecializedTemplate()->LoadLazySpecializations();
-  if (auto *FD = dyn_cast<FunctionDecl>(D)) {
-    if (auto *Template = FD->getPrimaryTemplate())
-      Template->LoadLazySpecializations();
-  }
+  RedeclarableTemplateDecl *Template = nullptr;
+  ArrayRef<TemplateArgument> Args;
+  if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D)) {
+    Template = CTSD->getSpecializedTemplate();
+    Args = CTSD->getTemplateArgs().asArray();
+  } else if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(D)) {
+    Template = VTSD->getSpecializedTemplate();
+    Args = VTSD->getTemplateArgs().asArray();
+  } else if (auto *FD = dyn_cast<FunctionDecl>(D)) {
+    if (auto *Tmplt = FD->getPrimaryTemplate()) {
+      Template = Tmplt;
+      Args = FD->getTemplateSpecializationArgs()->asArray();
+    }
+  }
+
+  if (Template)
+    Template->loadLazySpecializationsImpl(Args);
 }
 
 CXXCtorInitializer **
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 33fcddbbdb2f15..6816bbcd45dcbe 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -187,9 +187,19 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> {
 
   std::string readString() { return Record.readString(); }
 
-  void readDeclIDList(SmallVectorImpl<GlobalDeclID> &IDs) {
+  using LazySpecializationInfo =
+      RedeclarableTemplateDecl::LazySpecializationInfo;
+
+  LazySpecializationInfo ReadLazySpecializationInfo() {
+    GlobalDeclID ID = readDeclID();
+    unsigned Hash = Record.readInt();
+    bool IsPartial = Record.readInt();
+    return LazySpecializationInfo(ID, Hash, IsPartial);
+  }
+
+  void readDeclIDList(SmallVectorImpl<LazySpecializationInfo> &IDs) {
     for (unsigned I = 0, Size = Record.readInt(); I != Size; ++I)
-      IDs.push_back(readDeclID());
+      IDs.push_back(ReadLazySpecializationInfo());
   }
 
   Decl *readDecl() { return Record.readDecl(); }
@@ -285,7 +295,8 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> {
         ThisDeclID(thisDeclID), ThisDeclLoc(ThisDeclLoc) {}
 
   template <typename T>
-  static void AddLazySpecializations(T *D, SmallVectorImpl<GlobalDeclID> &IDs) {
+  static void
+  AddLazySpecializations(T *D, SmallVectorImpl<LazySpecializationInfo> &IDs) {
     if (IDs.empty())
       return;
 
@@ -295,14 +306,13 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> {
     auto *&LazySpecializations = D->getCommonPtr()->LazySpecializations;
 
     if (auto &Old = LazySpecializations) {
-      IDs.insert(IDs.end(), Old + 1, Old + 1 + Old[0].getRawValue());
+      IDs.insert(IDs.end(), Old + 1, Old + 1 + Old[0].DeclID.getRawValue());
       llvm::sort(IDs);
       IDs.erase(std::unique(IDs.begin(), IDs.end()), IDs.end());
     }
-
-    auto *Result = new (C) GlobalDeclID[1 + IDs.size()];
-    *Result = GlobalDeclID(IDs.size());
-
+    auto *Result = new (C)
+        RedeclarableTemplateDecl::LazySpecializationInfo[1 + IDs.size()];
+    Result->DeclID = GlobalDeclID(IDs.size());
     std::copy(IDs.begin(), IDs.end(), Result + 1);
 
     LazySpecializations = Result;
@@ -335,7 +345,9 @@ class ASTDeclReader : public DeclVisitor<ASTDeclReader, void> {
   void ReadFunctionDefinition(FunctionDecl *FD);
   void Visit(Decl *D);
 
-  void UpdateDecl(Decl *D, SmallVectorImpl<GlobalDeclID> &);
+  void UpdateDecl(
+      Decl *D,
+      SmallVectorImpl<RedeclarableTemplateDecl::LazySpecializationInfo> &);
 
   static void setNextObjCCategory(ObjCCategoryDecl *Cat,
                                   ObjCCategoryDecl *Next) {
@@ -2456,7 +2468,7 @@ void ASTDeclReader::VisitClassTemplateDecl(ClassTemplateDecl *D) {
   if (ThisDeclID == Redecl.getFirstID()) {
     // This ClassTemplateDecl owns a CommonPtr; read it to keep track of all of
     // the specializations.
-    SmallVector<GlobalDeclID, 32> SpecIDs;
+    SmallVector<LazySpecializationInfo, 32> SpecIDs;
     readDeclIDList(SpecIDs);
     ASTDeclReader::AddLazySpecializations(D, SpecIDs);
   }
@@ -2484,7 +2496,7 @@ void ASTDeclReader::VisitVarTemplateDecl(VarTemplateDecl *D) {
   if (ThisDeclID == Redecl.getFirstID()) {
     // This VarTemplateDecl owns a CommonPtr; read it to keep track of all of
     // the specializations.
-    SmallVector<GlobalDeclID, 32> SpecIDs;
+    SmallVector<LazySpecializationInfo, 32> SpecIDs;
     readDeclIDList(SpecIDs);
     ASTDeclReader::AddLazySpecializations(D, SpecIDs);
   }
@@ -2585,7 +2597,7 @@ void ASTDeclReader::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) {
 
   if (ThisDeclID == Redecl.getFirstID()) {
     // This FunctionTemplateDecl owns a CommonPtr; read it.
-    SmallVector<GlobalDeclID, 32> SpecIDs;
+    SmallVector<LazySpecializationInfo, 32> SpecIDs;
     readDeclIDList(SpecIDs);
     ASTDeclReader::AddLazySpecializations(D, SpecIDs);
   }
@@ -4286,7 +4298,9 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) {
   ProcessingUpdatesRAIIObj ProcessingUpdates(*this);
   DeclUpdateOffsetsMap::iterator UpdI = DeclUpdateOffsets.find(ID);
 
-  SmallVector<GlobalDeclID, 8> PendingLazySpecializationIDs;
+  using LazySpecializationInfo =
+      RedeclarableTemplateDecl::LazySpecializationInfo;
+  llvm::SmallVector<LazySpecializationInfo, 8> PendingLazySpecializationIDs;
 
   if (UpdI != DeclUpdateOffsets.end()) {
     auto UpdateOffsets = std::move(UpdI->second);
@@ -4563,7 +4577,7 @@ static void forAllLaterRedecls(DeclT *D, Fn F) {
 
 void ASTDeclReader::UpdateDecl(
     Decl *D,
-    llvm::SmallVectorImpl<GlobalDeclID> &PendingLazySpecializationIDs) {
+    SmallVectorImpl<LazySpecializationInfo> &PendingLazySpecializationIDs) {
   while (Record.getIdx() < Record.size()) {
     switch ((DeclUpdateKind)Record.readInt()) {
     case UPD_CXX_ADDED_IMPLICIT_MEMBER: {
@@ -4576,7 +4590,7 @@ void ASTDeclReader::UpdateDecl(
 
     case UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION:
       // It will be added to the template's lazy specialization set.
-      PendingLazySpecializationIDs.push_back(readDeclID());
+      PendingLazySpecializationIDs.push_back(ReadLazySpecializationInfo());
       break;
 
     case UPD_CXX_ADDED_ANONYMOUS_NAMESPACE: {
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 016d1d4acad137..455e8f0e749435 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -5796,12 +5796,29 @@ void ASTWriter::WriteDeclUpdatesBlocks(ASTContext &Context,
 
       switch (Kind) {
       case UPD_CXX_ADDED_IMPLICIT_MEMBER:
-      case UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION:
       case UPD_CXX_ADDED_ANONYMOUS_NAMESPACE:
         assert(Update.getDecl() && "no decl to add?");
         Record.AddDeclRef(Update.getDecl());
         break;
-
+      case UPD_CXX_ADDED_TEMPLATE_SPECIALIZATION: {
+        const Decl *Spec = Update.getDecl();
+        assert(Spec && "no decl to add?");
+        Record.AddDeclRef(Spec);
+        ArrayRef<TemplateArgument> Args;
+        if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(Spec))
+          Args = CTSD->getTemplateArgs().asArray();
+        else if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(Spec))
+          Args = VTSD->getTemplateArgs().asArray();
+        else if (auto *FD = dyn_cast<FunctionDecl>(Spec))
+          Args = FD->getTemplateSpecializationArgs()->asArray();
+        assert(Args.size());
+        Record.push_back(TemplateArgumentList::ComputeODRHash(Args));
+        bool IsPartialSpecialization =
+            isa<ClassTemplatePartialSpecializationDecl>(Spec) ||
+            isa<VarTemplatePartialSpecializationDecl>(Spec);
+        Record.push_back(IsPartialSpecialization);
+        break;
+      }
       case UPD_CXX_ADDED_FUNCTION_DEFINITION:
       case UPD_CXX_ADDED_VAR_DEFINITION:
         break;
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index ad357e30d57529..71a9bd1aefade6 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -177,11 +177,12 @@ namespace clang {
       Record.AddSourceLocation(typeParams->getRAngleLoc());
     }
 
-    /// Add to the record the first declaration from each module file that
-    /// provides a declaration of D. The intent is to provide a sufficient
-    /// set such that reloading this set will load all current redeclarations.
-    void AddFirstDeclFromEachModule(const Decl *D, bool IncludeLocal) {
-      llvm::MapVector<ModuleFile*, const Decl*> Firsts;
+    /// Collect the first declaration from each module file that provides a
+    /// declaration of D.
+    void CollectFirstDeclFromEachModule(
+        const Decl *D, bool IncludeLocal,
+        llvm::MapVector<ModuleFile *, const Decl *> &Firsts) {
+
       // FIXME: We can skip entries that we know are implied by others.
       for (const Decl *R = D->getMostRecentDecl(); R; R = R->getPreviousDecl()) {
         if (R->isFromASTFile())
@@ -189,10 +190,49 @@ namespace clang {
         else if (IncludeLocal)
           Firsts[nullptr] = R;
       }
+    }
+
+    /// Add to the record the first declaration from each module file that
+    /// provides a declaration of D. The intent is to provide a sufficient
+    /// set such that reloading this set will load all current redeclarations.
+    void AddFirstDeclFromEachModule(const Decl *D, bool IncludeLocal) {
+      llvm::MapVector<ModuleFile *, const Decl *> Firsts;
+      CollectFirstDeclFromEachModule(D, IncludeLocal, Firsts);
+
       for (const auto &F : Firsts)
         Record.AddDeclRef(F.second);
     }
 
+    /// Add to the record the first template specialization from each module
+    /// file that provides a declaration of D. We store the DeclId and an
+    /// ODRHash of the template arguments of D which should provide enough
+    /// information to load D only if the template instantiator needs it.
+    void AddFirstSpecializationDeclFromEachModule(const Decl *D,
+                                                  bool IncludeLocal) {
+      assert(isa<ClassTemplateSpecializationDecl>(D) ||
+             isa<VarTemplateSpecializationDecl>(D) ||
+             isa<FunctionDecl>(D) && "Must not be called with other decls");
+      llvm::MapVector<ModuleFile *, const Decl *> Firsts;
+      CollectFirstDeclFromEachModule(D, IncludeLocal, Firsts);
+
+      for (const auto &F : Firsts) {
+        Record.AddDeclRef(F.second);
+        ArrayRef<TemplateArgument> Args;
+        if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D))
+          Args = CTSD->getTemplateArgs().asArray();
+        else if (auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(D))
+          Args = VTSD->getTemplateArgs().asArray();
+        else if (auto *FD = dyn_cast<FunctionDecl>(D))
+          Args = FD->getTemplateSpecializationArgs()->asArray();
+        assert(Args.size());
+        Record.push_back(TemplateArgumentList::ComputeODRHash(Args));
+        bool IsPartialSpecialization =
+            isa<ClassTemplatePartialSpecializationDecl>(D) ||
+            isa<VarTemplatePartialSpecializationDecl>(D);
+        Record.push_back(IsPartialSpecialization);
+      }
+    }
+
     /// Get the specialization decl from an entry in the specialization list.
     template <typename EntryType>
     typename RedeclarableTemplateDecl::SpecEntryTraits<EntryType>::DeclType *
@@ -205,8 +245,9 @@ namespace clang {
     decltype(T::PartialSpecializations) &getPartialSpecializations(T *Common) {
       return Common->PartialSpecializations;
     }
-    ArrayRef<Decl> getPartialSpecializations(FunctionTemplateDecl::Common *) {
-      return {};
+    MutableArrayRef<FunctionTemplateSpecializationInfo>
+    getPartialSpecializations(FunctionTemplateDecl::Common *) {
+      return std::nullopt;
     }
 
     template<typename DeclTy>
@@ -222,9 +263,12 @@ namespace clang {
         assert(!Common->LazySpecializations);
       }
 
-      ArrayRef<GlobalDeclID> LazySpecializations;
+      using LazySpecializationInfo =
+          RedeclarableTemplateDecl::LazySpecializationInfo;
+      ArrayRef<LazySpecializationInfo> LazySpecializations;
       if (auto *LS = Common->LazySpecializations)
-        LazySpecializations = llvm::ArrayRef(LS + 1, LS[0].getRawValue());
+        LazySpecializations =
+            llvm::ArrayRef(LS + 1, LS[0].DeclID.getRawValue());
 
       // Add a slot to the record for the number of specializations.
       unsigned I = Record.size();
@@ -240,14 +284,20 @@ namespace clang {
 
       for (auto *D : Specs) {
         assert(D->isCanonicalDecl() && "non-canonical decl in set");
-        AddFirstDeclFromEachModule(D, /*IncludeLocal*/true);
+        AddFirstSpecializationDeclFromEachModule(D, /*IncludeLocal*/ true);
+      }
+      for (auto &SpecInfo : LazySpecializations) {
+        Record.push_back(SpecInfo.DeclID.getRawValue());
+        Record.push_back(SpecInfo.ODRHash);
+        Record.push_back(SpecInfo.IsPartial);
       }
-      Record.append(
-          DeclIDIterator<GlobalDeclID, DeclID>(LazySpecializations.begin()),
-          DeclIDIterator<GlobalDeclID, DeclID>(LazySpecializations.end()));
 
-      // Update the size entry we added earlier.
-      Record[I] = Record.size() - I - 1;
+      // Update the size entry we added earlier. We linerized the
+      // LazySpecializationInfo members and we need to adjust the size as we
+      // will read them always together.
+      assert((Record.size() - I - 1) % 3 == 0 &&
+             "Must be divisible by LazySpecializationInfo count!");
+      Record[I] = (Record.size() - I - 1) / 3;
     }
 
     /// Ensure that this template specialization is associated with the specified
diff --git a/clang/test/Modules/cxx-templates.cpp b/clang/test/Modules/cxx-templates.cpp
index b7d5741e69af61..e10ba7c2ac3ef2 100644
--- a/clang/test/Modules/cxx-templates.cpp
+++ b/clang/test/Modules/cxx-templates.cpp
@@ -251,7 +251,7 @@ namespace Std {
 
 // CHECK-DUMP:      ClassTemplateDecl {{.*}} <{{.*[/\\]}}cxx-templates-common.h:1:1, {{.*}}>  col:{{.*}} in cxx_templates_common SomeTemplate
 // CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate
-// CHECK-DUMP-NEXT:     TemplateArgument type 'char[2]'
+// CHECK-DUMP-NEXT:     TemplateArgument type 'char[1]'
 // CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} SomeTemplate definition
 // CHECK-DUMP-NEXT:     DefinitionData
 // CHECK-DUMP-NEXT:       DefaultConstructor
@@ -260,9 +260,9 @@ namespace Std {
 // CHECK-DUMP-NEXT:       CopyAssignment
 // CHECK-DUMP-NEXT:       MoveAssignment
 // CHECK-DUMP-NEXT:       Destructor
-// CHECK-DUMP-NEXT:     TemplateArgument type 'char[2]'
-// CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate
 // CHECK-DUMP-NEXT:     TemplateArgument type 'char[1]'
+// CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate
+// CHECK-DUMP-NEXT:     TemplateArgument type 'char[2]'
 // CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} SomeTemplate definition
 // CHECK-DUMP-NEXT:     DefinitionData
 // CHECK-DUMP-NEXT:       DefaultConstructor
@@ -271,4 +271,4 @@ namespace Std {
 // CHECK-DUMP-NEXT:       CopyAssignment
 // CHECK-DUMP-NEXT:       MoveAssignment
 // CHECK-DUMP-NEXT:       Destructor
-// CHECK-DUMP-NEXT:     TemplateArgument type 'char[1]'
+// CHECK-DUMP-NEXT:     TemplateArgument type 'char[2]'
diff --git a/clang/test/Modules/odr_hash.cpp b/clang/test/Modules/odr_hash.cpp
index fa8b2c81ab46e1..7cea3af3f41bdd 100644
--- a/clang/test/Modules/odr_hash.cpp
+++ b/clang/test/Modules/odr_hash.cpp
@@ -3084,8 +3084,8 @@ struct S5 {
 };
 #else
 S5 s5;
-// expected-error at second.h:* {{'PointersAndReferences::S5::x' from module 'SecondModule' is not present in definition of 'PointersAndReferences::S5' in module 'FirstModule'}}
-// expected-note at first.h:* {{declaration of 'x' does not match}}
+// expected-error at first.h:* {{'PointersAndReferences::S5::x' from module 'FirstModule' is not present in definition of 'PointersAndReferences::S5' in module 'SecondModule'}}
+// expected-note at second.h:* {{declaration of 'x' does not match}}
 #endif
 
 #if defined(FIRST)



More information about the cfe-commits mailing list