[clang] [Clang][Modules] Fix crash when redefining default template argument (PR #185237)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Mar 7 17:10:10 PST 2026
https://github.com/Qayyum-Ahmed updated https://github.com/llvm/llvm-project/pull/185237
>From 8d19de857b05afd966e08fc799c21e06d6985270 Mon Sep 17 00:00:00 2001
From: Qayyum <qayyumahmed553 at gmail.com>
Date: Sun, 8 Mar 2026 03:00:57 +0500
Subject: [PATCH] [Clang][Modules] Fix crash when redefining default template
argument
---
clang/lib/Sema/SemaTemplate.cpp | 70 +++++++++++++------
.../CXX/module/basic/basic.def.odr/p7.cppm | 63 +++++++++++++++++
2 files changed, 111 insertions(+), 22 deletions(-)
create mode 100644 clang/test/CXX/module/basic/basic.def.odr/p7.cppm
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 9b0bec20618a0..e713011ca80e4 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -26,6 +26,7 @@
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/PartialDiagnostic.h"
+#include "clang/Basic/Module.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Sema/DeclSpec.h"
@@ -2472,13 +2473,23 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams,
NewDefaultLoc = NewTypeParm->getDefaultArgumentLoc();
SawDefaultArgument = true;
- if (!OldTypeParm->getOwningModule())
+ bool SameDefault =
+ getASTContext().isSameDefaultTemplateArgument(OldTypeParm,
+ NewTypeParm);
+ if (Module *ImportedM = OldTypeParm->getImportedOwningModule()) {
+ if (!SameDefault) {
+ InconsistentDefaultArg = true;
+ PrevModuleName = ImportedM->getFullModuleName();
+ }
+ } else if (Module *LocalM = OldTypeParm->getLocalOwningModule()) {
+ if (LocalM->isModuleMapModule()) {
+ if (!SameDefault)
+ RedundantDefaultArg = true;
+ } else {
+ RedundantDefaultArg = true;
+ }
+ } else {
RedundantDefaultArg = true;
- else if (!getASTContext().isSameDefaultTemplateArgument(OldTypeParm,
- NewTypeParm)) {
- InconsistentDefaultArg = true;
- PrevModuleName =
- OldTypeParm->getImportedOwningModule()->getFullModuleName();
}
PreviousDefaultArgLoc = NewDefaultLoc;
} else if (OldTypeParm && OldTypeParm->hasDefaultArgument()) {
@@ -2527,13 +2538,22 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams,
OldDefaultLoc = OldNonTypeParm->getDefaultArgumentLoc();
NewDefaultLoc = NewNonTypeParm->getDefaultArgumentLoc();
SawDefaultArgument = true;
- if (!OldNonTypeParm->getOwningModule())
+ bool SameDefault = getASTContext().isSameDefaultTemplateArgument(
+ OldNonTypeParm, NewNonTypeParm);
+ if (Module *ImportedM = OldNonTypeParm->getImportedOwningModule()) {
+ if (!SameDefault) {
+ InconsistentDefaultArg = true;
+ PrevModuleName = ImportedM->getFullModuleName();
+ }
+ } else if (Module *LocalM = OldNonTypeParm->getLocalOwningModule()) {
+ if (LocalM->isModuleMapModule()) {
+ if (!SameDefault)
+ RedundantDefaultArg = true;
+ } else {
+ RedundantDefaultArg = true;
+ }
+ } else {
RedundantDefaultArg = true;
- else if (!getASTContext().isSameDefaultTemplateArgument(
- OldNonTypeParm, NewNonTypeParm)) {
- InconsistentDefaultArg = true;
- PrevModuleName =
- OldNonTypeParm->getImportedOwningModule()->getFullModuleName();
}
PreviousDefaultArgLoc = NewDefaultLoc;
} else if (OldNonTypeParm && OldNonTypeParm->hasDefaultArgument()) {
@@ -2578,13 +2598,22 @@ bool Sema::CheckTemplateParameterList(TemplateParameterList *NewParams,
OldDefaultLoc = OldTemplateParm->getDefaultArgument().getLocation();
NewDefaultLoc = NewTemplateParm->getDefaultArgument().getLocation();
SawDefaultArgument = true;
- if (!OldTemplateParm->getOwningModule())
+ bool SameDefault = getASTContext().isSameDefaultTemplateArgument(
+ OldTemplateParm, NewTemplateParm);
+ if (Module *ImportedM = OldTemplateParm->getImportedOwningModule()) {
+ if (!SameDefault) {
+ InconsistentDefaultArg = true;
+ PrevModuleName = ImportedM->getFullModuleName();
+ }
+ } else if (Module *LocalM = OldTemplateParm->getLocalOwningModule()) {
+ if (LocalM->isModuleMapModule()) {
+ if (!SameDefault)
+ RedundantDefaultArg = true;
+ } else {
+ RedundantDefaultArg = true;
+ }
+ } else {
RedundantDefaultArg = true;
- else if (!getASTContext().isSameDefaultTemplateArgument(
- OldTemplateParm, NewTemplateParm)) {
- InconsistentDefaultArg = true;
- PrevModuleName =
- OldTemplateParm->getImportedOwningModule()->getFullModuleName();
}
PreviousDefaultArgLoc = NewDefaultLoc;
} else if (OldTemplateParm && OldTemplateParm->hasDefaultArgument()) {
@@ -8138,9 +8167,6 @@ static Expr *BuildExpressionFromNonTypeTemplateArgumentValue(
return MakeInitList(Elts);
}
- case APValue::Matrix:
- llvm_unreachable("Matrix template argument expression not yet supported");
-
case APValue::None:
case APValue::Indeterminate:
llvm_unreachable("Unexpected APValue kind.");
@@ -11841,4 +11867,4 @@ SourceLocation Sema::getTopMostPointOfInstantiation(const NamedDecl *N) const {
return CSC.PointOfInstantiation;
}
return N->getLocation();
-}
+}
\ No newline at end of file
diff --git a/clang/test/CXX/module/basic/basic.def.odr/p7.cppm b/clang/test/CXX/module/basic/basic.def.odr/p7.cppm
new file mode 100644
index 0000000000000..89701b834f081
--- /dev/null
+++ b/clang/test/CXX/module/basic/basic.def.odr/p7.cppm
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -std=c++20 -verify %s
+
+// Global module fragment for named module 'a'.
+module;
+
+// Case 1: original crash – type parameter in GMF
+template <class = int> class FooGMF; // expected-note {{previous default template argument defined here}}
+template <class = void> class FooGMF; // expected-error {{template parameter redefines default argument}}
+
+// Case 2: same-TU redefinition without explicit template name
+template<class T = int> class FooNonMod; // expected-note {{previous default template argument defined here}}
+template<class T = void> class FooNonMod; // expected-error {{template parameter redefines default argument}}
+
+// Case 3: non-type parameter in GMF
+template <int N = 5> class NTGMF; // expected-note {{previous default template argument defined here}}
+template <int N = 7> class NTGMF; // expected-error {{template parameter redefines default argument}}
+
+
+// Case 4: legal vs illegal redefinitions across declaration/definition
+template <class T = int> class Legal;
+template <class T> class Legal { T value; };
+
+template <class T = int> class Illegal; // expected-note {{previous default template argument defined here}}
+template <class T = void> class Illegal { T value; }; // expected-error {{template parameter redefines default argument}}
+
+// Case 5: multiple redeclarations (type and non-type)
+// When the 3rd decl fires, Clang notes every prior decl that had a default.
+template <class = int> class Multi; // expected-note {{previous default template argument defined here}}
+template <class = int> class Multi; // expected-error {{template parameter redefines default argument}} expected-note {{previous default template argument defined here}}
+template <class = float> class Multi; // expected-error {{template parameter redefines default argument}}
+
+template <int N = 1> class MultiNT; // expected-note {{previous default template argument defined here}}
+template <int N = 1> class MultiNT; // expected-error {{template parameter redefines default argument}} expected-note {{previous default template argument defined here}}
+template <int N = 2> class MultiNT; // expected-error {{template parameter redefines default argument}}
+
+// Case 6: multiple parameters
+// For multi-param templates, each parameter gets its own error+note.
+// For a 3-decl chain, the 3rd decl notes all prior decls that had defaults (per param).
+template <class T = int, class U = double> class Pair; // expected-note 2 {{previous default template argument defined here}}
+template <class T = int, class U = double> class Pair; // expected-error 2 {{template parameter redefines default argument}} expected-note 2 {{previous default template argument defined here}}
+template <class T = void, class U = float> class Pair; // expected-error 2 {{template parameter redefines default argument}}
+
+template <int N = 5, char C = 'a'> class NTMulti; // expected-note 2 {{previous default template argument defined here}}
+template <int N = 5, char C = 'a'> class NTMulti; // expected-error 2 {{template parameter redefines default argument}} expected-note 2 {{previous default template argument defined here}}
+template <int N = 7, char C = 'b'> class NTMulti; // expected-error 2 {{template parameter redefines default argument}}
+
+// Case 7: template-template parameter defaults
+// Same cascading note pattern for a 3-decl chain with 2 params.
+template <class> class A;
+template <class> class B;
+
+template <template <class> class TT = A, class T = int> class TmplT; // expected-note 2 {{previous default template argument defined here}}
+template <template <class> class TT = A, class T = int> class TmplT; // expected-error 2 {{template parameter redefines default argument}} expected-note 2 {{previous default template argument defined here}}
+template <template <class> class TT = B, class T = void> class TmplT; // expected-error 2 {{template parameter redefines default argument}}
+
+// Case 8: forward declaration pattern
+template <class T = int> class Forward; // expected-note {{previous default template argument defined here}}
+template <class T> class Forward { T value; };
+template <class T = void> class Forward; // expected-error {{template parameter redefines default argument}}
+
+export module a;
+
+int main() {} // expected-warning {{'main' never has module linkage}}
\ No newline at end of file
More information about the cfe-commits
mailing list