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:
parent
a93033cc89
commit
766e259e38
|
@ -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<
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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}}
|
||||
};
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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}}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
Loading…
Reference in New Issue