[clang] [clang][diagnostics] suggest extern template syntax for -Wundefined-var-template (PR #187896)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Mar 21 22:31:17 PDT 2026
https://github.com/diohabara updated https://github.com/llvm/llvm-project/pull/187896
>From 3349dabfc52cd6f440c3bc64ed5e453476d4dd47 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=8D=8Ddiohabara=E5=8D=8D?=
<37092773+diohabara at users.noreply.github.com>
Date: Sat, 21 Mar 2026 18:52:57 +0900
Subject: [PATCH 1/5] [clang][diagnostics] suggest extern template syntax for
-Wundefined-var-template and -Wundefined-func-template
---
.../clang/Basic/DiagnosticSemaKinds.td | 2 +
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 68 ++++++++++++++++++-
...antiation.diagnose_on_undefined_entity.cpp | 4 ++
clang/test/SemaCXX/cxx1z-ast-print.cpp | 4 +-
.../instantiate-pure-virtual-function.cpp | 6 +-
.../test/SemaTemplate/undefined-template.cpp | 13 ++++
6 files changed, 93 insertions(+), 4 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d4d09a8ecef36..d8647f6d426c9 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5954,6 +5954,8 @@ def note_unreachable_template_decl
def note_inst_declaration_hint : Note<"add an explicit instantiation "
"declaration to suppress this warning if %q0 is explicitly instantiated in "
"another translation unit">;
+def note_inst_declaration_example : Note<
+ "e.g., '%0'">;
def note_evaluating_exception_spec_here : Note<
"in evaluation of exception specification for %q0 needed here">;
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index cc24e03e77c07..f221b180e2bdd 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5636,9 +5636,58 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
diag::note_unreachable_template_decl);
} else {
Diag(PatternDecl->getLocation(), diag::note_forward_template_decl);
- if (getLangOpts().CPlusPlus11)
+ if (getLangOpts().CPlusPlus11) {
Diag(PointOfInstantiation, diag::note_inst_declaration_hint)
<< Function;
+ // Suggest the exact extern template declaration syntax.
+ // Note: for functions with complex declarator return types
+ // (e.g., function pointers), the suggestion may not be
+ // perfectly formatted, but these cases are rare in practice.
+ std::string Suggestion;
+ {
+ llvm::raw_string_ostream OS(Suggestion);
+ OS << "extern template ";
+ // Constructors, destructors, and conversion operators have no
+ // separate return type in the declaration syntax.
+ if (!isa<CXXConstructorDecl>(Function) &&
+ !isa<CXXDestructorDecl>(Function) &&
+ !isa<CXXConversionDecl>(Function)) {
+ Function->getReturnType().print(OS, getPrintingPolicy());
+ OS << " ";
+ }
+ Function->getNameForDiagnostic(OS, getPrintingPolicy(),
+ /*Qualified=*/true);
+ OS << "(";
+ for (unsigned I = 0, N = Function->getNumParams(); I != N; ++I) {
+ if (I > 0)
+ OS << ", ";
+ Function->getParamDecl(I)->getType().print(
+ OS, getPrintingPolicy());
+ }
+ if (Function->isVariadic()) {
+ if (Function->getNumParams() > 0)
+ OS << ", ";
+ OS << "...";
+ }
+ OS << ")";
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(Function)) {
+ if (MD->getMethodQualifiers().hasConst())
+ OS << " const";
+ if (MD->getMethodQualifiers().hasVolatile())
+ OS << " volatile";
+ if (MD->getRefQualifier() == RQ_LValue)
+ OS << " &";
+ else if (MD->getRefQualifier() == RQ_RValue)
+ OS << " &&";
+ }
+ if (auto EST = Function->getExceptionSpecType();
+ EST == EST_BasicNoexcept)
+ OS << " noexcept";
+ OS << ";";
+ }
+ Diag(PointOfInstantiation, diag::note_inst_declaration_example)
+ << Suggestion;
+ }
}
}
}
@@ -6379,8 +6428,23 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
Diag(PointOfInstantiation, diag::warn_var_template_missing)
<< Var;
Diag(PatternDecl->getLocation(), diag::note_forward_template_decl);
- if (getLangOpts().CPlusPlus11)
+ if (getLangOpts().CPlusPlus11) {
Diag(PointOfInstantiation, diag::note_inst_declaration_hint) << Var;
+ // Suggest the exact extern template declaration syntax.
+ std::string Suggestion;
+ {
+ llvm::raw_string_ostream OS(Suggestion);
+ OS << "extern template ";
+ std::string QualName;
+ llvm::raw_string_ostream NameOS(QualName);
+ Var->getNameForDiagnostic(NameOS, getPrintingPolicy(),
+ /*Qualified=*/true);
+ Var->getType().print(OS, getPrintingPolicy(), QualName);
+ OS << ";";
+ }
+ Diag(PointOfInstantiation, diag::note_inst_declaration_example)
+ << Suggestion;
+ }
}
return;
}
diff --git a/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
index 24e24221939fa..b79e5983fe6be 100644
--- a/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
+++ b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
@@ -24,13 +24,17 @@ void use() {
foo.non_static_member_function(); // expected-warning{{instantiation of function 'Foo<int>::non_static_member_function' required here, but no definition is available}}
// expected-note at -1 {{add an explicit instantiation}}
+ // expected-note at -2 {{e.g., 'extern template}}
Foo<int>::static_member_function(); // expected-warning{{instantiation of function 'Foo<int>::static_member_function' required here, but no definition is available}}
// expected-note at -1 {{add an explicit instantiation}}
+ // expected-note at -2 {{e.g., 'extern template}}
(void)Foo<int>::static_data_member; // expected-warning{{instantiation of variable 'Foo<int>::static_data_member' required here, but no definition is available}}
// expected-note at -1 {{add an explicit instantiation}}
+ // expected-note at -2 {{e.g., 'extern template}}
Foo<int>::nested::static_member_function(); // expected-warning{{instantiation of function 'Foo<int>::nested::static_member_function' required here, but no definition is available}}
// expected-note at -1 {{add an explicit instantiation}}
+ // expected-note at -2 {{e.g., 'extern template}}
}
diff --git a/clang/test/SemaCXX/cxx1z-ast-print.cpp b/clang/test/SemaCXX/cxx1z-ast-print.cpp
index a6ba149e974ef..642b7ceff935e 100644
--- a/clang/test/SemaCXX/cxx1z-ast-print.cpp
+++ b/clang/test/SemaCXX/cxx1z-ast-print.cpp
@@ -7,5 +7,7 @@ struct TypeSuffix {
// CHECK: int k = TypeSuffix().x<0L> + TypeSuffix().y<0L>;
int k = TypeSuffix().x<0L> + TypeSuffix().y<0L>; // expected-warning {{instantiation of variable 'TypeSuffix::x<0>' required here, but no definition is available}} \
// expected-note {{add an explicit instantiation declaration to suppress this warning if 'TypeSuffix::x<0>' is explicitly instantiated in another translation unit}} \
+ // expected-note {{e.g., 'extern template}} \
// expected-warning {{instantiation of variable 'TypeSuffix::y<0L>' required here, but no definition is available}} \
- // expected-note {{add an explicit instantiation declaration to suppress this warning if 'TypeSuffix::y<0L>' is explicitly instantiated in another translation unit}}
+ // expected-note {{add an explicit instantiation declaration to suppress this warning if 'TypeSuffix::y<0L>' is explicitly instantiated in another translation unit}} \
+ // expected-note {{e.g., 'extern template}}
diff --git a/clang/test/SemaTemplate/instantiate-pure-virtual-function.cpp b/clang/test/SemaTemplate/instantiate-pure-virtual-function.cpp
index caec42b6b77f9..548f7662b0af5 100644
--- a/clang/test/SemaTemplate/instantiate-pure-virtual-function.cpp
+++ b/clang/test/SemaTemplate/instantiate-pure-virtual-function.cpp
@@ -23,6 +23,7 @@ namespace call_pure_virtual_function_from_virtual {
public:
const void foo(const T &) { B::bar(1); } // expected-warning {{instantiation of function 'call_pure_virtual_function_from_virtual::B<int>::bar' required here, but no definition is available}}
// expected-note at -1 {{add an explicit instantiation declaration to suppress this warning if 'call_pure_virtual_function_from_virtual::B<int>::bar' is explicitly instantiated in another translation unit}}
+ // expected-note at -2 {{e.g., 'extern template}}
virtual const void bar(unsigned int) = 0; // expected-note {{forward declaration of template entity is here}}
};
@@ -54,7 +55,10 @@ namespace non_pure_virtual_function {
// expected-note at -3 {{add an explicit instantiation declaration to suppress this warning if 'non_pure_virtual_function::B<int>::bar' is explicitly instantiated in another translation unit}}
// expected-note at -4 {{add an explicit instantiation declaration to suppress this warning if 'non_pure_virtual_function::B<int>::bar' is explicitly instantiated in another translation unit}}
// expected-note at -5 {{add an explicit instantiation declaration to suppress this warning if 'non_pure_virtual_function::B<int>::bar' is explicitly instantiated in another translation unit}}
-// expected-note at -6 {{used here}}
+// expected-note at -6 {{e.g., 'extern template}}
+// expected-note at -7 {{e.g., 'extern template}}
+// expected-note at -8 {{e.g., 'extern template}}
+// expected-note at -9 {{used here}}
public:
constexpr void bar(unsigned int) override { }
diff --git a/clang/test/SemaTemplate/undefined-template.cpp b/clang/test/SemaTemplate/undefined-template.cpp
index 52530e2e3909a..c26ab6c8ff03a 100644
--- a/clang/test/SemaTemplate/undefined-template.cpp
+++ b/clang/test/SemaTemplate/undefined-template.cpp
@@ -37,16 +37,19 @@ char func_01() {
char func_02() {
return C1<int>::s_var_1; // expected-warning{{instantiation of variable 'C1<int>::s_var_1' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::s_var_1' is explicitly instantiated in another translation unit}}
+ // expected-note at -2{{e.g., 'extern template char C1<int>::s_var_1;'}}
}
char func_03() {
return C1<char>::s_var_2; // expected-warning{{instantiation of variable 'C1<char>::s_var_2' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<char>::s_var_2' is explicitly instantiated in another translation unit}}
+ // expected-note at -2{{e.g., 'extern template char C1<char>::s_var_2;'}}
}
void func_04() {
C1<int>::s_func_1(); // expected-warning{{instantiation of function 'C1<int>::s_func_1' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::s_func_1' is explicitly instantiated in another translation unit}}
+ // expected-note at -2{{e.g., 'extern template void C1<int>::s_func_1();'}}
}
void func_05() {
@@ -56,11 +59,13 @@ void func_05() {
void func_06() {
C1<char>::s_func_2(); // expected-warning{{instantiation of function 'C1<char>::s_func_2' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<char>::s_func_2' is explicitly instantiated in another translation unit}}
+ // expected-note at -2{{e.g., 'extern template void C1<char>::s_func_2();'}}
}
void func_07(C1<int> *x) {
x->meth_1(); // expected-warning{{instantiation of function 'C1<int>::meth_1' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::meth_1' is explicitly instantiated in another translation unit}}
+ // expected-note at -2{{e.g., 'extern template void C1<int>::meth_1();'}}
}
void func_08(C1<int> *x) {
@@ -70,6 +75,7 @@ void func_08(C1<int> *x) {
void func_09(C1<char> *x) {
x->meth_1(); // expected-warning{{instantiation of function 'C1<char>::meth_1' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<char>::meth_1' is explicitly instantiated in another translation unit}}
+ // expected-note at -2{{e.g., 'extern template void C1<char>::meth_1();'}}
}
char func_10() {
@@ -79,6 +85,7 @@ char func_10() {
char func_11() {
return C1<int>::s_tvar_2<long>; // expected-warning{{instantiation of variable 'C1<int>::s_tvar_2<long>' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::s_tvar_2<long>' is explicitly instantiated in another translation unit}}
+ // expected-note at -2{{e.g., 'extern template char C1<int>::s_tvar_2<long>;'}}
}
void func_12() {
@@ -88,6 +95,7 @@ void func_12() {
void func_13() {
C1<int>::s_tfunc_2<long>(); // expected-warning{{instantiation of function 'C1<int>::s_tfunc_2<long>' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::s_tfunc_2<long>' is explicitly instantiated in another translation unit}}
+ // expected-note at -2{{e.g., 'extern template void C1<int>::s_tfunc_2<long>();'}}
}
char func_14() {
@@ -97,6 +105,7 @@ char func_14() {
char func_15() {
return C1<int>::C2<char>::s_var_2; //expected-warning {{instantiation of variable 'C1<int>::C2<char>::s_var_2' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::C2<char>::s_var_2' is explicitly instantiated in another translation unit}}
+ // expected-note at -2{{e.g., 'extern template char C1<int>::C2<char>::s_var_2;'}}
}
void func_16() {
@@ -106,6 +115,7 @@ void func_16() {
void func_17() {
C1<int>::C2<char>::s_func_2(); // expected-warning{{instantiation of function 'C1<int>::C2<char>::s_func_2' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::C2<char>::s_func_2' is explicitly instantiated in another translation unit}}
+ // expected-note at -2{{e.g., 'extern template void C1<int>::C2<char>::s_func_2();'}}
}
void func_18(C1<int>::C2<long> *x) {
@@ -115,6 +125,7 @@ void func_18(C1<int>::C2<long> *x) {
void func_19(C1<int>::C2<char> *x) {
x->meth_2(); // expected-warning{{instantiation of function 'C1<int>::C2<char>::meth_2' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::C2<char>::meth_2' is explicitly instantiated in another translation unit}}
+ // expected-note at -2{{e.g., 'extern template void C1<int>::C2<char>::meth_2();'}}
}
char func_20() {
@@ -124,6 +135,7 @@ char func_20() {
char func_21() {
return C1<int>::C2<long>::s_tvar_2<long>; // expected-warning{{instantiation of variable 'C1<int>::C2<long>::s_tvar_2<long>' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::C2<long>::s_tvar_2<long>' is explicitly instantiated in another translation unit}}
+ // expected-note at -2{{e.g., 'extern template char C1<int>::C2<long>::s_tvar_2<long>;'}}
}
void func_22(C1<int>::C2<long> *x) {
@@ -133,6 +145,7 @@ void func_22(C1<int>::C2<long> *x) {
void func_23(C1<int>::C2<long> *x) {
x->tmeth_2<int>(); // expected-warning{{instantiation of function 'C1<int>::C2<long>::tmeth_2<int>' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::C2<long>::tmeth_2<int>' is explicitly instantiated in another translation unit}}
+ // expected-note at -2{{e.g., 'extern template void C1<int>::C2<long>::tmeth_2<int>();'}}
}
namespace test_24 {
>From 7465dfc4dbbdb05cf463d7f26a65825bb4941718 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=8D=8Ddiohabara=E5=8D=8D?=
<37092773+diohabara at users.noreply.github.com>
Date: Sun, 22 Mar 2026 11:13:34 +0900
Subject: [PATCH 2/5] chore: remove func warning suggestion
---
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 51 +------------------
...antiation.diagnose_on_undefined_entity.cpp | 3 --
.../instantiate-pure-virtual-function.cpp | 6 +--
.../test/SemaTemplate/undefined-template.cpp | 8 ---
4 files changed, 2 insertions(+), 66 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index f221b180e2bdd..5590433d05818 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5636,58 +5636,9 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
diag::note_unreachable_template_decl);
} else {
Diag(PatternDecl->getLocation(), diag::note_forward_template_decl);
- if (getLangOpts().CPlusPlus11) {
+ if (getLangOpts().CPlusPlus11)
Diag(PointOfInstantiation, diag::note_inst_declaration_hint)
<< Function;
- // Suggest the exact extern template declaration syntax.
- // Note: for functions with complex declarator return types
- // (e.g., function pointers), the suggestion may not be
- // perfectly formatted, but these cases are rare in practice.
- std::string Suggestion;
- {
- llvm::raw_string_ostream OS(Suggestion);
- OS << "extern template ";
- // Constructors, destructors, and conversion operators have no
- // separate return type in the declaration syntax.
- if (!isa<CXXConstructorDecl>(Function) &&
- !isa<CXXDestructorDecl>(Function) &&
- !isa<CXXConversionDecl>(Function)) {
- Function->getReturnType().print(OS, getPrintingPolicy());
- OS << " ";
- }
- Function->getNameForDiagnostic(OS, getPrintingPolicy(),
- /*Qualified=*/true);
- OS << "(";
- for (unsigned I = 0, N = Function->getNumParams(); I != N; ++I) {
- if (I > 0)
- OS << ", ";
- Function->getParamDecl(I)->getType().print(
- OS, getPrintingPolicy());
- }
- if (Function->isVariadic()) {
- if (Function->getNumParams() > 0)
- OS << ", ";
- OS << "...";
- }
- OS << ")";
- if (const auto *MD = dyn_cast<CXXMethodDecl>(Function)) {
- if (MD->getMethodQualifiers().hasConst())
- OS << " const";
- if (MD->getMethodQualifiers().hasVolatile())
- OS << " volatile";
- if (MD->getRefQualifier() == RQ_LValue)
- OS << " &";
- else if (MD->getRefQualifier() == RQ_RValue)
- OS << " &&";
- }
- if (auto EST = Function->getExceptionSpecType();
- EST == EST_BasicNoexcept)
- OS << " noexcept";
- OS << ";";
- }
- Diag(PointOfInstantiation, diag::note_inst_declaration_example)
- << Suggestion;
- }
}
}
}
diff --git a/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
index b79e5983fe6be..24f6852171f44 100644
--- a/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
+++ b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
@@ -24,11 +24,9 @@ void use() {
foo.non_static_member_function(); // expected-warning{{instantiation of function 'Foo<int>::non_static_member_function' required here, but no definition is available}}
// expected-note at -1 {{add an explicit instantiation}}
- // expected-note at -2 {{e.g., 'extern template}}
Foo<int>::static_member_function(); // expected-warning{{instantiation of function 'Foo<int>::static_member_function' required here, but no definition is available}}
// expected-note at -1 {{add an explicit instantiation}}
- // expected-note at -2 {{e.g., 'extern template}}
(void)Foo<int>::static_data_member; // expected-warning{{instantiation of variable 'Foo<int>::static_data_member' required here, but no definition is available}}
// expected-note at -1 {{add an explicit instantiation}}
@@ -36,5 +34,4 @@ void use() {
Foo<int>::nested::static_member_function(); // expected-warning{{instantiation of function 'Foo<int>::nested::static_member_function' required here, but no definition is available}}
// expected-note at -1 {{add an explicit instantiation}}
- // expected-note at -2 {{e.g., 'extern template}}
}
diff --git a/clang/test/SemaTemplate/instantiate-pure-virtual-function.cpp b/clang/test/SemaTemplate/instantiate-pure-virtual-function.cpp
index 548f7662b0af5..caec42b6b77f9 100644
--- a/clang/test/SemaTemplate/instantiate-pure-virtual-function.cpp
+++ b/clang/test/SemaTemplate/instantiate-pure-virtual-function.cpp
@@ -23,7 +23,6 @@ namespace call_pure_virtual_function_from_virtual {
public:
const void foo(const T &) { B::bar(1); } // expected-warning {{instantiation of function 'call_pure_virtual_function_from_virtual::B<int>::bar' required here, but no definition is available}}
// expected-note at -1 {{add an explicit instantiation declaration to suppress this warning if 'call_pure_virtual_function_from_virtual::B<int>::bar' is explicitly instantiated in another translation unit}}
- // expected-note at -2 {{e.g., 'extern template}}
virtual const void bar(unsigned int) = 0; // expected-note {{forward declaration of template entity is here}}
};
@@ -55,10 +54,7 @@ namespace non_pure_virtual_function {
// expected-note at -3 {{add an explicit instantiation declaration to suppress this warning if 'non_pure_virtual_function::B<int>::bar' is explicitly instantiated in another translation unit}}
// expected-note at -4 {{add an explicit instantiation declaration to suppress this warning if 'non_pure_virtual_function::B<int>::bar' is explicitly instantiated in another translation unit}}
// expected-note at -5 {{add an explicit instantiation declaration to suppress this warning if 'non_pure_virtual_function::B<int>::bar' is explicitly instantiated in another translation unit}}
-// expected-note at -6 {{e.g., 'extern template}}
-// expected-note at -7 {{e.g., 'extern template}}
-// expected-note at -8 {{e.g., 'extern template}}
-// expected-note at -9 {{used here}}
+// expected-note at -6 {{used here}}
public:
constexpr void bar(unsigned int) override { }
diff --git a/clang/test/SemaTemplate/undefined-template.cpp b/clang/test/SemaTemplate/undefined-template.cpp
index c26ab6c8ff03a..f1331c3437b93 100644
--- a/clang/test/SemaTemplate/undefined-template.cpp
+++ b/clang/test/SemaTemplate/undefined-template.cpp
@@ -49,7 +49,6 @@ char func_03() {
void func_04() {
C1<int>::s_func_1(); // expected-warning{{instantiation of function 'C1<int>::s_func_1' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::s_func_1' is explicitly instantiated in another translation unit}}
- // expected-note at -2{{e.g., 'extern template void C1<int>::s_func_1();'}}
}
void func_05() {
@@ -59,13 +58,11 @@ void func_05() {
void func_06() {
C1<char>::s_func_2(); // expected-warning{{instantiation of function 'C1<char>::s_func_2' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<char>::s_func_2' is explicitly instantiated in another translation unit}}
- // expected-note at -2{{e.g., 'extern template void C1<char>::s_func_2();'}}
}
void func_07(C1<int> *x) {
x->meth_1(); // expected-warning{{instantiation of function 'C1<int>::meth_1' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::meth_1' is explicitly instantiated in another translation unit}}
- // expected-note at -2{{e.g., 'extern template void C1<int>::meth_1();'}}
}
void func_08(C1<int> *x) {
@@ -75,7 +72,6 @@ void func_08(C1<int> *x) {
void func_09(C1<char> *x) {
x->meth_1(); // expected-warning{{instantiation of function 'C1<char>::meth_1' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<char>::meth_1' is explicitly instantiated in another translation unit}}
- // expected-note at -2{{e.g., 'extern template void C1<char>::meth_1();'}}
}
char func_10() {
@@ -95,7 +91,6 @@ void func_12() {
void func_13() {
C1<int>::s_tfunc_2<long>(); // expected-warning{{instantiation of function 'C1<int>::s_tfunc_2<long>' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::s_tfunc_2<long>' is explicitly instantiated in another translation unit}}
- // expected-note at -2{{e.g., 'extern template void C1<int>::s_tfunc_2<long>();'}}
}
char func_14() {
@@ -115,7 +110,6 @@ void func_16() {
void func_17() {
C1<int>::C2<char>::s_func_2(); // expected-warning{{instantiation of function 'C1<int>::C2<char>::s_func_2' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::C2<char>::s_func_2' is explicitly instantiated in another translation unit}}
- // expected-note at -2{{e.g., 'extern template void C1<int>::C2<char>::s_func_2();'}}
}
void func_18(C1<int>::C2<long> *x) {
@@ -125,7 +119,6 @@ void func_18(C1<int>::C2<long> *x) {
void func_19(C1<int>::C2<char> *x) {
x->meth_2(); // expected-warning{{instantiation of function 'C1<int>::C2<char>::meth_2' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::C2<char>::meth_2' is explicitly instantiated in another translation unit}}
- // expected-note at -2{{e.g., 'extern template void C1<int>::C2<char>::meth_2();'}}
}
char func_20() {
@@ -145,7 +138,6 @@ void func_22(C1<int>::C2<long> *x) {
void func_23(C1<int>::C2<long> *x) {
x->tmeth_2<int>(); // expected-warning{{instantiation of function 'C1<int>::C2<long>::tmeth_2<int>' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::C2<long>::tmeth_2<int>' is explicitly instantiated in another translation unit}}
- // expected-note at -2{{e.g., 'extern template void C1<int>::C2<long>::tmeth_2<int>();'}}
}
namespace test_24 {
>From cb800faade6532139b27423b921ee496afb8072b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=8D=8Ddiohabara=E5=8D=8D?=
<37092773+diohabara at users.noreply.github.com>
Date: Sun, 22 Mar 2026 12:25:07 +0900
Subject: [PATCH 3/5] chore: emit more informative warnings
---
clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 ++--
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 3 ++-
...icit_instantiation.diagnose_on_undefined_entity.cpp | 2 +-
clang/test/SemaCXX/cxx1z-ast-print.cpp | 4 ++--
clang/test/SemaTemplate/undefined-template.cpp | 10 +++++-----
5 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d8647f6d426c9..d42cb042b3df1 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5954,8 +5954,8 @@ def note_unreachable_template_decl
def note_inst_declaration_hint : Note<"add an explicit instantiation "
"declaration to suppress this warning if %q0 is explicitly instantiated in "
"another translation unit">;
-def note_inst_declaration_example : Note<
- "e.g., '%0'">;
+def note_inst_declaration_extern_suggestion : Note<
+ "use '%0' to declare the definition is in another translation unit">;
def note_evaluating_exception_spec_here : Note<
"in evaluation of exception specification for %q0 needed here">;
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 5590433d05818..bcefbffb12a90 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -6393,7 +6393,8 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
Var->getType().print(OS, getPrintingPolicy(), QualName);
OS << ";";
}
- Diag(PointOfInstantiation, diag::note_inst_declaration_example)
+ Diag(PointOfInstantiation,
+ diag::note_inst_declaration_extern_suggestion)
<< Suggestion;
}
}
diff --git a/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
index 24f6852171f44..885197e5157da 100644
--- a/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
+++ b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
@@ -30,7 +30,7 @@ void use() {
(void)Foo<int>::static_data_member; // expected-warning{{instantiation of variable 'Foo<int>::static_data_member' required here, but no definition is available}}
// expected-note at -1 {{add an explicit instantiation}}
- // expected-note at -2 {{e.g., 'extern template}}
+ // expected-note at -2 {{use 'extern template}}
Foo<int>::nested::static_member_function(); // expected-warning{{instantiation of function 'Foo<int>::nested::static_member_function' required here, but no definition is available}}
// expected-note at -1 {{add an explicit instantiation}}
diff --git a/clang/test/SemaCXX/cxx1z-ast-print.cpp b/clang/test/SemaCXX/cxx1z-ast-print.cpp
index 642b7ceff935e..d1c856ff0dfa8 100644
--- a/clang/test/SemaCXX/cxx1z-ast-print.cpp
+++ b/clang/test/SemaCXX/cxx1z-ast-print.cpp
@@ -7,7 +7,7 @@ struct TypeSuffix {
// CHECK: int k = TypeSuffix().x<0L> + TypeSuffix().y<0L>;
int k = TypeSuffix().x<0L> + TypeSuffix().y<0L>; // expected-warning {{instantiation of variable 'TypeSuffix::x<0>' required here, but no definition is available}} \
// expected-note {{add an explicit instantiation declaration to suppress this warning if 'TypeSuffix::x<0>' is explicitly instantiated in another translation unit}} \
- // expected-note {{e.g., 'extern template}} \
+ // expected-note {{use 'extern template}} \
// expected-warning {{instantiation of variable 'TypeSuffix::y<0L>' required here, but no definition is available}} \
// expected-note {{add an explicit instantiation declaration to suppress this warning if 'TypeSuffix::y<0L>' is explicitly instantiated in another translation unit}} \
- // expected-note {{e.g., 'extern template}}
+ // expected-note {{use 'extern template}}
diff --git a/clang/test/SemaTemplate/undefined-template.cpp b/clang/test/SemaTemplate/undefined-template.cpp
index f1331c3437b93..12530d44dd5fb 100644
--- a/clang/test/SemaTemplate/undefined-template.cpp
+++ b/clang/test/SemaTemplate/undefined-template.cpp
@@ -37,13 +37,13 @@ char func_01() {
char func_02() {
return C1<int>::s_var_1; // expected-warning{{instantiation of variable 'C1<int>::s_var_1' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::s_var_1' is explicitly instantiated in another translation unit}}
- // expected-note at -2{{e.g., 'extern template char C1<int>::s_var_1;'}}
+ // expected-note at -2{{use 'extern template char C1<int>::s_var_1;'}}
}
char func_03() {
return C1<char>::s_var_2; // expected-warning{{instantiation of variable 'C1<char>::s_var_2' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<char>::s_var_2' is explicitly instantiated in another translation unit}}
- // expected-note at -2{{e.g., 'extern template char C1<char>::s_var_2;'}}
+ // expected-note at -2{{use 'extern template char C1<char>::s_var_2;'}}
}
void func_04() {
@@ -81,7 +81,7 @@ char func_10() {
char func_11() {
return C1<int>::s_tvar_2<long>; // expected-warning{{instantiation of variable 'C1<int>::s_tvar_2<long>' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::s_tvar_2<long>' is explicitly instantiated in another translation unit}}
- // expected-note at -2{{e.g., 'extern template char C1<int>::s_tvar_2<long>;'}}
+ // expected-note at -2{{use 'extern template char C1<int>::s_tvar_2<long>;'}}
}
void func_12() {
@@ -100,7 +100,7 @@ char func_14() {
char func_15() {
return C1<int>::C2<char>::s_var_2; //expected-warning {{instantiation of variable 'C1<int>::C2<char>::s_var_2' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::C2<char>::s_var_2' is explicitly instantiated in another translation unit}}
- // expected-note at -2{{e.g., 'extern template char C1<int>::C2<char>::s_var_2;'}}
+ // expected-note at -2{{use 'extern template char C1<int>::C2<char>::s_var_2;'}}
}
void func_16() {
@@ -128,7 +128,7 @@ char func_20() {
char func_21() {
return C1<int>::C2<long>::s_tvar_2<long>; // expected-warning{{instantiation of variable 'C1<int>::C2<long>::s_tvar_2<long>' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::C2<long>::s_tvar_2<long>' is explicitly instantiated in another translation unit}}
- // expected-note at -2{{e.g., 'extern template char C1<int>::C2<long>::s_tvar_2<long>;'}}
+ // expected-note at -2{{use 'extern template char C1<int>::C2<long>::s_tvar_2<long>;'}}
}
void func_22(C1<int>::C2<long> *x) {
>From 8392c045f5229cdf77603c168fc938969773f968 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=8D=8Ddiohabara=E5=8D=8D?=
<37092773+diohabara at users.noreply.github.com>
Date: Sun, 22 Mar 2026 13:14:21 +0900
Subject: [PATCH 4/5] feat: add explicit specialization
---
clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 ++
clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 15 +++++++++++++++
...instantiation.diagnose_on_undefined_entity.cpp | 1 +
clang/test/SemaCXX/cxx1z-ast-print.cpp | 4 +++-
clang/test/SemaTemplate/undefined-template.cpp | 5 +++++
5 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index d42cb042b3df1..a8d9c571eee9b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5956,6 +5956,8 @@ def note_inst_declaration_hint : Note<"add an explicit instantiation "
"another translation unit">;
def note_inst_declaration_extern_suggestion : Note<
"use '%0' to declare the definition is in another translation unit">;
+def note_inst_declaration_specialization_suggestion : Note<
+ "use '%0' to provide a definition for this type">;
def note_evaluating_exception_spec_here : Note<
"in evaluation of exception specification for %q0 needed here">;
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index bcefbffb12a90..db56de0570132 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -6396,6 +6396,21 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
Diag(PointOfInstantiation,
diag::note_inst_declaration_extern_suggestion)
<< Suggestion;
+ // Suggest explicit specialization syntax.
+ std::string SpecSuggestion;
+ {
+ llvm::raw_string_ostream OS(SpecSuggestion);
+ OS << "template <> ";
+ std::string QualName;
+ llvm::raw_string_ostream NameOS(QualName);
+ Var->getNameForDiagnostic(NameOS, getPrintingPolicy(),
+ /*Qualified=*/true);
+ Var->getType().print(OS, getPrintingPolicy(), QualName);
+ OS << ";";
+ }
+ Diag(PointOfInstantiation,
+ diag::note_inst_declaration_specialization_suggestion)
+ << SpecSuggestion;
}
}
return;
diff --git a/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
index 885197e5157da..8a6103e0f2996 100644
--- a/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
+++ b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
@@ -31,6 +31,7 @@ void use() {
(void)Foo<int>::static_data_member; // expected-warning{{instantiation of variable 'Foo<int>::static_data_member' required here, but no definition is available}}
// expected-note at -1 {{add an explicit instantiation}}
// expected-note at -2 {{use 'extern template}}
+ // expected-note at -3 {{use 'template <>}}
Foo<int>::nested::static_member_function(); // expected-warning{{instantiation of function 'Foo<int>::nested::static_member_function' required here, but no definition is available}}
// expected-note at -1 {{add an explicit instantiation}}
diff --git a/clang/test/SemaCXX/cxx1z-ast-print.cpp b/clang/test/SemaCXX/cxx1z-ast-print.cpp
index d1c856ff0dfa8..6326cf138d965 100644
--- a/clang/test/SemaCXX/cxx1z-ast-print.cpp
+++ b/clang/test/SemaCXX/cxx1z-ast-print.cpp
@@ -8,6 +8,8 @@ struct TypeSuffix {
int k = TypeSuffix().x<0L> + TypeSuffix().y<0L>; // expected-warning {{instantiation of variable 'TypeSuffix::x<0>' required here, but no definition is available}} \
// expected-note {{add an explicit instantiation declaration to suppress this warning if 'TypeSuffix::x<0>' is explicitly instantiated in another translation unit}} \
// expected-note {{use 'extern template}} \
+ // expected-note {{use 'template <>}} \
// expected-warning {{instantiation of variable 'TypeSuffix::y<0L>' required here, but no definition is available}} \
// expected-note {{add an explicit instantiation declaration to suppress this warning if 'TypeSuffix::y<0L>' is explicitly instantiated in another translation unit}} \
- // expected-note {{use 'extern template}}
+ // expected-note {{use 'extern template}} \
+ // expected-note {{use 'template <>}}
diff --git a/clang/test/SemaTemplate/undefined-template.cpp b/clang/test/SemaTemplate/undefined-template.cpp
index 12530d44dd5fb..bf98b2f577331 100644
--- a/clang/test/SemaTemplate/undefined-template.cpp
+++ b/clang/test/SemaTemplate/undefined-template.cpp
@@ -38,12 +38,14 @@ char func_02() {
return C1<int>::s_var_1; // expected-warning{{instantiation of variable 'C1<int>::s_var_1' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::s_var_1' is explicitly instantiated in another translation unit}}
// expected-note at -2{{use 'extern template char C1<int>::s_var_1;'}}
+ // expected-note at -3{{use 'template <> char C1<int>::s_var_1;'}}
}
char func_03() {
return C1<char>::s_var_2; // expected-warning{{instantiation of variable 'C1<char>::s_var_2' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<char>::s_var_2' is explicitly instantiated in another translation unit}}
// expected-note at -2{{use 'extern template char C1<char>::s_var_2;'}}
+ // expected-note at -3{{use 'template <> char C1<char>::s_var_2;'}}
}
void func_04() {
@@ -82,6 +84,7 @@ char func_11() {
return C1<int>::s_tvar_2<long>; // expected-warning{{instantiation of variable 'C1<int>::s_tvar_2<long>' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::s_tvar_2<long>' is explicitly instantiated in another translation unit}}
// expected-note at -2{{use 'extern template char C1<int>::s_tvar_2<long>;'}}
+ // expected-note at -3{{use 'template <> char C1<int>::s_tvar_2<long>;'}}
}
void func_12() {
@@ -101,6 +104,7 @@ char func_15() {
return C1<int>::C2<char>::s_var_2; //expected-warning {{instantiation of variable 'C1<int>::C2<char>::s_var_2' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::C2<char>::s_var_2' is explicitly instantiated in another translation unit}}
// expected-note at -2{{use 'extern template char C1<int>::C2<char>::s_var_2;'}}
+ // expected-note at -3{{use 'template <> char C1<int>::C2<char>::s_var_2;'}}
}
void func_16() {
@@ -129,6 +133,7 @@ char func_21() {
return C1<int>::C2<long>::s_tvar_2<long>; // expected-warning{{instantiation of variable 'C1<int>::C2<long>::s_tvar_2<long>' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::C2<long>::s_tvar_2<long>' is explicitly instantiated in another translation unit}}
// expected-note at -2{{use 'extern template char C1<int>::C2<long>::s_tvar_2<long>;'}}
+ // expected-note at -3{{use 'template <> char C1<int>::C2<long>::s_tvar_2<long>;'}}
}
void func_22(C1<int>::C2<long> *x) {
>From 73eb46c322f6d0ae9bafac992a9d114a5071236e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=8D=8Ddiohabara=E5=8D=8D?=
<37092773+diohabara at users.noreply.github.com>
Date: Sun, 22 Mar 2026 14:30:57 +0900
Subject: [PATCH 5/5] feat: support generic definition
---
.../clang/Basic/DiagnosticSemaKinds.td | 6 ++-
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 54 ++++++++++++++++++-
...antiation.diagnose_on_undefined_entity.cpp | 6 ++-
clang/test/SemaCXX/cxx1z-ast-print.cpp | 6 ++-
.../test/SemaTemplate/undefined-template.cpp | 27 ++++++----
5 files changed, 83 insertions(+), 16 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a8d9c571eee9b..252de54acba0e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -5954,10 +5954,14 @@ def note_unreachable_template_decl
def note_inst_declaration_hint : Note<"add an explicit instantiation "
"declaration to suppress this warning if %q0 is explicitly instantiated in "
"another translation unit">;
+def note_inst_declaration_num_ways : Note<
+ "there %plural{1:is 1 way|:are %0 ways}0 to fix this:">;
def note_inst_declaration_extern_suggestion : Note<
"use '%0' to declare the definition is in another translation unit">;
def note_inst_declaration_specialization_suggestion : Note<
- "use '%0' to provide a definition for this type">;
+ "or use '%0' to provide a definition for this type">;
+def note_inst_declaration_definition_suggestion : Note<
+ "or use '%0' to provide a definition for all types">;
def note_evaluating_exception_spec_here : Note<
"in evaluation of exception specification for %q0 needed here">;
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index db56de0570132..2e064645146f2 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -6381,7 +6381,32 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
Diag(PatternDecl->getLocation(), diag::note_forward_template_decl);
if (getLangOpts().CPlusPlus11) {
Diag(PointOfInstantiation, diag::note_inst_declaration_hint) << Var;
- // Suggest the exact extern template declaration syntax.
+
+ // Determine if we can suggest a generic out-of-class definition.
+ // FIXME: Support nested class templates (e.g.,
+ // C1<T>::C2<T1>::value) and member variable templates (e.g.,
+ // C1<T>::s_tvar_2<T1>) which require multiple template
+ // parameter lists.
+ bool CanSuggestDefinition = false;
+ const CXXRecordDecl *PatternRD = nullptr;
+ const ClassTemplateDecl *PatternCTD = nullptr;
+ if (!PatternDecl->getDescribedVarTemplate() &&
+ !isa<VarTemplateSpecializationDecl>(PatternDecl)) {
+ PatternRD = dyn_cast<CXXRecordDecl>(PatternDecl->getDeclContext());
+ if (PatternRD) {
+ PatternCTD = PatternRD->getDescribedClassTemplate();
+ if (PatternCTD &&
+ (PatternRD->getDeclContext()->isFileContext() ||
+ isa<NamespaceDecl>(PatternRD->getDeclContext())))
+ CanSuggestDefinition = true;
+ }
+ }
+
+ unsigned NumWays = CanSuggestDefinition ? 3 : 2;
+ Diag(PointOfInstantiation, diag::note_inst_declaration_num_ways)
+ << NumWays;
+
+ // Suggest extern template declaration syntax.
std::string Suggestion;
{
llvm::raw_string_ostream OS(Suggestion);
@@ -6396,6 +6421,7 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
Diag(PointOfInstantiation,
diag::note_inst_declaration_extern_suggestion)
<< Suggestion;
+
// Suggest explicit specialization syntax.
std::string SpecSuggestion;
{
@@ -6411,6 +6437,32 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
Diag(PointOfInstantiation,
diag::note_inst_declaration_specialization_suggestion)
<< SpecSuggestion;
+
+ // Suggest generic out-of-class definition syntax.
+ if (CanSuggestDefinition) {
+ std::string DefSuggestion;
+ {
+ llvm::raw_string_ostream OS(DefSuggestion);
+ // print() already appends a trailing space.
+ PatternCTD->getTemplateParameters()->print(OS, getASTContext(),
+ getPrintingPolicy());
+ std::string QualName;
+ llvm::raw_string_ostream NameOS(QualName);
+ NameOS << PatternRD->getName() << "<";
+ const auto *TPL = PatternCTD->getTemplateParameters();
+ for (unsigned I = 0, N = TPL->size(); I != N; ++I) {
+ if (I > 0)
+ NameOS << ", ";
+ NameOS << TPL->getParam(I)->getName();
+ }
+ NameOS << ">::" << PatternDecl->getName();
+ PatternDecl->getType().print(OS, getPrintingPolicy(), QualName);
+ OS << ";";
+ }
+ Diag(PointOfInstantiation,
+ diag::note_inst_declaration_definition_suggestion)
+ << DefSuggestion;
+ }
}
}
return;
diff --git a/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
index 8a6103e0f2996..269bb7b8e5cd3 100644
--- a/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
+++ b/clang/test/SemaCXX/attr-exclude_from_explicit_instantiation.diagnose_on_undefined_entity.cpp
@@ -30,8 +30,10 @@ void use() {
(void)Foo<int>::static_data_member; // expected-warning{{instantiation of variable 'Foo<int>::static_data_member' required here, but no definition is available}}
// expected-note at -1 {{add an explicit instantiation}}
- // expected-note at -2 {{use 'extern template}}
- // expected-note at -3 {{use 'template <>}}
+ // expected-note at -2 {{there are 3 ways to fix this:}}
+ // expected-note at -3 {{use 'extern template}}
+ // expected-note at -4 {{or use 'template <>}}
+ // expected-note at -5 {{or use 'template <class T>}}
Foo<int>::nested::static_member_function(); // expected-warning{{instantiation of function 'Foo<int>::nested::static_member_function' required here, but no definition is available}}
// expected-note at -1 {{add an explicit instantiation}}
diff --git a/clang/test/SemaCXX/cxx1z-ast-print.cpp b/clang/test/SemaCXX/cxx1z-ast-print.cpp
index 6326cf138d965..dabe073135cfe 100644
--- a/clang/test/SemaCXX/cxx1z-ast-print.cpp
+++ b/clang/test/SemaCXX/cxx1z-ast-print.cpp
@@ -7,9 +7,11 @@ struct TypeSuffix {
// CHECK: int k = TypeSuffix().x<0L> + TypeSuffix().y<0L>;
int k = TypeSuffix().x<0L> + TypeSuffix().y<0L>; // expected-warning {{instantiation of variable 'TypeSuffix::x<0>' required here, but no definition is available}} \
// expected-note {{add an explicit instantiation declaration to suppress this warning if 'TypeSuffix::x<0>' is explicitly instantiated in another translation unit}} \
+ // expected-note {{there are 2 ways to fix this:}} \
// expected-note {{use 'extern template}} \
- // expected-note {{use 'template <>}} \
+ // expected-note {{or use 'template <>}} \
// expected-warning {{instantiation of variable 'TypeSuffix::y<0L>' required here, but no definition is available}} \
// expected-note {{add an explicit instantiation declaration to suppress this warning if 'TypeSuffix::y<0L>' is explicitly instantiated in another translation unit}} \
+ // expected-note {{there are 2 ways to fix this:}} \
// expected-note {{use 'extern template}} \
- // expected-note {{use 'template <>}}
+ // expected-note {{or use 'template <>}}
diff --git a/clang/test/SemaTemplate/undefined-template.cpp b/clang/test/SemaTemplate/undefined-template.cpp
index bf98b2f577331..ca6bca0413039 100644
--- a/clang/test/SemaTemplate/undefined-template.cpp
+++ b/clang/test/SemaTemplate/undefined-template.cpp
@@ -37,15 +37,19 @@ char func_01() {
char func_02() {
return C1<int>::s_var_1; // expected-warning{{instantiation of variable 'C1<int>::s_var_1' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::s_var_1' is explicitly instantiated in another translation unit}}
- // expected-note at -2{{use 'extern template char C1<int>::s_var_1;'}}
- // expected-note at -3{{use 'template <> char C1<int>::s_var_1;'}}
+ // expected-note at -2{{there are 3 ways to fix this:}}
+ // expected-note at -3{{use 'extern template char C1<int>::s_var_1;'}}
+ // expected-note at -4{{or use 'template <> char C1<int>::s_var_1;'}}
+ // expected-note at -5{{or use 'template <class T> char C1<T>::s_var_1;' to provide a definition for all types}}
}
char func_03() {
return C1<char>::s_var_2; // expected-warning{{instantiation of variable 'C1<char>::s_var_2' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<char>::s_var_2' is explicitly instantiated in another translation unit}}
- // expected-note at -2{{use 'extern template char C1<char>::s_var_2;'}}
- // expected-note at -3{{use 'template <> char C1<char>::s_var_2;'}}
+ // expected-note at -2{{there are 3 ways to fix this:}}
+ // expected-note at -3{{use 'extern template char C1<char>::s_var_2;'}}
+ // expected-note at -4{{or use 'template <> char C1<char>::s_var_2;'}}
+ // expected-note at -5{{or use 'template <class T> char C1<T>::s_var_2;' to provide a definition for all types}}
}
void func_04() {
@@ -83,8 +87,9 @@ char func_10() {
char func_11() {
return C1<int>::s_tvar_2<long>; // expected-warning{{instantiation of variable 'C1<int>::s_tvar_2<long>' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::s_tvar_2<long>' is explicitly instantiated in another translation unit}}
- // expected-note at -2{{use 'extern template char C1<int>::s_tvar_2<long>;'}}
- // expected-note at -3{{use 'template <> char C1<int>::s_tvar_2<long>;'}}
+ // expected-note at -2{{there are 2 ways to fix this:}}
+ // expected-note at -3{{use 'extern template char C1<int>::s_tvar_2<long>;'}}
+ // expected-note at -4{{or use 'template <> char C1<int>::s_tvar_2<long>;'}}
}
void func_12() {
@@ -103,8 +108,9 @@ char func_14() {
char func_15() {
return C1<int>::C2<char>::s_var_2; //expected-warning {{instantiation of variable 'C1<int>::C2<char>::s_var_2' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::C2<char>::s_var_2' is explicitly instantiated in another translation unit}}
- // expected-note at -2{{use 'extern template char C1<int>::C2<char>::s_var_2;'}}
- // expected-note at -3{{use 'template <> char C1<int>::C2<char>::s_var_2;'}}
+ // expected-note at -2{{there are 2 ways to fix this:}}
+ // expected-note at -3{{use 'extern template char C1<int>::C2<char>::s_var_2;'}}
+ // expected-note at -4{{or use 'template <> char C1<int>::C2<char>::s_var_2;'}}
}
void func_16() {
@@ -132,8 +138,9 @@ char func_20() {
char func_21() {
return C1<int>::C2<long>::s_tvar_2<long>; // expected-warning{{instantiation of variable 'C1<int>::C2<long>::s_tvar_2<long>' required here, but no definition is available}}
// expected-note at -1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::C2<long>::s_tvar_2<long>' is explicitly instantiated in another translation unit}}
- // expected-note at -2{{use 'extern template char C1<int>::C2<long>::s_tvar_2<long>;'}}
- // expected-note at -3{{use 'template <> char C1<int>::C2<long>::s_tvar_2<long>;'}}
+ // expected-note at -2{{there are 2 ways to fix this:}}
+ // expected-note at -3{{use 'extern template char C1<int>::C2<long>::s_tvar_2<long>;'}}
+ // expected-note at -4{{or use 'template <> char C1<int>::C2<long>::s_tvar_2<long>;'}}
}
void func_22(C1<int>::C2<long> *x) {
More information about the cfe-commits
mailing list