From 192d1798b26682c460740723f811cc9b33c1c113 Mon Sep 17 00:00:00 2001 From: David Majnemer Date: Wed, 27 Nov 2013 08:20:38 +0000 Subject: Sema: Instantiate local class and their members appropriately We would fail to instantiate them when the surrounding function was instantiated. Instantiate the class and add it's members to the list of pending instantiations, they should be resolved when we are finished with the function's body. This fixes PR9685. llvm-svn: 195827 --- clang/lib/Sema/SemaTemplate.cpp | 5 +- clang/lib/Sema/SemaTemplateInstantiate.cpp | 8 ++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 48 ++++++++--- clang/test/CXX/temp/temp.spec/temp.inst/p1.cpp | 11 ++- .../test/SemaTemplate/instantiate-local-class.cpp | 96 +++++++++++++++++++++- 5 files changed, 147 insertions(+), 21 deletions(-) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 63d8d8db26f..e995ae1c36e 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -6265,7 +6265,10 @@ Sema::CheckSpecializationInstantiationRedecl(SourceLocation NewLoc, switch (NewTSK) { case TSK_Undeclared: case TSK_ImplicitInstantiation: - llvm_unreachable("Don't check implicit instantiations here"); + assert( + (PrevTSK == TSK_Undeclared || PrevTSK == TSK_ImplicitInstantiation) && + "previous declaration must be implicit!"); + return false; case TSK_ExplicitSpecialization: switch (PrevTSK) { diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 40e020dc076..7dc8d2b1957 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -2456,6 +2456,11 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation, CXXRecordDecl *Instantiation, const MultiLevelTemplateArgumentList &TemplateArgs, TemplateSpecializationKind TSK) { + assert( + (TSK == TSK_ExplicitInstantiationDefinition || + TSK == TSK_ExplicitInstantiationDeclaration || + (TSK == TSK_ImplicitInstantiation && Instantiation->isLocalClass())) && + "Unexpected template specialization kind!"); for (DeclContext::decl_iterator D = Instantiation->decls_begin(), DEnd = Instantiation->decls_end(); D != DEnd; ++D) { @@ -2496,6 +2501,9 @@ Sema::InstantiateClassMembers(SourceLocation PointOfInstantiation, InstantiateFunctionDefinition(PointOfInstantiation, Function); } else { Function->setTemplateSpecializationKind(TSK, PointOfInstantiation); + if (TSK == TSK_ImplicitInstantiation) + PendingLocalImplicitInstantiations.push_back( + std::make_pair(Function, PointOfInstantiation)); } } } else if (VarDecl *Var = dyn_cast(*D)) { diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index a0af0329530..3e192490583 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -25,6 +25,17 @@ using namespace clang; +static bool isDeclWithinFunction(const Decl *D) { + const DeclContext *DC = D->getDeclContext(); + if (DC->isFunctionOrMethod()) + return true; + + if (DC->isRecord()) + return cast(DC)->isLocalClass(); + + return false; +} + bool TemplateDeclInstantiator::SubstQualifier(const DeclaratorDecl *OldDecl, DeclaratorDecl *NewDecl) { if (!OldDecl->getQualifierLoc()) @@ -655,19 +666,17 @@ Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) { } } - if (D->getDeclContext()->isFunctionOrMethod()) - SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, Enum); - // C++11 [temp.inst]p1: The implicit instantiation of a class template // specialization causes the implicit instantiation of the declarations, but // not the definitions of scoped member enumerations. - // FIXME: There appears to be no wording for what happens for an enum defined - // within a block scope, but we treat that much like a member template. Only - // instantiate the definition when visiting the definition in that case, since - // we will visit all redeclarations. - if (!Enum->isScoped() && Def && - (!D->getDeclContext()->isFunctionOrMethod() || D->isCompleteDefinition())) + // + // DR1484 clarifies that enumeration definitions inside of a template + // declaration aren't considered entities that can be separately instantiated + // from the rest of the entity they are declared inside of. + if (isDeclWithinFunction(D) ? D == Def : Def && !Enum->isScoped()) { + SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, Enum); InstantiateEnumDefinition(Enum, Def); + } return Enum; } @@ -1118,13 +1127,26 @@ Decl *TemplateDeclInstantiator::VisitCXXRecordDecl(CXXRecordDecl *D) { Record->setObjectOfFriendDecl(); // Make sure that anonymous structs and unions are recorded. - if (D->isAnonymousStructOrUnion()) { + if (D->isAnonymousStructOrUnion()) Record->setAnonymousStructOrUnion(true); - if (Record->getDeclContext()->getRedeclContext()->isFunctionOrMethod()) - SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, Record); - } + + if (D->isLocalClass()) + SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, Record); Owner->addDecl(Record); + + // DR1484 clarifies that the members of a local class are instantiated as part + // of the instantiation of their enclosing entity. + if (D->isCompleteDefinition() && D->isLocalClass()) { + if (SemaRef.InstantiateClass(D->getLocation(), Record, D, TemplateArgs, + TSK_ImplicitInstantiation, + /*Complain=*/true)) { + llvm_unreachable("InstantiateClass shouldn't fail here!"); + } else { + SemaRef.InstantiateClassMembers(D->getLocation(), Record, TemplateArgs, + TSK_ImplicitInstantiation); + } + } return Record; } diff --git a/clang/test/CXX/temp/temp.spec/temp.inst/p1.cpp b/clang/test/CXX/temp/temp.spec/temp.inst/p1.cpp index 8684fc4dabd..adf812b714d 100644 --- a/clang/test/CXX/temp/temp.spec/temp.inst/p1.cpp +++ b/clang/test/CXX/temp/temp.spec/temp.inst/p1.cpp @@ -33,24 +33,23 @@ namespace ScopedEnum { ScopedEnum1::E e1; // ok ScopedEnum1::E e2 = decltype(e2)::e; // expected-note {{in instantiation of enumeration 'ScopedEnum::ScopedEnum1::E' requested here}} - // The behavior for enums defined within function templates is not clearly - // specified by the standard. We follow the rules for enums defined within - // class templates. + // DR1484 specifies that enumerations cannot be separately instantiated, + // they will be instantiated with the rest of the template declaration. template int f() { enum class E { - e = T::error + e = T::error // expected-error {{has no members}} }; return (int)E(); } - int test1 = f(); + int test1 = f(); // expected-note {{here}} template int g() { enum class E { e = T::error // expected-error {{has no members}} }; - return E::e; // expected-note {{here}} + return E::e; } int test2 = g(); // expected-note {{here}} } diff --git a/clang/test/SemaTemplate/instantiate-local-class.cpp b/clang/test/SemaTemplate/instantiate-local-class.cpp index c151fbb9a5b..2b5db0fda3e 100644 --- a/clang/test/SemaTemplate/instantiate-local-class.cpp +++ b/clang/test/SemaTemplate/instantiate-local-class.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -verify %s +// RUN: %clang_cc1 -verify -std=c++11 %s // expected-no-diagnostics template void f0() { @@ -66,3 +66,97 @@ namespace PR8801 { template void foo(); } + +namespace TemplatePacksAndLambdas { + template int g(T...); + struct S { + template static void f(int f = g([]{ static T t; return ++t; }()...)) {} + }; + void h() { S::f(); } +} + +namespace PR9685 { + template void forEach(Thing t) { t.func(); } + + template void doIt() { + struct Functor { + void func() { (void)i; } + int i; + }; + + forEach(Functor()); + } + + void call() { + doIt(); + } +} + +namespace PR12702 { + struct S { + template bool apply(F f) { return f(); } + }; + + template struct T { + void foo() { + struct F { + int x; + + bool operator()() { return x == 0; } + }; + + S().apply(F()); + } + }; + + void call() { T().foo(); } +} + +namespace PR17139 { + template void foo(const T &t) { t.foo(); } + + template void bar(F *f) { + struct B { + F *fn; + void foo() const { fn(); } + } b = { f }; + foo(b); + } + + void go() {} + + void test() { bar(go); } +} + +namespace PR17740 { +class C { +public: + template static void foo(T function); + template static void bar(T function); + template static void func(T function); +}; + +template void C::foo(T function) { function(); } + +template void C::bar(T function) { + foo([&function]() { function(); }); +} + +template void C::func(T function) { + struct Struct { + T mFunction; + + Struct(T function) : mFunction(function) {}; + + void operator()() { + mFunction(); + }; + }; + + bar(Struct(function)); +} + +void call() { + C::func([]() {}); +} +} -- cgit v1.2.1