Skip to content

Commit

Permalink
Warn if function or variable cannot be implicitly instantiated
Browse files Browse the repository at this point in the history
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
  • Loading branch information
spavloff committed Apr 19, 2016
1 parent 77fa84e commit 7dcc97e
Show file tree
Hide file tree
Showing 17 changed files with 218 additions and 12 deletions.
5 changes: 5 additions & 0 deletions clang/include/clang/AST/DeclBase.h
Expand Up @@ -52,6 +52,7 @@ struct PrintingPolicy;
class RecordDecl;
class Stmt;
class StoredDeclsMap;
class TemplateDecl;
class TranslationUnitDecl;
class UsingDirectiveDecl;
}
Expand Down Expand Up @@ -905,6 +906,10 @@ class Decl {
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;
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticGroups.td
Expand Up @@ -75,6 +75,8 @@ def : DiagGroup<"ctor-dtor-privacy">;
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">;
Expand Down
13 changes: 12 additions & 1 deletion clang/include/clang/Basic/DiagnosticSemaKinds.td
Expand Up @@ -3883,7 +3883,18 @@ def note_template_type_alias_instantiation_here : Note<
"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<
Expand Down
6 changes: 4 additions & 2 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -7171,7 +7171,8 @@ class Sema {
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,
Expand All @@ -7195,7 +7196,8 @@ class Sema {
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,
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/AST/DeclBase.cpp
Expand Up @@ -196,6 +196,17 @@ bool Decl::isTemplateDecl() const {
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();
Expand Down
7 changes: 2 additions & 5 deletions clang/lib/Sema/SemaOverload.cpp
Expand Up @@ -9324,11 +9324,8 @@ static void DiagnoseArityMismatch(Sema &S, OverloadCandidate *Cand,
}

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");
}
Expand Down
29 changes: 25 additions & 4 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Expand Up @@ -3530,7 +3530,8 @@ TemplateDeclInstantiator::InitMethodInstantiation(CXXMethodDecl *New,
void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
FunctionDecl *Function,
bool Recursive,
bool DefinitionRequired) {
bool DefinitionRequired,
bool AtEndOfTU) {
if (Function->isInvalidDecl() || Function->isDefined())
return;

Expand Down Expand Up @@ -3604,6 +3605,16 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
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;
Expand Down Expand Up @@ -3951,7 +3962,7 @@ void Sema::InstantiateStaticDataMemberDefinition(

void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
VarDecl *Var, bool Recursive,
bool DefinitionRequired) {
bool DefinitionRequired, bool AtEndOfTU) {
if (Var->isInvalidDecl())
return;

Expand Down Expand Up @@ -4083,6 +4094,16 @@ void Sema::InstantiateVariableDefinition(SourceLocation PointOfInstantiation,
== 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;
Expand Down Expand Up @@ -4852,7 +4873,7 @@ void Sema::PerformPendingInstantiations(bool LocalOnly) {
bool DefinitionRequired = Function->getTemplateSpecializationKind() ==
TSK_ExplicitInstantiationDefinition;
InstantiateFunctionDefinition(/*FIXME:*/Inst.second, Function, true,
DefinitionRequired);
DefinitionRequired, true);
continue;
}

Expand Down Expand Up @@ -4893,7 +4914,7 @@ void Sema::PerformPendingInstantiations(bool LocalOnly) {
// Instantiate static data member definitions or variable template
// specializations.
InstantiateVariableDefinition(/*FIXME:*/ Inst.second, Var, true,
DefinitionRequired);
DefinitionRequired, true);
}
}

Expand Down
1 change: 1 addition & 0 deletions clang/test/CXX/temp/temp.decls/temp.mem/p1.cpp
Expand Up @@ -10,6 +10,7 @@ template <class T> struct A {
}
};
};
extern template bool A<bool>::cond;

int foo() {
A<bool>::cond = true;
Expand Down
3 changes: 3 additions & 0 deletions clang/test/OpenMP/parallel_ast_print.cpp
Expand Up @@ -227,4 +227,7 @@ void foo(const Foo<int> &arg) {
}
}

template<typename T>
T S<T>::TS = 0;

#endif
3 changes: 3 additions & 0 deletions clang/test/OpenMP/parallel_sections_ast_print.cpp
Expand Up @@ -141,4 +141,7 @@ int main(int argc, char **argv) {
return tmain<int, 5>(b, &b) + tmain<long, 1>(x, &x);
}

template<typename T>
T S<T>::TS = 0;

#endif
3 changes: 3 additions & 0 deletions clang/test/OpenMP/target_parallel_ast_print.cpp
Expand Up @@ -227,4 +227,7 @@ int main (int argc, char **argv) {
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
3 changes: 3 additions & 0 deletions clang/test/OpenMP/task_ast_print.cpp
Expand Up @@ -149,4 +149,7 @@ int main(int argc, char **argv) {
return tmain<int, 5>(b, &b) + tmain<long, 1>(x, &x);
}

extern template int S<int>::TS;
extern template long S<long>::TS;

#endif
2 changes: 2 additions & 0 deletions clang/test/OpenMP/teams_ast_print.cpp
Expand Up @@ -109,4 +109,6 @@ int main (int argc, char **argv) {
return tmain<int, 5>(b, &b) + tmain<long, 1>(x, &x);
}

extern template int S<int>::TS;
extern template long S<long>::TS;
#endif
1 change: 1 addition & 0 deletions clang/test/OpenMP/threadprivate_ast_print.cpp
Expand Up @@ -69,4 +69,5 @@ int main () {
return (foo<int>());
}

extern template int ST<int>::m;
#endif
1 change: 1 addition & 0 deletions clang/test/SemaCXX/PR10177.cpp
Expand Up @@ -54,6 +54,7 @@ namespace N {

namespace { template<typename> extern int n; }
template<typename T> int g() { return n<int>; }
namespace { extern template int n<int>; }

#endif

1 change: 1 addition & 0 deletions clang/test/SemaCXX/undefined-internal.cpp
Expand Up @@ -82,6 +82,7 @@ namespace test5 {
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}}
Expand Down
139 changes: 139 additions & 0 deletions 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;
}

0 comments on commit 7dcc97e

Please sign in to comment.