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?= <[email protected]> Date: Sat, 21 Mar 2026 18:52:57 +0900 Subject: [PATCH 1/8] [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@-1 {{add an explicit instantiation}} + // expected-note@-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@-1 {{add an explicit instantiation}} + // expected-note@-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@-1 {{add an explicit instantiation}} + // expected-note@-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@-1 {{add an explicit instantiation}} + // expected-note@-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@-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@-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@-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@-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@-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@-6 {{used here}} +// expected-note@-6 {{e.g., 'extern template}} +// expected-note@-7 {{e.g., 'extern template}} +// expected-note@-8 {{e.g., 'extern template}} +// expected-note@-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@-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@-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@-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@-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@-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@-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@-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@-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@-1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::meth_1' is explicitly instantiated in another translation unit}} + // expected-note@-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@-1{{add an explicit instantiation declaration to suppress this warning if 'C1<char>::meth_1' is explicitly instantiated in another translation unit}} + // expected-note@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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@-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?= <[email protected]> Date: Sun, 22 Mar 2026 11:13:34 +0900 Subject: [PATCH 2/8] 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@-1 {{add an explicit instantiation}} - // expected-note@-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@-1 {{add an explicit instantiation}} - // expected-note@-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@-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@-1 {{add an explicit instantiation}} - // expected-note@-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@-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@-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@-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@-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@-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@-6 {{e.g., 'extern template}} -// expected-note@-7 {{e.g., 'extern template}} -// expected-note@-8 {{e.g., 'extern template}} -// expected-note@-9 {{used here}} +// expected-note@-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@-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@-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@-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@-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@-1{{add an explicit instantiation declaration to suppress this warning if 'C1<int>::meth_1' is explicitly instantiated in another translation unit}} - // expected-note@-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@-1{{add an explicit instantiation declaration to suppress this warning if 'C1<char>::meth_1' is explicitly instantiated in another translation unit}} - // expected-note@-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@-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@-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@-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@-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@-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@-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@-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@-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?= <[email protected]> Date: Sun, 22 Mar 2026 12:25:07 +0900 Subject: [PATCH 3/8] 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@-1 {{add an explicit instantiation}} - // expected-note@-2 {{e.g., 'extern template}} + // expected-note@-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@-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@-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@-2{{e.g., 'extern template char C1<int>::s_var_1;'}} + // expected-note@-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@-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@-2{{e.g., 'extern template char C1<char>::s_var_2;'}} + // expected-note@-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@-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@-2{{e.g., 'extern template char C1<int>::s_tvar_2<long>;'}} + // expected-note@-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@-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@-2{{e.g., 'extern template char C1<int>::C2<char>::s_var_2;'}} + // expected-note@-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@-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@-2{{e.g., 'extern template char C1<int>::C2<long>::s_tvar_2<long>;'}} + // expected-note@-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?= <[email protected]> Date: Sun, 22 Mar 2026 13:14:21 +0900 Subject: [PATCH 4/8] 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@-1 {{add an explicit instantiation}} // expected-note@-2 {{use 'extern template}} + // expected-note@-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@-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@-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@-2{{use 'extern template char C1<int>::s_var_1;'}} + // expected-note@-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@-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@-2{{use 'extern template char C1<char>::s_var_2;'}} + // expected-note@-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@-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@-2{{use 'extern template char C1<int>::s_tvar_2<long>;'}} + // expected-note@-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@-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@-2{{use 'extern template char C1<int>::C2<char>::s_var_2;'}} + // expected-note@-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@-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@-2{{use 'extern template char C1<int>::C2<long>::s_tvar_2<long>;'}} + // expected-note@-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?= <[email protected]> Date: Sun, 22 Mar 2026 14:30:57 +0900 Subject: [PATCH 5/8] 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@-1 {{add an explicit instantiation}} - // expected-note@-2 {{use 'extern template}} - // expected-note@-3 {{use 'template <>}} + // expected-note@-2 {{there are 3 ways to fix this:}} + // expected-note@-3 {{use 'extern template}} + // expected-note@-4 {{or use 'template <>}} + // expected-note@-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@-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@-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@-2{{use 'extern template char C1<int>::s_var_1;'}} - // expected-note@-3{{use 'template <> char C1<int>::s_var_1;'}} + // expected-note@-2{{there are 3 ways to fix this:}} + // expected-note@-3{{use 'extern template char C1<int>::s_var_1;'}} + // expected-note@-4{{or use 'template <> char C1<int>::s_var_1;'}} + // expected-note@-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@-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@-2{{use 'extern template char C1<char>::s_var_2;'}} - // expected-note@-3{{use 'template <> char C1<char>::s_var_2;'}} + // expected-note@-2{{there are 3 ways to fix this:}} + // expected-note@-3{{use 'extern template char C1<char>::s_var_2;'}} + // expected-note@-4{{or use 'template <> char C1<char>::s_var_2;'}} + // expected-note@-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@-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@-2{{use 'extern template char C1<int>::s_tvar_2<long>;'}} - // expected-note@-3{{use 'template <> char C1<int>::s_tvar_2<long>;'}} + // expected-note@-2{{there are 2 ways to fix this:}} + // expected-note@-3{{use 'extern template char C1<int>::s_tvar_2<long>;'}} + // expected-note@-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@-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@-2{{use 'extern template char C1<int>::C2<char>::s_var_2;'}} - // expected-note@-3{{use 'template <> char C1<int>::C2<char>::s_var_2;'}} + // expected-note@-2{{there are 2 ways to fix this:}} + // expected-note@-3{{use 'extern template char C1<int>::C2<char>::s_var_2;'}} + // expected-note@-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@-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@-2{{use 'extern template char C1<int>::C2<long>::s_tvar_2<long>;'}} - // expected-note@-3{{use 'template <> char C1<int>::C2<long>::s_tvar_2<long>;'}} + // expected-note@-2{{there are 2 ways to fix this:}} + // expected-note@-3{{use 'extern template char C1<int>::C2<long>::s_tvar_2<long>;'}} + // expected-note@-4{{or use 'template <> char C1<int>::C2<long>::s_tvar_2<long>;'}} } void func_22(C1<int>::C2<long> *x) { From b62e85c10fa24305789531de5f86de738a5b2575 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=8Ddiohabara=E5=8D=8D?= <[email protected]> Date: Sun, 22 Mar 2026 15:37:54 +0900 Subject: [PATCH 6/8] feat: support generic definition for nested/var templates --- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 71 ++++++++++++------- .../test/SemaTemplate/undefined-template.cpp | 9 ++- 2 files changed, 50 insertions(+), 30 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 2e064645146f2..7a6a18afb430b 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -6382,24 +6382,26 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation, if (getLangOpts().CPlusPlus11) { Diag(PointOfInstantiation, diag::note_inst_declaration_hint) << Var; - // 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. + // Determine if we can suggest a generic out-of-class definition + // by collecting all enclosing class template declarations. 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; + SmallVector<const ClassTemplateDecl *, 4> EnclosingCTDs; + const VarTemplateDecl *PatternVTD = + PatternDecl->getDescribedVarTemplate(); + { + const DeclContext *DC = PatternDecl->getDeclContext(); + bool Valid = true; + while (DC && !DC->isFileContext() && + !isa<NamespaceDecl>(DC)) { + const auto *RD = dyn_cast<CXXRecordDecl>(DC); + if (!RD) { Valid = false; break; } + const auto *CTD = RD->getDescribedClassTemplate(); + if (!CTD) { Valid = false; break; } + EnclosingCTDs.push_back(CTD); + DC = DC->getParent(); } + if (Valid && !EnclosingCTDs.empty()) + CanSuggestDefinition = true; } unsigned NumWays = CanSuggestDefinition ? 3 : 2; @@ -6443,20 +6445,35 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation, std::string DefSuggestion; { llvm::raw_string_ostream OS(DefSuggestion); - // print() already appends a trailing space. - PatternCTD->getTemplateParameters()->print(OS, getASTContext(), - getPrintingPolicy()); + // Print class template parameter lists (outermost to + // innermost). Each print() appends a trailing space. + for (auto It = EnclosingCTDs.rbegin(), + End = EnclosingCTDs.rend(); + It != End; ++It) + (*It)->getTemplateParameters()->print( + OS, getASTContext(), getPrintingPolicy()); + // Print variable template parameter list if present. + if (PatternVTD) + PatternVTD->getTemplateParameters()->print( + OS, getASTContext(), getPrintingPolicy()); + // Build qualified name: C1<T>::C2<T1>::varName 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(); + for (auto It = EnclosingCTDs.rbegin(), + End = EnclosingCTDs.rend(); + It != End; ++It) { + const auto *TPL = (*It)->getTemplateParameters(); + NameOS << (*It)->getName() << "<"; + for (unsigned I = 0, N = TPL->size(); I != N; ++I) { + if (I > 0) + NameOS << ", "; + NameOS << TPL->getParam(I)->getName(); + } + NameOS << ">::"; } - NameOS << ">::" << PatternDecl->getName(); - PatternDecl->getType().print(OS, getPrintingPolicy(), QualName); + NameOS << PatternDecl->getName(); + PatternDecl->getType().print(OS, getPrintingPolicy(), + QualName); OS << ";"; } Diag(PointOfInstantiation, diff --git a/clang/test/SemaTemplate/undefined-template.cpp b/clang/test/SemaTemplate/undefined-template.cpp index ca6bca0413039..1b19d58e14b7a 100644 --- a/clang/test/SemaTemplate/undefined-template.cpp +++ b/clang/test/SemaTemplate/undefined-template.cpp @@ -87,9 +87,10 @@ 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@-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@-2{{there are 2 ways to fix this:}} + // expected-note@-2{{there are 3 ways to fix this:}} // expected-note@-3{{use 'extern template char C1<int>::s_tvar_2<long>;'}} // expected-note@-4{{or use 'template <> char C1<int>::s_tvar_2<long>;'}} + // expected-note@-5{{or use 'template <class T> template <class T1> char C1<T>::s_tvar_2;' to provide a definition for all types}} } void func_12() { @@ -108,9 +109,10 @@ 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@-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@-2{{there are 2 ways to fix this:}} + // expected-note@-2{{there are 3 ways to fix this:}} // expected-note@-3{{use 'extern template char C1<int>::C2<char>::s_var_2;'}} // expected-note@-4{{or use 'template <> char C1<int>::C2<char>::s_var_2;'}} + // expected-note@-5{{or use 'template <class T> template <typename T1> char C1<T>::C2<T1>::s_var_2;' to provide a definition for all types}} } void func_16() { @@ -138,9 +140,10 @@ 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@-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@-2{{there are 2 ways to fix this:}} + // expected-note@-2{{there are 3 ways to fix this:}} // expected-note@-3{{use 'extern template char C1<int>::C2<long>::s_tvar_2<long>;'}} // expected-note@-4{{or use 'template <> char C1<int>::C2<long>::s_tvar_2<long>;'}} + // expected-note@-5{{or use 'template <class T> template <typename T1> template <class T2> char C1<T>::C2<T1>::s_tvar_2;' to provide a definition for all types}} } void func_22(C1<int>::C2<long> *x) { From f0b8baf64b2395d94c18943d8218b580aba81ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=8Ddiohabara=E5=8D=8D?= <[email protected]> Date: Sun, 22 Mar 2026 15:38:23 +0900 Subject: [PATCH 7/8] chore: format --- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 7a6a18afb430b..e6a7f253032cd 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -6391,12 +6391,17 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation, { const DeclContext *DC = PatternDecl->getDeclContext(); bool Valid = true; - while (DC && !DC->isFileContext() && - !isa<NamespaceDecl>(DC)) { + while (DC && !DC->isFileContext() && !isa<NamespaceDecl>(DC)) { const auto *RD = dyn_cast<CXXRecordDecl>(DC); - if (!RD) { Valid = false; break; } + if (!RD) { + Valid = false; + break; + } const auto *CTD = RD->getDescribedClassTemplate(); - if (!CTD) { Valid = false; break; } + if (!CTD) { + Valid = false; + break; + } EnclosingCTDs.push_back(CTD); DC = DC->getParent(); } @@ -6447,20 +6452,18 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation, llvm::raw_string_ostream OS(DefSuggestion); // Print class template parameter lists (outermost to // innermost). Each print() appends a trailing space. - for (auto It = EnclosingCTDs.rbegin(), - End = EnclosingCTDs.rend(); + for (auto It = EnclosingCTDs.rbegin(), End = EnclosingCTDs.rend(); It != End; ++It) - (*It)->getTemplateParameters()->print( - OS, getASTContext(), getPrintingPolicy()); + (*It)->getTemplateParameters()->print(OS, getASTContext(), + getPrintingPolicy()); // Print variable template parameter list if present. if (PatternVTD) - PatternVTD->getTemplateParameters()->print( - OS, getASTContext(), getPrintingPolicy()); + PatternVTD->getTemplateParameters()->print(OS, getASTContext(), + getPrintingPolicy()); // Build qualified name: C1<T>::C2<T1>::varName std::string QualName; llvm::raw_string_ostream NameOS(QualName); - for (auto It = EnclosingCTDs.rbegin(), - End = EnclosingCTDs.rend(); + for (auto It = EnclosingCTDs.rbegin(), End = EnclosingCTDs.rend(); It != End; ++It) { const auto *TPL = (*It)->getTemplateParameters(); NameOS << (*It)->getName() << "<"; @@ -6472,8 +6475,7 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation, NameOS << ">::"; } NameOS << PatternDecl->getName(); - PatternDecl->getType().print(OS, getPrintingPolicy(), - QualName); + PatternDecl->getType().print(OS, getPrintingPolicy(), QualName); OS << ";"; } Diag(PointOfInstantiation, From 10f2a3fc097e8123aebe519aa13e9918bab681dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=8Ddiohabara=E5=8D=8D?= <[email protected]> Date: Sun, 22 Mar 2026 16:03:53 +0900 Subject: [PATCH 8/8] fix: add tempalte<> prefixes for specialization --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 13 ++++++++++++- clang/test/SemaTemplate/undefined-template.cpp | 6 +++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index e6a7f253032cd..04b8146105384 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -6433,7 +6433,18 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation, std::string SpecSuggestion; { llvm::raw_string_ostream OS(SpecSuggestion); - OS << "template <> "; + // Count the number of template<> prefixes needed: one for + // each enclosing class template specialization, plus one if + // the variable itself is a variable template specialization. + unsigned NumSpecPrefixes = 0; + for (const DeclContext *DC = Var->getDeclContext(); DC; + DC = DC->getParent()) + if (isa<ClassTemplateSpecializationDecl>(DC)) + ++NumSpecPrefixes; + if (isa<VarTemplateSpecializationDecl>(Var)) + ++NumSpecPrefixes; + for (unsigned I = 0; I < NumSpecPrefixes; ++I) + OS << "template <> "; std::string QualName; llvm::raw_string_ostream NameOS(QualName); Var->getNameForDiagnostic(NameOS, getPrintingPolicy(), diff --git a/clang/test/SemaTemplate/undefined-template.cpp b/clang/test/SemaTemplate/undefined-template.cpp index 1b19d58e14b7a..39fe9194dc289 100644 --- a/clang/test/SemaTemplate/undefined-template.cpp +++ b/clang/test/SemaTemplate/undefined-template.cpp @@ -89,7 +89,7 @@ char func_11() { // expected-note@-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@-2{{there are 3 ways to fix this:}} // expected-note@-3{{use 'extern template char C1<int>::s_tvar_2<long>;'}} - // expected-note@-4{{or use 'template <> char C1<int>::s_tvar_2<long>;'}} + // expected-note@-4{{or use 'template <> template <> char C1<int>::s_tvar_2<long>;'}} // expected-note@-5{{or use 'template <class T> template <class T1> char C1<T>::s_tvar_2;' to provide a definition for all types}} } @@ -111,7 +111,7 @@ char func_15() { // expected-note@-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@-2{{there are 3 ways to fix this:}} // expected-note@-3{{use 'extern template char C1<int>::C2<char>::s_var_2;'}} - // expected-note@-4{{or use 'template <> char C1<int>::C2<char>::s_var_2;'}} + // expected-note@-4{{or use 'template <> template <> char C1<int>::C2<char>::s_var_2;'}} // expected-note@-5{{or use 'template <class T> template <typename T1> char C1<T>::C2<T1>::s_var_2;' to provide a definition for all types}} } @@ -142,7 +142,7 @@ char func_21() { // expected-note@-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@-2{{there are 3 ways to fix this:}} // expected-note@-3{{use 'extern template char C1<int>::C2<long>::s_tvar_2<long>;'}} - // expected-note@-4{{or use 'template <> char C1<int>::C2<long>::s_tvar_2<long>;'}} + // expected-note@-4{{or use 'template <> template <> template <> char C1<int>::C2<long>::s_tvar_2<long>;'}} // expected-note@-5{{or use 'template <class T> template <typename T1> template <class T2> char C1<T>::C2<T1>::s_tvar_2;' to provide a definition for all types}} } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
