Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -549,6 +549,7 @@ const clang::PrintingPolicy &getPrintingPolicy() const { return PrintingPolicy; } + clang::PrintingPolicy &getPrintingPolicy() { return PrintingPolicy; } void setPrintingPolicy(const clang::PrintingPolicy &Policy) { PrintingPolicy = Policy; Index: include/clang/AST/PrettyPrinter.h =================================================================== --- include/clang/AST/PrettyPrinter.h +++ include/clang/AST/PrettyPrinter.h @@ -42,6 +42,7 @@ ConstantArraySizeAsWritten(false), AnonymousTagLocations(true), SuppressStrongLifetime(false), SuppressLifetimeQualifiers(false), SuppressTemplateArgsInCXXConstructors(false), + PrintTemplateParameters(false), Bool(LO.Bool), TerseOutput(false), PolishForDeclaration(false), Half(LO.Half), MSWChar(LO.MicrosoftExt && !LO.WChar), IncludeNewlines(true), MSVCFormatting(false) { } @@ -143,6 +144,10 @@ /// constructors. unsigned SuppressTemplateArgsInCXXConstructors : 1; + /// \brief When true, a template declaration name is printed with parameters, + /// e.g. 'C1::C2' instead of 'C1::C2'. + bool PrintTemplateParameters : 1; + /// \brief Whether we can use 'bool' rather than '_Bool', even if the language /// doesn't actually have 'bool' (because, e.g., it is defined as a macro). unsigned Bool : 1; Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -75,6 +75,8 @@ def GNUDesignator : DiagGroup<"gnu-designator">; def GNUStringLiteralOperatorTemplate : DiagGroup<"gnu-string-literal-operator-template">; +def UndefinedVarTemplate : DiagGroup<"undefined-var-template">; +def UndefinedFuncTemplate : DiagGroup<"undefined-func-template">; def DeleteIncomplete : DiagGroup<"delete-incomplete">; def DeleteNonVirtualDtor : DiagGroup<"delete-non-virtual-dtor">; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -3883,7 +3883,15 @@ "in instantiation of template type alias %0 requested here">; def note_template_exception_spec_instantiation_here : Note< "in instantiation of exception specification for %0 requested here">; - +def warn_var_template_missing : Warning<"instantiation of variable %q0 " + "required here, but %qt1 is not defined">, InGroup; +def warn_func_template_missing : Warning<"instantiation of function %q0 " + "required here, but %qt1 is not defined">, InGroup, + DefaultIgnore; +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_default_arg_instantiation_here : Note< "in instantiation of default argument for '%0' required here">; def note_default_function_arg_instantiation_here : Note< Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -7171,7 +7171,8 @@ void InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, FunctionDecl *Function, bool Recursive = false, - bool DefinitionRequired = false); + bool DefinitionRequired = false, + bool AtEndOfTU = false); VarTemplateSpecializationDecl *BuildVarTemplateInstantiation( VarTemplateDecl *VarTemplate, VarDecl *FromVar, const TemplateArgumentList &TemplateArgList, @@ -7195,7 +7196,8 @@ const MultiLevelTemplateArgumentList &TemplateArgs); void InstantiateVariableDefinition(SourceLocation PointOfInstantiation, VarDecl *Var, bool Recursive = false, - bool DefinitionRequired = false); + bool DefinitionRequired = false, + bool AtEndOfTU = false); void InstantiateStaticDataMemberDefinition( SourceLocation PointOfInstantiation, VarDecl *Var, Index: lib/AST/ASTDiagnostic.cpp =================================================================== --- lib/AST/ASTDiagnostic.cpp +++ lib/AST/ASTDiagnostic.cpp @@ -318,6 +318,18 @@ bool PrintFromType, bool ElideType, bool ShowColors, raw_ostream &OS); +namespace { +/// \brief Helper class to restore PrintingPolicy::PrintTemplateParameters, when +/// it temporary changes. +struct SavedPrintTemplParamFlag { + PrintingPolicy &PPolicy; + bool SavedValue; + SavedPrintTemplParamFlag(PrintingPolicy &P) : PPolicy(P), + SavedValue(P.PrintTemplateParameters) {} + ~SavedPrintTemplParamFlag() { PPolicy.PrintTemplateParameters = SavedValue; } +}; +} + void clang::FormatASTNodeDiagnosticArgument( DiagnosticsEngine::ArgumentKind Kind, intptr_t Val, @@ -385,9 +397,13 @@ } case DiagnosticsEngine::ak_nameddecl: { bool Qualified; + SavedPrintTemplParamFlag PrintFlag(Context.getPrintingPolicy()); if (Modifier == "q" && Argument.empty()) Qualified = true; - else { + else if (Modifier == "qt" && Argument.empty()) { + Qualified = true; + Context.getPrintingPolicy().PrintTemplateParameters = true; + } else { assert(Modifier.empty() && Argument.empty() && "Invalid modifier for NamedDecl* argument"); Qualified = false; Index: lib/AST/Decl.cpp =================================================================== --- lib/AST/Decl.cpp +++ lib/AST/Decl.cpp @@ -1404,6 +1404,73 @@ printQualifiedName(OS, getASTContext().getPrintingPolicy()); } +namespace { + +/// \brief Class for printing template parameters in diagnostic messages. +/// +/// Allows a bit more readable reference to templates in the case of several +/// template levels. For instance, a reference to the template function \c meth +/// in the code: +/// +/// \code +/// template struct C1 { +/// template class TC> struct C2 { +/// template static void meth(); +/// }; +/// }; +/// \endcode +/// +/// is printed as C1::C2>::meth rather than C1::C2::meth. +/// +class TemplParamPrinter { + unsigned TypePNo; ///< Sequential number of type parameter. + unsigned NonTypePNo; ///< Sequential number of non-type parameter. + unsigned TemplPNo; ///< Sequential number of template parameter. + bool Enabled; ///< If true, parameters are printed. + +public: + TemplParamPrinter(bool E) + : TypePNo(0), NonTypePNo(0), TemplPNo(0), Enabled(E) {} + + void printTemplateParameters(raw_ostream &OS, const TemplateDecl *Templ) { + if (!Enabled) + return; + OS << "<"; + TemplateParameterList *TPL = Templ->getTemplateParameters(); + for (unsigned i = 0, e = TPL->size(); i != e; ++i) { + if (i != 0) + OS << ","; + const Decl *Param = TPL->getParam(i); + if (auto *TP = dyn_cast(Param)) { + if (TP->isParameterPack()) + OS << "..."; + OS << 'T'; + if (TypePNo) + OS << TypePNo; + TypePNo++; + } else if (auto *NT = dyn_cast(Param)) { + if (NT->isParameterPack()) + OS << "..."; + OS << 'N'; + if (NonTypePNo) + OS << NonTypePNo; + NonTypePNo++; + } else if (auto *TT = dyn_cast(Param)) { + if (TT->isParameterPack()) + OS << "..."; + OS << "TT"; + if (TemplPNo) + OS << TemplPNo; + OS << "<>"; + TemplPNo++; + } + } + OS << ">"; + } +}; + +} + void NamedDecl::printQualifiedName(raw_ostream &OS, const PrintingPolicy &P) const { const DeclContext *Ctx = getDeclContext(); @@ -1422,6 +1489,7 @@ Ctx = Ctx->getParent(); } + TemplParamPrinter TPPrinter(P.PrintTemplateParameters); for (ContextsTy::reverse_iterator I = Contexts.rbegin(), E = Contexts.rend(); I != E; ++I) { if (const auto *Spec = dyn_cast(*I)) { @@ -1444,8 +1512,12 @@ } else if (const auto *RD = dyn_cast(*I)) { if (!RD->getIdentifier()) OS << "(anonymous " << RD->getKindName() << ')'; - else + else { OS << *RD; + if (auto *CXXRD = dyn_cast(RD)) + if (ClassTemplateDecl *Templ = CXXRD->getDescribedClassTemplate()) + TPPrinter.printTemplateParameters(OS, Templ); + } } else if (const auto *FD = dyn_cast(*I)) { const FunctionProtoType *FT = nullptr; if (FD->hasWrittenPrototype()) @@ -1482,9 +1554,16 @@ OS << "::"; } - if (getDeclName()) - OS << *this; - else + if (getDeclName()) { + printName(OS); + if (auto FD = dyn_cast(this)) { + if (const FunctionTemplateDecl *FTD = FD->getDescribedFunctionTemplate()) + TPPrinter.printTemplateParameters(OS, FTD); + } else if (auto VD = dyn_cast(this)) { + if (const VarTemplateDecl *VTD = VD->getDescribedVarTemplate()) + TPPrinter.printTemplateParameters(OS, VTD); + } + } else OS << "(anonymous)"; } Index: lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- lib/Sema/SemaTemplateInstantiateDecl.cpp +++ lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3530,7 +3530,8 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, FunctionDecl *Function, bool Recursive, - bool DefinitionRequired) { + bool DefinitionRequired, + bool AtEndOfTU) { if (Function->isInvalidDecl() || Function->isDefined()) return; @@ -3604,6 +3605,15 @@ assert(!Recursive); PendingInstantiations.push_back( std::make_pair(Function, PointOfInstantiation)); + } else if (Function->getTemplateSpecializationKind() + == TSK_ImplicitInstantiation) { + if (AtEndOfTU && !getDiagnostics().hasErrorOccurred()) { + Diag(PointOfInstantiation, diag::warn_func_template_missing) + << Function << PatternDecl; + if (getLangOpts().CPlusPlus11) + Diag(PointOfInstantiation, diag::note_inst_declaration_hint) + << Function; + } } return; @@ -3951,7 +3961,7 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation, VarDecl *Var, bool Recursive, - bool DefinitionRequired) { + bool DefinitionRequired, bool AtEndOfTU) { if (Var->isInvalidDecl()) return; @@ -4083,6 +4093,15 @@ == TSK_ExplicitInstantiationDefinition) { PendingInstantiations.push_back( std::make_pair(Var, PointOfInstantiation)); + } else if (Var->getTemplateSpecializationKind() + == TSK_ImplicitInstantiation) { + // Warn about missing definition at the end of translation unit. + if (AtEndOfTU && !getDiagnostics().hasErrorOccurred()) { + Diag(PointOfInstantiation, diag::warn_var_template_missing) + << Var << PatternDecl; + if (getLangOpts().CPlusPlus11) + Diag(PointOfInstantiation, diag::note_inst_declaration_hint) << Var; + } } return; @@ -4852,7 +4871,7 @@ bool DefinitionRequired = Function->getTemplateSpecializationKind() == TSK_ExplicitInstantiationDefinition; InstantiateFunctionDefinition(/*FIXME:*/Inst.second, Function, true, - DefinitionRequired); + DefinitionRequired, true); continue; } @@ -4893,7 +4912,7 @@ // Instantiate static data member definitions or variable template // specializations. InstantiateVariableDefinition(/*FIXME:*/ Inst.second, Var, true, - DefinitionRequired); + DefinitionRequired, true); } } Index: test/CXX/temp/temp.decls/temp.mem/p1.cpp =================================================================== --- test/CXX/temp/temp.decls/temp.mem/p1.cpp +++ test/CXX/temp/temp.decls/temp.mem/p1.cpp @@ -10,6 +10,7 @@ } }; }; +extern template bool A::cond; int foo() { A::cond = true; Index: test/OpenMP/parallel_ast_print.cpp =================================================================== --- test/OpenMP/parallel_ast_print.cpp +++ test/OpenMP/parallel_ast_print.cpp @@ -227,4 +227,7 @@ } } +template +T S::TS = 0; + #endif Index: test/OpenMP/parallel_sections_ast_print.cpp =================================================================== --- test/OpenMP/parallel_sections_ast_print.cpp +++ test/OpenMP/parallel_sections_ast_print.cpp @@ -141,4 +141,7 @@ return tmain(b, &b) + tmain(x, &x); } +template +T S::TS = 0; + #endif Index: test/OpenMP/target_parallel_ast_print.cpp =================================================================== --- test/OpenMP/target_parallel_ast_print.cpp +++ test/OpenMP/target_parallel_ast_print.cpp @@ -227,4 +227,7 @@ return tmain(argc, &argc) + tmain(argv[0][0], argv[0]); } +extern template int S::TS; +extern template char S::TS; + #endif Index: test/OpenMP/task_ast_print.cpp =================================================================== --- test/OpenMP/task_ast_print.cpp +++ test/OpenMP/task_ast_print.cpp @@ -149,4 +149,7 @@ return tmain(b, &b) + tmain(x, &x); } +extern template int S::TS; +extern template long S::TS; + #endif Index: test/OpenMP/teams_ast_print.cpp =================================================================== --- test/OpenMP/teams_ast_print.cpp +++ test/OpenMP/teams_ast_print.cpp @@ -109,4 +109,6 @@ return tmain(b, &b) + tmain(x, &x); } +extern template int S::TS; +extern template long S::TS; #endif Index: test/OpenMP/threadprivate_ast_print.cpp =================================================================== --- test/OpenMP/threadprivate_ast_print.cpp +++ test/OpenMP/threadprivate_ast_print.cpp @@ -69,4 +69,5 @@ return (foo()); } +extern template int ST::m; #endif Index: test/SemaCXX/PR10177.cpp =================================================================== --- test/SemaCXX/PR10177.cpp +++ test/SemaCXX/PR10177.cpp @@ -54,6 +54,7 @@ namespace { template extern int n; } template int g() { return n; } +namespace { extern template int n; } #endif Index: test/SemaCXX/undefined-internal.cpp =================================================================== --- test/SemaCXX/undefined-internal.cpp +++ test/SemaCXX/undefined-internal.cpp @@ -82,6 +82,7 @@ static int var; // expected-warning {{variable 'test5::B::var' has internal linkage but is not defined}} static void foo(); // expected-warning {{function 'test5::B::foo' has internal linkage but is not defined}} }; + extern template int B::var; void test() { B::var = 0; // expected-note {{used here}} Index: test/SemaTemplate/undefined-template.cpp =================================================================== --- /dev/null +++ test/SemaTemplate/undefined-template.cpp @@ -0,0 +1,139 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 -Wundefined-func-template %s + +template struct C1 { + static char s_var_1; + static char s_var_2; + static void s_func_1(); + static void s_func_2(); + void meth_1(); + void meth_2(); + template static char s_tvar_2; + template static void s_tfunc_2(); + template struct C2 { + static char s_var_2; + static void s_func_2(); + void meth_2(); + template static char s_tvar_2; + template void tmeth_2(); + }; +}; + +extern template char C1::s_var_2; +extern template void C1::s_func_2(); +extern template void C1::meth_2(); +extern template char C1::s_tvar_2; +extern template void C1::s_tfunc_2(); +extern template void C1::C2::s_var_2; +extern template void C1::C2::s_func_2(); +extern template void C1::C2::meth_2(); +extern template char C1::C2::s_tvar_2; +extern template void C1::C2::tmeth_2(); + +char func_01() { + return C1::s_var_2; +} + +char func_02() { + return C1::s_var_1; // expected-warning{{instantiation of variable 'C1::s_var_1' required here, but 'C1::s_var_1' is not defined}} + // expected-note@-1{{add an explicit instantiation declaration to suppress this warning if 'C1::s_var_1' is explicitly instantiated in another translation unit}} +} + +char func_03() { + return C1::s_var_2; // expected-warning{{instantiation of variable 'C1::s_var_2' required here, but 'C1::s_var_2' is not defined}} + // expected-note@-1{{add an explicit instantiation declaration to suppress this warning if 'C1::s_var_2' is explicitly instantiated in another translation unit}} +} + +void func_04() { + C1::s_func_1(); // expected-warning{{instantiation of function 'C1::s_func_1' required here, but 'C1::s_func_1' is not defined}} + // expected-note@-1{{add an explicit instantiation declaration to suppress this warning if 'C1::s_func_1' is explicitly instantiated in another translation unit}} +} + +void func_05() { + C1::s_func_2(); +} + +void func_06() { + C1::s_func_2(); // expected-warning{{instantiation of function 'C1::s_func_2' required here, but 'C1::s_func_2' is not defined}} + // expected-note@-1{{add an explicit instantiation declaration to suppress this warning if 'C1::s_func_2' is explicitly instantiated in another translation unit}} +} + +void func_07(C1 *x) { + x->meth_1(); // expected-warning{{instantiation of function 'C1::meth_1' required here, but 'C1::meth_1' is not defined}} + // expected-note@-1{{add an explicit instantiation declaration to suppress this warning if 'C1::meth_1' is explicitly instantiated in another translation unit}} +} + +void func_08(C1 *x) { + x->meth_2(); +} + +void func_09(C1 *x) { + x->meth_1(); // expected-warning{{instantiation of function 'C1::meth_1' required here, but 'C1::meth_1' is not defined}} + // expected-note@-1{{add an explicit instantiation declaration to suppress this warning if 'C1::meth_1' is explicitly instantiated in another translation unit}} +} + +char func_10() { + return C1::s_tvar_2; +} + +char func_11() { + return C1::s_tvar_2; // expected-warning{{instantiation of variable 'C1::s_tvar_2' required here, but 'C1::s_tvar_2' is not defined}} + // expected-note@-1{{add an explicit instantiation declaration to suppress this warning if 'C1::s_tvar_2' is explicitly instantiated in another translation unit}} +} + +void func_12() { + C1::s_tfunc_2(); +} + +void func_13() { + C1::s_tfunc_2(); // expected-warning{{instantiation of function 'C1::s_tfunc_2' required here, but 'C1::s_tfunc_2' is not defined}} + // expected-note@-1{{add an explicit instantiation declaration to suppress this warning if 'C1::s_tfunc_2' is explicitly instantiated in another translation unit}} +} + +char func_14() { + return C1::C2::s_var_2; +} + +char func_15() { + return C1::C2::s_var_2; //expected-warning {{instantiation of variable 'C1::C2::s_var_2' required here, but 'C1::C2::s_var_2' is not defined}} + // expected-note@-1{{add an explicit instantiation declaration to suppress this warning if 'C1::C2::s_var_2' is explicitly instantiated in another translation unit}} +} + +void func_16() { + C1::C2::s_func_2(); +} + +void func_17() { + C1::C2::s_func_2(); // expected-warning{{instantiation of function 'C1::C2::s_func_2' required here, but 'C1::C2::s_func_2' is not defined}} + // expected-note@-1{{add an explicit instantiation declaration to suppress this warning if 'C1::C2::s_func_2' is explicitly instantiated in another translation unit}} +} + +void func_18(C1::C2 *x) { + x->meth_2(); +} + +void func_19(C1::C2 *x) { + x->meth_2(); // expected-warning{{instantiation of function 'C1::C2::meth_2' required here, but 'C1::C2::meth_2' is not defined}} + // expected-note@-1{{add an explicit instantiation declaration to suppress this warning if 'C1::C2::meth_2' is explicitly instantiated in another translation unit}} +} + +char func_20() { + return C1::C2::s_tvar_2; +} + +char func_21() { + return C1::C2::s_tvar_2; // expected-warning{{instantiation of variable 'C1::C2::s_tvar_2' required here, but 'C1::C2::s_tvar_2' is not defined}} + // expected-note@-1{{add an explicit instantiation declaration to suppress this warning if 'C1::C2::s_tvar_2' is explicitly instantiated in another translation unit}} +} + +void func_22(C1::C2 *x) { + x->tmeth_2(); +} + +void func_23(C1::C2 *x) { + x->tmeth_2(); // expected-warning{{instantiation of function 'C1::C2::tmeth_2' required here, but 'C1::C2::tmeth_2' is not defined}} + // expected-note@-1{{add an explicit instantiation declaration to suppress this warning if 'C1::C2::tmeth_2' is explicitly instantiated in another translation unit}} +} + +int main() { + return 0; +}