Sema: Do not allow template declarations inside local classes

Summary:
Enforce the rule in C++11 [temp.mem]p2 that local classes cannot have
member templates.

This fixes PR16947.

N.B.  C++14 has slightly different wording to afford generic lambdas
declared inside of functions.

Fun fact:  Some formulations of local classes with member templates
would cause clang to crash during Itanium mangling, such as the
following:

void outer_mem() {
  struct Inner {
    template <typename = void>
    struct InnerTemplateClass {
      static void itc_mem() {}
    };
  };
  Inner::InnerTemplateClass<>::itc_mem();
}

Reviewers: eli.friedman, rsmith, doug.gregor, faisalv

Reviewed By: doug.gregor

CC: cfe-commits, ygao

Differential Revision: http://llvm-reviews.chandlerc.com/D1866

llvm-svn: 193144
This commit is contained in:
David Majnemer 2013-10-22 04:14:18 +00:00
parent a93033cc89
commit 766e259e38
8 changed files with 35 additions and 230 deletions

View File

@ -2835,6 +2835,8 @@ def warn_template_export_unsupported : Warning<
"exported templates are unsupported">;
def err_template_outside_namespace_or_class_scope : Error<
"templates can only be declared in namespace or class scope">;
def err_template_inside_local_class : Error<
"templates cannot be declared inside of a local class">;
def err_template_linkage : Error<"templates must have C++ linkage">;
def err_template_typedef : Error<"a typedef cannot be a template">;
def err_template_unnamed_class : Error<

View File

@ -5459,8 +5459,20 @@ Sema::CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams) {
while (Ctx && isa<LinkageSpecDecl>(Ctx))
Ctx = Ctx->getParent();
if (Ctx && (Ctx->isFileContext() || Ctx->isRecord()))
return false;
if (Ctx) {
if (Ctx->isFileContext())
return false;
if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Ctx)) {
// C++ [temp.mem]p2:
// A local class shall not have member templates.
if (RD->isLocalClass())
return Diag(TemplateParams->getTemplateLoc(),
diag::err_template_inside_local_class)
<< TemplateParams->getSourceRange();
else
return false;
}
}
return Diag(TemplateParams->getTemplateLoc(),
diag::err_template_outside_namespace_or_class_scope)

View File

@ -0,0 +1,12 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
template <typename>
void quux();
void fun() {
struct foo {
template <typename> struct bar {}; // expected-error{{templates cannot be declared inside of a local class}}
template <typename> void baz() {} // expected-error{{templates cannot be declared inside of a local class}}
template <typename> void qux(); // expected-error{{templates cannot be declared inside of a local class}}
};
}

View File

@ -75,16 +75,6 @@ inline void OmittingCode(float x) {
}
void CallOmittingCode() { OmittingCode(1); }
// CHECK: @_ZZ25LocalTemplateFunctionTestdEN5Local3fooIdEET_S1_
int LocalTemplateFunctionTest(double d) {
struct Local {
template<class T> T foo(T t) {
return t;
}
};
return Local().foo(d);
}
// CHECK: @_ZZ15LocalAnonStructvENUt0_1gEv
inline void LocalAnonStruct() {
if (0) {

View File

@ -1,55 +0,0 @@
// RUN: %clang_cc1 -pedantic-errors -std=c++11 -emit-pch %s -o %t-cxx11
// RUN: %clang_cc1 -ast-print -pedantic-errors -std=c++11 -include-pch %t-cxx11 %s | FileCheck -check-prefix=CHECK-PRINT %s
#ifndef HEADER_INCLUDED
#define HEADER_INCLUDED
int nontemplate_test(double d) {
struct Local {
template<class T> T foo(T t) {
return t;
}
};
return Local{}.foo(d);
}
template<class U>
U template_test(U d) {
struct Local {
template<class T> T foo(T t) {
return t;
}
};
return Local{}.foo(d);
}
int nested_local() {
struct Inner1 {
int inner1_foo(char c) {
struct Inner2 {
template<class T> T inner2_foo(T t) {
return t;
}
};
return Inner2{}.inner2_foo(3.14);
}
};
return Inner1{}.inner1_foo('a');
}
#else
// CHECK-PRINT: U template_test
// CHECK-PRINT: int nontemplate_test(double)
int nontemplate_test(double);
template double template_test(double);
int test2(int y) {
return nontemplate_test(y) + template_test(y);
}
#endif

View File

@ -1,58 +0,0 @@
// RUN: %clang_cc1 -pedantic-errors -std=c++1y -emit-pch %s -o %t-cxx1y
// RUN: %clang_cc1 -ast-print -pedantic-errors -std=c++1y -include-pch %t-cxx1y %s | FileCheck -check-prefix=CHECK-PRINT %s
#ifndef HEADER_INCLUDED
#define HEADER_INCLUDED
auto nested_local_call_all() {
struct Inner1 {
auto inner1_foo(char c) {
struct Inner2 {
template<class T> T inner2_foo(T t) {
return t;
}
};
return Inner2{};
}
};
return Inner1{}.inner1_foo('a').inner2_foo(4);
}
auto nested_local() {
struct Inner1 {
auto inner1_foo(char c) {
struct Inner2 {
template<class T> T inner2_foo(T t) {
return t;
}
};
return Inner2{};
}
};
return Inner1{};
}
int test() {
auto A = nested_local_call_all();
auto B = nested_local();
auto C = B.inner1_foo('a');
C.inner2_foo(3.14);
}
#else
// CHECK-PRINT: int nested_local_call_all
// CHECK-PRINT: nested_local
auto nested_local_call_all();
int test(int y) {
return nested_local_call_all();
}
#endif

View File

@ -44,13 +44,14 @@ namespace dr1330_example {
A<int>().f(42);
}
struct S {
template<typename T>
static int f() noexcept(noexcept(A<T>().f("boo!"))) { return 0; } // \
// expected-note {{instantiation of exception spec}}
typedef decltype(f<S>()) X;
};
int test2() {
struct S {
template<typename T>
static int f() noexcept(noexcept(A<T>().f("boo!"))) { return 0; } // \
// expected-note {{instantiation of exception spec}}
typedef decltype(f<S>()) X;
};
S().f<S>(); // ok
S().f<int>(); // expected-note {{instantiation of exception spec}}
}

View File

@ -1,99 +0,0 @@
// RUN: %clang_cc1 -std=c++1y -verify %s
// RUN: %clang_cc1 -std=c++1y -verify %s -fdelayed-template-parsing
namespace nested_local_templates_1 {
template <class T> struct Outer {
template <class U> int outer_mem(T t, U u) {
struct Inner {
template <class V> int inner_mem(T t, U u, V v) {
struct InnerInner {
template <class W> int inner_inner_mem(W w, T t, U u, V v) {
return 0;
}
};
InnerInner().inner_inner_mem("abc", t, u, v);
return 0;
}
};
Inner i;
i.inner_mem(t, u, 3.14);
return 0;
}
template <class U> int outer_mem(T t, U *u);
};
template int Outer<int>::outer_mem(int, char);
template <class T> template <class U> int Outer<T>::outer_mem(T t, U *u) {
struct Inner {
template <class V>
int inner_mem(T t, U u, V v) { //expected-note{{candidate function}}
struct InnerInner {
template <class W> int inner_inner_mem(W w, T t, U u, V v) { return 0; }
};
InnerInner().inner_inner_mem("abc", t, u, v);
return 0;
}
};
Inner i;
i.inner_mem(t, U{}, i);
i.inner_mem(t, u, 3.14); //expected-error{{no matching member function for call to 'inner}}
return 0;
}
template int Outer<int>::outer_mem(int, char *); //expected-note{{in instantiation of function}}
} // end ns
namespace nested_local_templates_2 {
template <class T> struct Outer {
template <class U> void outer_mem(T t, U u) {
struct Inner {
template <class V> struct InnerTemplateClass {
template <class W>
void itc_mem(T t, U u, V v, W w) { //expected-note{{candidate function}}
struct InnerInnerInner {
template <class X> void iii_mem(X x) {}
};
InnerInnerInner i;
i.iii_mem("abc");
}
};
};
Inner i;
typename Inner::template InnerTemplateClass<Inner> ii;
ii.itc_mem(t, u, i, "jim");
ii.itc_mem(t, u, 0, "abd"); //expected-error{{no matching member function}}
}
};
template void
Outer<int>::outer_mem(int, char); //expected-note{{in instantiation of}}
}
namespace more_nested_local_templates {
int test() {
struct Local {
template<class U> void foo(U u) {
struct Inner {
template<class A>
auto operator()(A a, U u2) -> U {
return u2;
};
};
Inner GL;
GL('a', u );
GL(3.14, u );
}
};
Local l;
l.foo("nmabc");
return 0;
}
int t = test();
}