From: Richard Smith Date: Thu, 29 Nov 2012 01:34:07 +0000 (+0000) Subject: The declaration of a special member can require overload resolution to be X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8bf22e5b5283da91aeff7685b01104db44de15e7;p=platform%2Fupstream%2Fllvm.git The declaration of a special member can require overload resolution to be performed, to determine whether that special member is deleted or constexpr. That overload resolution process can in turn trigger the instantiation of a template, which can do anything, including triggering the declaration of that very same special member function. When this happens, do not try to recursively declare the special member -- that's impossible. Instead, only try to realise the truth. There is no special member. llvm-svn: 168847 --- diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 1e6ac18..fea957d 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -745,6 +745,24 @@ public: /// of -Wselector. llvm::DenseMap ReferencedSelectors; + /// Kinds of C++ special members. + enum CXXSpecialMember { + CXXDefaultConstructor, + CXXCopyConstructor, + CXXMoveConstructor, + CXXCopyAssignment, + CXXMoveAssignment, + CXXDestructor, + CXXInvalid + }; + + typedef std::pair SpecialMemberDecl; + + /// The C++ special members which we are currently in the process of + /// declaring. If this process recursively triggers the declaration of the + /// same special member, we should act as if it is not yet declared. + llvm::SmallSet SpecialMembersBeingDeclared; + void ReadMethodPool(Selector Sel); /// Private Helper predicate to check for 'self'. @@ -1507,15 +1525,6 @@ public: AccessSpecifier AS, NamedDecl *PrevDecl, Declarator *D = 0); - enum CXXSpecialMember { - CXXDefaultConstructor, - CXXCopyConstructor, - CXXMoveConstructor, - CXXCopyAssignment, - CXXMoveAssignment, - CXXDestructor, - CXXInvalid - }; bool CheckNontrivialField(FieldDecl *FD); void DiagnoseNontrivial(const RecordType* Record, CXXSpecialMember mem); CXXSpecialMember getSpecialMember(const CXXMethodDecl *MD); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index b805679..96dc1c9 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -6987,6 +6987,36 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(SourceLocation Loc, return ExceptSpec; } +namespace { +/// RAII object to register a special member as being currently declared. +struct DeclaringSpecialMember { + Sema &S; + Sema::SpecialMemberDecl D; + bool WasAlreadyBeingDeclared; + + DeclaringSpecialMember(Sema &S, CXXRecordDecl *RD, Sema::CXXSpecialMember CSM) + : S(S), D(RD, CSM) { + WasAlreadyBeingDeclared = !S.SpecialMembersBeingDeclared.insert(D); + if (WasAlreadyBeingDeclared) + // This almost never happens, but if it does, ensure that our cache + // doesn't contain a stale result. + S.SpecialMemberCache.clear(); + + // FIXME: Register a note to be produced if we encounter an error while + // declaring the special member. + } + ~DeclaringSpecialMember() { + if (!WasAlreadyBeingDeclared) + S.SpecialMembersBeingDeclared.erase(D); + } + + /// \brief Are we already trying to declare this special member? + bool isAlreadyBeingDeclared() const { + return WasAlreadyBeingDeclared; + } +}; +} + CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( CXXRecordDecl *ClassDecl) { // C++ [class.ctor]p5: @@ -6998,6 +7028,10 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( assert(ClassDecl->needsImplicitDefaultConstructor() && "Should not build implicit default constructor!"); + DeclaringSpecialMember DSM(*this, ClassDecl, CXXDefaultConstructor); + if (DSM.isAlreadyBeingDeclared()) + return 0; + bool Constexpr = defaultedSpecialMemberIsConstexpr(*this, ClassDecl, CXXDefaultConstructor, false); @@ -7310,6 +7344,10 @@ CXXDestructorDecl *Sema::DeclareImplicitDestructor(CXXRecordDecl *ClassDecl) { // inline public member of its class. assert(!ClassDecl->hasDeclaredDestructor()); + DeclaringSpecialMember DSM(*this, ClassDecl, CXXDestructor); + if (DSM.isAlreadyBeingDeclared()) + return 0; + // Create the actual destructor declaration. CanQualType ClassType = Context.getCanonicalType(Context.getTypeDeclType(ClassDecl)); @@ -7793,6 +7831,10 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) { // operators taking an object instead of a reference are allowed. assert(!ClassDecl->hasDeclaredCopyAssignment()); + DeclaringSpecialMember DSM(*this, ClassDecl, CXXCopyAssignment); + if (DSM.isAlreadyBeingDeclared()) + return 0; + QualType ArgType = Context.getTypeDeclType(ClassDecl); QualType RetType = Context.getLValueReferenceType(ArgType); if (ClassDecl->implicitCopyAssignmentHasConstParam()) @@ -8212,6 +8254,10 @@ CXXMethodDecl *Sema::DeclareImplicitMoveAssignment(CXXRecordDecl *ClassDecl) { // - [first 4 bullets] assert(ClassDecl->needsImplicitMoveAssignment()); + DeclaringSpecialMember DSM(*this, ClassDecl, CXXMoveAssignment); + if (DSM.isAlreadyBeingDeclared()) + return 0; + // [Checked after we build the declaration] // - the move assignment operator would not be implicitly defined as // deleted, @@ -8569,6 +8615,10 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor( // constructor, one is declared implicitly. assert(!ClassDecl->hasDeclaredCopyConstructor()); + DeclaringSpecialMember DSM(*this, ClassDecl, CXXCopyConstructor); + if (DSM.isAlreadyBeingDeclared()) + return 0; + QualType ClassType = Context.getTypeDeclType(ClassDecl); QualType ArgType = ClassType; bool Const = ClassDecl->implicitCopyConstructorHasConstParam(); @@ -8739,6 +8789,10 @@ CXXConstructorDecl *Sema::DeclareImplicitMoveConstructor( // - [first 4 bullets] assert(ClassDecl->needsImplicitMoveConstructor()); + DeclaringSpecialMember DSM(*this, ClassDecl, CXXMoveConstructor); + if (DSM.isAlreadyBeingDeclared()) + return 0; + // [Checked after we build the declaration] // - the move assignment operator would not be implicitly defined as // deleted, diff --git a/clang/test/SemaCXX/implicit-member-functions.cpp b/clang/test/SemaCXX/implicit-member-functions.cpp index 8451739..b5f7fe1 100644 --- a/clang/test/SemaCXX/implicit-member-functions.cpp +++ b/clang/test/SemaCXX/implicit-member-functions.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s struct A { }; A::A() { } // expected-error {{definition of implicitly declared default constructor}} @@ -50,3 +50,70 @@ namespace PR7594 { }; C *C::c = new C(); } + +namespace Recursion { + template struct InvokeCopyConstructor { + static const T &get(); + typedef decltype(T(get())) type; // expected-error {{no matching conver}} + }; + struct B; + struct A { + typedef B type; + template::type> + // expected-note@-1 {{in instantiation of template class}} + A(const T &); + // expected-note@-1 {{in instantiation of default argument}} + // expected-note@-2 {{while substituting deduced template arguments}} + }; + struct B { // expected-note {{candidate constructor (the implicit move }} + B(); // expected-note {{candidate constructor not viable}} + A a; + }; + // Triggering the declaration of B's copy constructor causes overload + // resolution to occur for A's copying constructor, which instantiates + // InvokeCopyConstructor, which triggers the declaration of B's copy + // constructor. Notionally, this happens when we get to the end of the + // definition of 'struct B', so there is no declared copy constructor yet. + // + // This behavior is g++-compatible, but isn't exactly right; the class is + // supposed to be incomplete when we implicitly declare its special members. + B b = B(); + + + // Another case, which isn't ill-formed under our rules. This is inspired by + // a problem which occurs when combining CGAL with libstdc++-4.7. + + template T &&declval(); + template struct pair { + pair(); + template())), + typename = decltype(U(declval()))> + pair(const pair &); + }; + + template struct Line; + + template struct Vector { + Vector(const Line &l); + }; + + template struct Point { + Vector v; + }; + + template struct Line { + pair, Vector> x; + }; + + // Trigger declaration of Line copy ctor, which causes substitution into + // pair's templated constructor, which triggers instantiation of the + // definition of Point's copy constructor, which performs overload resolution + // on Vector's constructors, which requires declaring all of Line's + // constructors. That should not find a copy constructor (because we've not + // declared it yet), but by the time we get all the way back here, we should + // find the copy constructor. + Line L1; + Line L2(L1); +}