[cfe-commits] r168847 - in /cfe/trunk: include/clang/Sema/Sema.h lib/Sema/SemaDeclCXX.cpp test/SemaCXX/implicit-member-functions.cpp

Richard Smith richard-llvm at metafoo.co.uk
Wed Nov 28 17:34:08 PST 2012


Author: rsmith
Date: Wed Nov 28 19:34:07 2012
New Revision: 168847

URL: http://llvm.org/viewvc/llvm-project?rev=168847&view=rev
Log:
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.

Modified:
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/test/SemaCXX/implicit-member-functions.cpp

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=168847&r1=168846&r2=168847&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Wed Nov 28 19:34:07 2012
@@ -745,6 +745,24 @@
   /// of -Wselector.
   llvm::DenseMap<Selector, SourceLocation> ReferencedSelectors;
 
+  /// Kinds of C++ special members.
+  enum CXXSpecialMember {
+    CXXDefaultConstructor,
+    CXXCopyConstructor,
+    CXXMoveConstructor,
+    CXXCopyAssignment,
+    CXXMoveAssignment,
+    CXXDestructor,
+    CXXInvalid
+  };
+
+  typedef std::pair<CXXRecordDecl*, CXXSpecialMember> 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<SpecialMemberDecl, 4> SpecialMembersBeingDeclared;
+
   void ReadMethodPool(Selector Sel);
 
   /// Private Helper predicate to check for 'self'.
@@ -1507,15 +1525,6 @@
                             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);

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=168847&r1=168846&r2=168847&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Wed Nov 28 19:34:07 2012
@@ -6987,6 +6987,36 @@
   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 @@
   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 @@
   //   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 @@
   // 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 @@
   //   - [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 @@
   //   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 @@
   //   - [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,

Modified: cfe/trunk/test/SemaCXX/implicit-member-functions.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/implicit-member-functions.cpp?rev=168847&r1=168846&r2=168847&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/implicit-member-functions.cpp (original)
+++ cfe/trunk/test/SemaCXX/implicit-member-functions.cpp Wed Nov 28 19:34:07 2012
@@ -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 @@
   };
   C *C::c = new C();
 }
+
+namespace Recursion {
+  template<typename T> struct InvokeCopyConstructor {
+    static const T &get();
+    typedef decltype(T(get())) type; // expected-error {{no matching conver}}
+  };
+  struct B;
+  struct A {
+    typedef B type;
+    template<typename T,
+             typename = typename InvokeCopyConstructor<typename T::type>::type>
+    // expected-note at -1 {{in instantiation of template class}}
+    A(const T &);
+    // expected-note at -1 {{in instantiation of default argument}}
+    // expected-note at -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<B>, 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<typename T> T &&declval();
+  template<typename T, typename U> struct pair {
+    pair();
+    template<typename V, typename W,
+             typename = decltype(T(declval<const V&>())),
+             typename = decltype(U(declval<const W&>()))>
+    pair(const pair<V,W> &);
+  };
+
+  template<typename K> struct Line;
+
+  template<typename K> struct Vector {
+    Vector(const Line<K> &l);
+  };
+
+  template<typename K> struct Point {
+    Vector<K> v;
+  };
+
+  template<typename K> struct Line {
+    pair<Point<K>, Vector<K>> 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<void> L1;
+  Line<void> L2(L1);
+}





More information about the cfe-commits mailing list