Warn if function or variable cannot be implicitly instantiated
With this patch compiler emits warning if it tries to make implicit instantiation
of a template but cannot find the template definition. The warning can be suppressed
by explicit instantiation declaration or by command line options
-Wundefined-var-template and -Wundefined-func-template. The implementation follows
the discussion of http://reviews.llvm.org/D12326.
Differential Revision: http://reviews.llvm.org/D16396
llvm-svn: 266719
diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h
index bf61924..f7ddb5b 100644
--- a/clang/include/clang/AST/DeclBase.h
+++ b/clang/include/clang/AST/DeclBase.h
@@ -52,6 +52,7 @@
class RecordDecl;
class Stmt;
class StoredDeclsMap;
+class TemplateDecl;
class TranslationUnitDecl;
class UsingDirectiveDecl;
}
@@ -905,6 +906,10 @@
DeclKind == FunctionTemplate;
}
+ /// \brief If this is a declaration that describes some template, this
+ /// method returns that template declaration.
+ TemplateDecl *getDescribedTemplate() const;
+
/// \brief Returns the function itself, or the templated function if this is a
/// function template.
FunctionDecl *getAsFunction() LLVM_READONLY;
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index ddfccfb..587eb00 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/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">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index b89728c..1cb21f2 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3883,7 +3883,18 @@
"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 no definition is available">,
+ InGroup<UndefinedVarTemplate>;
+def warn_func_template_missing : Warning<"instantiation of function %q0 "
+ "required here, but no definition is available">,
+ InGroup<UndefinedFuncTemplate>, DefaultIgnore;
+def note_forward_template_decl : Note<
+ "forward declaration of template entity is here">;
+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<
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index abe18af..f53bc05 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/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,
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index 57149af..aec3b7c 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -196,6 +196,17 @@
return isa<TemplateDecl>(this);
}
+TemplateDecl *Decl::getDescribedTemplate() const {
+ if (auto *FD = dyn_cast<FunctionDecl>(this))
+ return FD->getDescribedFunctionTemplate();
+ else if (auto *RD = dyn_cast<CXXRecordDecl>(this))
+ return RD->getDescribedClassTemplate();
+ else if (auto *VD = dyn_cast<VarDecl>(this))
+ return VD->getDescribedVarTemplate();
+
+ return nullptr;
+}
+
const DeclContext *Decl::getParentFunctionOrMethod() const {
for (const DeclContext *DC = getDeclContext();
DC && !DC->isTranslationUnit() && !DC->isNamespace();
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 034b090..405a66f 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -9324,11 +9324,8 @@
}
static TemplateDecl *getDescribedTemplate(Decl *Templated) {
- if (FunctionDecl *FD = dyn_cast<FunctionDecl>(Templated))
- return FD->getDescribedFunctionTemplate();
- else if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Templated))
- return RD->getDescribedClassTemplate();
-
+ if (TemplateDecl *TD = Templated->getDescribedTemplate())
+ return TD;
llvm_unreachable("Unsupported: Getting the described template declaration"
" for bad deduction diagnosis");
}
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 307d804..8e42be0 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/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,16 @@
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;
+ Diag(PatternDecl->getLocation(), diag::note_forward_template_decl);
+ if (getLangOpts().CPlusPlus11)
+ Diag(PointOfInstantiation, diag::note_inst_declaration_hint)
+ << Function;
+ }
}
return;
@@ -3951,7 +3962,7 @@
void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
VarDecl *Var, bool Recursive,
- bool DefinitionRequired) {
+ bool DefinitionRequired, bool AtEndOfTU) {
if (Var->isInvalidDecl())
return;
@@ -4083,6 +4094,16 @@
== 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;
+ Diag(PatternDecl->getLocation(), diag::note_forward_template_decl);
+ if (getLangOpts().CPlusPlus11)
+ Diag(PointOfInstantiation, diag::note_inst_declaration_hint) << Var;
+ }
}
return;
@@ -4852,7 +4873,7 @@
bool DefinitionRequired = Function->getTemplateSpecializationKind() ==
TSK_ExplicitInstantiationDefinition;
InstantiateFunctionDefinition(/*FIXME:*/Inst.second, Function, true,
- DefinitionRequired);
+ DefinitionRequired, true);
continue;
}
@@ -4893,7 +4914,7 @@
// Instantiate static data member definitions or variable template
// specializations.
InstantiateVariableDefinition(/*FIXME:*/ Inst.second, Var, true,
- DefinitionRequired);
+ DefinitionRequired, true);
}
}
diff --git a/clang/test/CXX/temp/temp.decls/temp.mem/p1.cpp b/clang/test/CXX/temp/temp.decls/temp.mem/p1.cpp
index 01eab24..b48e145 100644
--- a/clang/test/CXX/temp/temp.decls/temp.mem/p1.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.mem/p1.cpp
@@ -10,6 +10,7 @@
}
};
};
+extern template bool A<bool>::cond;
int foo() {
A<bool>::cond = true;
diff --git a/clang/test/OpenMP/parallel_ast_print.cpp b/clang/test/OpenMP/parallel_ast_print.cpp
index 3391b2c..8a15339 100644
--- a/clang/test/OpenMP/parallel_ast_print.cpp
+++ b/clang/test/OpenMP/parallel_ast_print.cpp
@@ -227,4 +227,7 @@
}
}
+template<typename T>
+T S<T>::TS = 0;
+
#endif
diff --git a/clang/test/OpenMP/parallel_sections_ast_print.cpp b/clang/test/OpenMP/parallel_sections_ast_print.cpp
index 9f5c1fa..a66b75e 100644
--- a/clang/test/OpenMP/parallel_sections_ast_print.cpp
+++ b/clang/test/OpenMP/parallel_sections_ast_print.cpp
@@ -141,4 +141,7 @@
return tmain<int, 5>(b, &b) + tmain<long, 1>(x, &x);
}
+template<typename T>
+T S<T>::TS = 0;
+
#endif
diff --git a/clang/test/OpenMP/target_parallel_ast_print.cpp b/clang/test/OpenMP/target_parallel_ast_print.cpp
index ef48dc3..1c0fca5 100644
--- a/clang/test/OpenMP/target_parallel_ast_print.cpp
+++ b/clang/test/OpenMP/target_parallel_ast_print.cpp
@@ -227,4 +227,7 @@
return tmain<int, 5>(argc, &argc) + tmain<char, 1>(argv[0][0], argv[0]);
}
+extern template int S<int>::TS;
+extern template char S<char>::TS;
+
#endif
diff --git a/clang/test/OpenMP/task_ast_print.cpp b/clang/test/OpenMP/task_ast_print.cpp
index da3f5e4..37e5833 100644
--- a/clang/test/OpenMP/task_ast_print.cpp
+++ b/clang/test/OpenMP/task_ast_print.cpp
@@ -149,4 +149,7 @@
return tmain<int, 5>(b, &b) + tmain<long, 1>(x, &x);
}
+extern template int S<int>::TS;
+extern template long S<long>::TS;
+
#endif
diff --git a/clang/test/OpenMP/teams_ast_print.cpp b/clang/test/OpenMP/teams_ast_print.cpp
index 292586a..f3d577c 100644
--- a/clang/test/OpenMP/teams_ast_print.cpp
+++ b/clang/test/OpenMP/teams_ast_print.cpp
@@ -109,4 +109,6 @@
return tmain<int, 5>(b, &b) + tmain<long, 1>(x, &x);
}
+extern template int S<int>::TS;
+extern template long S<long>::TS;
#endif
diff --git a/clang/test/OpenMP/threadprivate_ast_print.cpp b/clang/test/OpenMP/threadprivate_ast_print.cpp
index 2d876c1..f789c2f 100644
--- a/clang/test/OpenMP/threadprivate_ast_print.cpp
+++ b/clang/test/OpenMP/threadprivate_ast_print.cpp
@@ -69,4 +69,5 @@
return (foo<int>());
}
+extern template int ST<int>::m;
#endif
diff --git a/clang/test/SemaCXX/PR10177.cpp b/clang/test/SemaCXX/PR10177.cpp
index e361ff3..9286e29 100644
--- a/clang/test/SemaCXX/PR10177.cpp
+++ b/clang/test/SemaCXX/PR10177.cpp
@@ -54,6 +54,7 @@
namespace { template<typename> extern int n; }
template<typename T> int g() { return n<int>; }
+namespace { extern template int n<int>; }
#endif
diff --git a/clang/test/SemaCXX/undefined-internal.cpp b/clang/test/SemaCXX/undefined-internal.cpp
index 29ca5de..59e6fdf 100644
--- a/clang/test/SemaCXX/undefined-internal.cpp
+++ b/clang/test/SemaCXX/undefined-internal.cpp
@@ -82,6 +82,7 @@
static int var; // expected-warning {{variable 'test5::B<test5::(anonymous namespace)::A>::var' has internal linkage but is not defined}}
static void foo(); // expected-warning {{function 'test5::B<test5::(anonymous namespace)::A>::foo' has internal linkage but is not defined}}
};
+ extern template int B<A>::var;
void test() {
B<A>::var = 0; // expected-note {{used here}}
diff --git a/clang/test/SemaTemplate/undefined-template.cpp b/clang/test/SemaTemplate/undefined-template.cpp
new file mode 100644
index 0000000..a03d0b7
--- /dev/null
+++ b/clang/test/SemaTemplate/undefined-template.cpp
@@ -0,0 +1,139 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 -Wundefined-func-template %s
+
+template <class T> struct C1 {
+ static char s_var_1; // expected-note{{forward declaration of template entity is here}}
+ static char s_var_2; // expected-note{{forward declaration of template entity is here}}
+ static void s_func_1(); // expected-note{{forward declaration of template entity is here}}
+ static void s_func_2(); // expected-note{{forward declaration of template entity is here}}
+ void meth_1(); // expected-note2{{forward declaration of template entity is here}}
+ void meth_2();
+ template <class T1> static char s_tvar_2; // expected-note{{forward declaration of template entity is here}}
+ template <class T1> static void s_tfunc_2(); // expected-note{{forward declaration of template entity is here}}
+ template<typename T1> struct C2 {
+ static char s_var_2; // expected-note{{forward declaration of template entity is here}}
+ static void s_func_2(); // expected-note{{forward declaration of template entity is here}}
+ void meth_2(); // expected-note{{forward declaration of template entity is here}}
+ template <class T2> static char s_tvar_2; // expected-note{{forward declaration of template entity is here}}
+ template <class T2> void tmeth_2(); // expected-note{{forward declaration of template entity is here}}
+ };
+};
+
+extern template char C1<int>::s_var_2;
+extern template void C1<int>::s_func_2();
+extern template void C1<int>::meth_2();
+extern template char C1<int>::s_tvar_2<char>;
+extern template void C1<int>::s_tfunc_2<char>();
+extern template void C1<int>::C2<long>::s_var_2;
+extern template void C1<int>::C2<long>::s_func_2();
+extern template void C1<int>::C2<long>::meth_2();
+extern template char C1<int>::C2<long>::s_tvar_2<char>;
+extern template void C1<int>::C2<long>::tmeth_2<char>();
+
+char func_01() {
+ return C1<int>::s_var_2;
+}
+
+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}}
+}
+
+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}}
+}
+
+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}}
+}
+
+void func_05() {
+ C1<int>::s_func_2();
+}
+
+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}}
+}
+
+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}}
+}
+
+void func_08(C1<int> *x) {
+ x->meth_2();
+}
+
+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}}
+}
+
+char func_10() {
+ return C1<int>::s_tvar_2<char>;
+}
+
+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}}
+}
+
+void func_12() {
+ C1<int>::s_tfunc_2<char>();
+}
+
+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}}
+}
+
+char func_14() {
+ return C1<int>::C2<long>::s_var_2;
+}
+
+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}}
+}
+
+void func_16() {
+ C1<int>::C2<long>::s_func_2();
+}
+
+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}}
+}
+
+void func_18(C1<int>::C2<long> *x) {
+ x->meth_2();
+}
+
+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}}
+}
+
+char func_20() {
+ return C1<int>::C2<long>::s_tvar_2<char>;
+}
+
+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}}
+}
+
+void func_22(C1<int>::C2<long> *x) {
+ x->tmeth_2<char>();
+}
+
+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}}
+}
+
+int main() {
+ return 0;
+}