Skip to content

[clang] ODR hashes depth+index and not name of TemplateTypeParm #144796

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

emaxx-google
Copy link
Contributor

Change the ODR hashing logic to use the depth+index indices instead of template parameter names. This prevents spurious ODR errors in header module builds when the type canonicalization picks up different expressions - with different template parameter names (profiling used for canonicalization ignores template parameter names). This fixes #143152.

This commit removes test assertions from the regression test of #108866 - the compiler errors asserted by that test stop firing with this commit. It's questionable whether these diagnostics were correct. We add a FIXME to find a better regression test for that fix.

Change the ODR hashing logic to use the depth+index indices instead
of template parameter names. This prevents spurious ODR errors in
header module builds when the type canonicalization picks up
different expressions - with different template parameter names
(profiling used for canonicalization ignores template parameter
names). This fixes llvm#143152.

This commit removes test assertions from the regression test of
llvm#108866 - the compiler
errors asserted by that test stop firing with this commit. It's
questionable whether these diagnostics were correct. We add a
FIXME to find a better regression test for that fix.
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:modules C++20 modules and Clang Header Modules labels Jun 18, 2025
@llvmbot
Copy link
Member

llvmbot commented Jun 18, 2025

@llvm/pr-subscribers-clang

Author: Maksim Ivanov (emaxx-google)

Changes

Change the ODR hashing logic to use the depth+index indices instead of template parameter names. This prevents spurious ODR errors in header module builds when the type canonicalization picks up different expressions - with different template parameter names (profiling used for canonicalization ignores template parameter names). This fixes #143152.

This commit removes test assertions from the regression test of #108866 - the compiler errors asserted by that test stop firing with this commit. It's questionable whether these diagnostics were correct. We add a FIXME to find a better regression test for that fix.


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

3 Files Affected:

  • (modified) clang/lib/AST/ODRHash.cpp (+17-1)
  • (modified) clang/test/Modules/odr_hash.cpp (+23)
  • (modified) clang/test/PCH/race-condition.cpp (+6-5)
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index f8446dfbc6859..84fac90a03e87 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -828,7 +828,23 @@ void ODRHash::AddDecl(const Decl *D) {
     return;
   }
 
-  AddDeclarationName(ND->getDeclName());
+  // For template parameters, use depth+index instead of name, because type
+  // canonicalization can change the name of the template parameter.
+  if (auto *TTPD = dyn_cast<TemplateTypeParmDecl>(ND)) {
+    ID.AddInteger(TTPD->getDepth());
+    ID.AddInteger(TTPD->getIndex());
+    AddBoolean(TTPD->isParameterPack());
+  } else if (auto *NTTPD = dyn_cast<NonTypeTemplateParmDecl>(ND)) {
+    ID.AddInteger(NTTPD->getDepth());
+    ID.AddInteger(NTTPD->getIndex());
+    AddBoolean(NTTPD->isParameterPack());
+  } else if (auto *TTPD = dyn_cast<TemplateTemplateParmDecl>(ND)) {
+    ID.AddInteger(TTPD->getDepth());
+    ID.AddInteger(TTPD->getIndex());
+    AddBoolean(TTPD->isParameterPack());
+  } else {
+    AddDeclarationName(ND->getDeclName());
+  }
 
   // If this was a specialization we should take into account its template
   // arguments. This helps to reduce collisions coming when visiting template
diff --git a/clang/test/Modules/odr_hash.cpp b/clang/test/Modules/odr_hash.cpp
index da24b1fe8729a..8ef53e32f2e95 100644
--- a/clang/test/Modules/odr_hash.cpp
+++ b/clang/test/Modules/odr_hash.cpp
@@ -5164,6 +5164,29 @@ namespace A {
 A::X x;
 #endif
 
+namespace TemplateDecltypeOperator {
+
+#if defined(FIRST) || defined(SECOND)
+template <class T6>
+T6 func();
+#endif
+
+#if defined(SECOND)
+template <class UnrelatedT>
+using UnrelatedAlias = decltype(func<UnrelatedT>())();
+#endif
+
+#if defined(FIRST) || defined(SECOND)
+class A {
+  template <class T6>
+  operator decltype(func<T6>()) () {}
+};
+#else
+A a;
+#endif
+
+}
+
 // Keep macros contained to one file.
 #ifdef FIRST
 #undef FIRST
diff --git a/clang/test/PCH/race-condition.cpp b/clang/test/PCH/race-condition.cpp
index 752b0cc3ff628..666d92ed2ce8b 100644
--- a/clang/test/PCH/race-condition.cpp
+++ b/clang/test/PCH/race-condition.cpp
@@ -31,11 +31,12 @@ constexpr enable_if_t<meta<F>::value == 2, void> midpoint(F) {}
 
 #else
 
-// expected-error@27{{'N::midpoint' has different definitions in different modules; defined here first difference is 1st parameter with type 'F'}}
-// expected-error@24{{'N::midpoint' has different definitions in different modules; defined here first difference is 1st parameter with type 'U'}}
-// expected-note@21{{but in '' found 1st parameter with type 'T'}}
+// FIXME: Change the test to trigger a suitable error: previously the test
+// asserted the ODR error ("'N::midpoint' has different definitions in different
+// modules"), which isn't fully correct as there's only one module, and since a
+// change in the ODR hash calculation this error isn't triggered anymore.
+
 int x = N::something;
-// expected-error@37{{no member named 'something' in namespace 'N'}}
-// expected-note@21{{but in '' found 1st parameter with type 'T'}}
+// expected-error@38{{no member named 'something' in namespace 'N'}}
 
 #endif

@llvmbot
Copy link
Member

llvmbot commented Jun 18, 2025

@llvm/pr-subscribers-clang-modules

Author: Maksim Ivanov (emaxx-google)

Changes

Change the ODR hashing logic to use the depth+index indices instead of template parameter names. This prevents spurious ODR errors in header module builds when the type canonicalization picks up different expressions - with different template parameter names (profiling used for canonicalization ignores template parameter names). This fixes #143152.

This commit removes test assertions from the regression test of #108866 - the compiler errors asserted by that test stop firing with this commit. It's questionable whether these diagnostics were correct. We add a FIXME to find a better regression test for that fix.


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

3 Files Affected:

  • (modified) clang/lib/AST/ODRHash.cpp (+17-1)
  • (modified) clang/test/Modules/odr_hash.cpp (+23)
  • (modified) clang/test/PCH/race-condition.cpp (+6-5)
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index f8446dfbc6859..84fac90a03e87 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -828,7 +828,23 @@ void ODRHash::AddDecl(const Decl *D) {
     return;
   }
 
-  AddDeclarationName(ND->getDeclName());
+  // For template parameters, use depth+index instead of name, because type
+  // canonicalization can change the name of the template parameter.
+  if (auto *TTPD = dyn_cast<TemplateTypeParmDecl>(ND)) {
+    ID.AddInteger(TTPD->getDepth());
+    ID.AddInteger(TTPD->getIndex());
+    AddBoolean(TTPD->isParameterPack());
+  } else if (auto *NTTPD = dyn_cast<NonTypeTemplateParmDecl>(ND)) {
+    ID.AddInteger(NTTPD->getDepth());
+    ID.AddInteger(NTTPD->getIndex());
+    AddBoolean(NTTPD->isParameterPack());
+  } else if (auto *TTPD = dyn_cast<TemplateTemplateParmDecl>(ND)) {
+    ID.AddInteger(TTPD->getDepth());
+    ID.AddInteger(TTPD->getIndex());
+    AddBoolean(TTPD->isParameterPack());
+  } else {
+    AddDeclarationName(ND->getDeclName());
+  }
 
   // If this was a specialization we should take into account its template
   // arguments. This helps to reduce collisions coming when visiting template
diff --git a/clang/test/Modules/odr_hash.cpp b/clang/test/Modules/odr_hash.cpp
index da24b1fe8729a..8ef53e32f2e95 100644
--- a/clang/test/Modules/odr_hash.cpp
+++ b/clang/test/Modules/odr_hash.cpp
@@ -5164,6 +5164,29 @@ namespace A {
 A::X x;
 #endif
 
+namespace TemplateDecltypeOperator {
+
+#if defined(FIRST) || defined(SECOND)
+template <class T6>
+T6 func();
+#endif
+
+#if defined(SECOND)
+template <class UnrelatedT>
+using UnrelatedAlias = decltype(func<UnrelatedT>())();
+#endif
+
+#if defined(FIRST) || defined(SECOND)
+class A {
+  template <class T6>
+  operator decltype(func<T6>()) () {}
+};
+#else
+A a;
+#endif
+
+}
+
 // Keep macros contained to one file.
 #ifdef FIRST
 #undef FIRST
diff --git a/clang/test/PCH/race-condition.cpp b/clang/test/PCH/race-condition.cpp
index 752b0cc3ff628..666d92ed2ce8b 100644
--- a/clang/test/PCH/race-condition.cpp
+++ b/clang/test/PCH/race-condition.cpp
@@ -31,11 +31,12 @@ constexpr enable_if_t<meta<F>::value == 2, void> midpoint(F) {}
 
 #else
 
-// expected-error@27{{'N::midpoint' has different definitions in different modules; defined here first difference is 1st parameter with type 'F'}}
-// expected-error@24{{'N::midpoint' has different definitions in different modules; defined here first difference is 1st parameter with type 'U'}}
-// expected-note@21{{but in '' found 1st parameter with type 'T'}}
+// FIXME: Change the test to trigger a suitable error: previously the test
+// asserted the ODR error ("'N::midpoint' has different definitions in different
+// modules"), which isn't fully correct as there's only one module, and since a
+// change in the ODR hash calculation this error isn't triggered anymore.
+
 int x = N::something;
-// expected-error@37{{no member named 'something' in namespace 'N'}}
-// expected-note@21{{but in '' found 1st parameter with type 'T'}}
+// expected-error@38{{no member named 'something' in namespace 'N'}}
 
 #endif

#endif

#if defined(FIRST) || defined(SECOND)
class A {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Without the changes in this PR, this test would fail with this message:

'TemplateDecltypeOperator::A' with definition in module 'SecondModule' has different definitions in different modules; first difference is this function template
but in 'FirstModule' found different function template

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.

[clang] ODR violation with header modules and template conversion operator
2 participants