From 766e259e38e51c3773fcbd429f1faef52cb1de36 Mon Sep 17 00:00:00 2001 From: David Majnemer Date: Tue, 22 Oct 2013 04:14:18 +0000 Subject: [PATCH] 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 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 --- .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/lib/Sema/SemaTemplate.cpp | 16 ++- .../test/CXX/temp/temp.decls/temp.mem/p2.cpp | 12 +++ .../CodeGenCXX/mangle-local-class-names.cpp | 10 -- clang/test/PCH/cxx-local-templates.cpp | 55 ----------- clang/test/PCH/cxx1y-local-templates.cpp | 58 ----------- .../instantiate-exception-spec-cxx11.cpp | 13 +-- .../SemaTemplate/local-member-templates.cpp | 99 ------------------- 8 files changed, 35 insertions(+), 230 deletions(-) create mode 100644 clang/test/CXX/temp/temp.decls/temp.mem/p2.cpp delete mode 100644 clang/test/PCH/cxx-local-templates.cpp delete mode 100644 clang/test/PCH/cxx1y-local-templates.cpp delete mode 100644 clang/test/SemaTemplate/local-member-templates.cpp diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index fa5a3115b02a..415c4b29633a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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< diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 596995071dd0..c49756d9aa27 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -5459,8 +5459,20 @@ Sema::CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams) { while (Ctx && isa(Ctx)) Ctx = Ctx->getParent(); - if (Ctx && (Ctx->isFileContext() || Ctx->isRecord())) - return false; + if (Ctx) { + if (Ctx->isFileContext()) + return false; + if (CXXRecordDecl *RD = dyn_cast(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) diff --git a/clang/test/CXX/temp/temp.decls/temp.mem/p2.cpp b/clang/test/CXX/temp/temp.decls/temp.mem/p2.cpp new file mode 100644 index 000000000000..c24d5a9b50de --- /dev/null +++ b/clang/test/CXX/temp/temp.decls/temp.mem/p2.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +template +void quux(); + +void fun() { + struct foo { + template struct bar {}; // expected-error{{templates cannot be declared inside of a local class}} + template void baz() {} // expected-error{{templates cannot be declared inside of a local class}} + template void qux(); // expected-error{{templates cannot be declared inside of a local class}} + }; +} diff --git a/clang/test/CodeGenCXX/mangle-local-class-names.cpp b/clang/test/CodeGenCXX/mangle-local-class-names.cpp index 186d76a225a9..8b950fcd1776 100644 --- a/clang/test/CodeGenCXX/mangle-local-class-names.cpp +++ b/clang/test/CodeGenCXX/mangle-local-class-names.cpp @@ -75,16 +75,6 @@ inline void OmittingCode(float x) { } void CallOmittingCode() { OmittingCode(1); } -// CHECK: @_ZZ25LocalTemplateFunctionTestdEN5Local3fooIdEET_S1_ -int LocalTemplateFunctionTest(double d) { - struct Local { - template T foo(T t) { - return t; - } - }; - return Local().foo(d); -} - // CHECK: @_ZZ15LocalAnonStructvENUt0_1gEv inline void LocalAnonStruct() { if (0) { diff --git a/clang/test/PCH/cxx-local-templates.cpp b/clang/test/PCH/cxx-local-templates.cpp deleted file mode 100644 index 277ad831099f..000000000000 --- a/clang/test/PCH/cxx-local-templates.cpp +++ /dev/null @@ -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 T foo(T t) { - return t; - } - }; - return Local{}.foo(d); -} - -template -U template_test(U d) { - struct Local { - template T foo(T t) { - return t; - } - }; - return Local{}.foo(d); -} - -int nested_local() { - struct Inner1 { - int inner1_foo(char c) { - struct Inner2 { - template 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 diff --git a/clang/test/PCH/cxx1y-local-templates.cpp b/clang/test/PCH/cxx1y-local-templates.cpp deleted file mode 100644 index ccab03d4b4ea..000000000000 --- a/clang/test/PCH/cxx1y-local-templates.cpp +++ /dev/null @@ -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 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 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 diff --git a/clang/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp b/clang/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp index 97bf00303bbe..5f43ea2c27a1 100644 --- a/clang/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp +++ b/clang/test/SemaTemplate/instantiate-exception-spec-cxx11.cpp @@ -44,13 +44,14 @@ namespace dr1330_example { A().f(42); } + struct S { + template + static int f() noexcept(noexcept(A().f("boo!"))) { return 0; } // \ + // expected-note {{instantiation of exception spec}} + typedef decltype(f()) X; + }; + int test2() { - struct S { - template - static int f() noexcept(noexcept(A().f("boo!"))) { return 0; } // \ - // expected-note {{instantiation of exception spec}} - typedef decltype(f()) X; - }; S().f(); // ok S().f(); // expected-note {{instantiation of exception spec}} } diff --git a/clang/test/SemaTemplate/local-member-templates.cpp b/clang/test/SemaTemplate/local-member-templates.cpp deleted file mode 100644 index 847d483a0021..000000000000 --- a/clang/test/SemaTemplate/local-member-templates.cpp +++ /dev/null @@ -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 struct Outer { - template int outer_mem(T t, U u) { - struct Inner { - template int inner_mem(T t, U u, V v) { - struct InnerInner { - template 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 int outer_mem(T t, U *u); -}; - -template int Outer::outer_mem(int, char); - -template template int Outer::outer_mem(T t, U *u) { - struct Inner { - template - int inner_mem(T t, U u, V v) { //expected-note{{candidate function}} - struct InnerInner { - template 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::outer_mem(int, char *); //expected-note{{in instantiation of function}} - -} // end ns - -namespace nested_local_templates_2 { - -template struct Outer { - template void outer_mem(T t, U u) { - struct Inner { - template struct InnerTemplateClass { - template - void itc_mem(T t, U u, V v, W w) { //expected-note{{candidate function}} - struct InnerInnerInner { - template void iii_mem(X x) {} - }; - InnerInnerInner i; - i.iii_mem("abc"); - } - }; - }; - Inner i; - typename Inner::template InnerTemplateClass ii; - ii.itc_mem(t, u, i, "jim"); - ii.itc_mem(t, u, 0, "abd"); //expected-error{{no matching member function}} - } -}; - -template void -Outer::outer_mem(int, char); //expected-note{{in instantiation of}} - -} - -namespace more_nested_local_templates { - -int test() { - struct Local { - template void foo(U u) { - struct Inner { - template - 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(); -} \ No newline at end of file