PR22763: if a defaulted (non-user-provided) special member function is
authorRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 16 Nov 2017 23:54:56 +0000 (23:54 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 16 Nov 2017 23:54:56 +0000 (23:54 +0000)
explicitly instantiated, still emit it with each use.

We don't emit a definition of the member with an explicit instantiation
definition (and indeed it appears that we're not allowed to, since an explicit
instantiation definition does not constitute an odr-use and only odr-use
permits definition for defaulted special members). So we still need to emit a
weak definition with each use.

This also makes defaulted-in-class declarations behave more like
implicitly-declared special members, which matches their design intent.
And it matches the way this problem was solved in GCC.

llvm-svn: 318474

clang/include/clang/AST/DeclCXX.h
clang/lib/AST/ASTContext.cpp
clang/test/CodeGenCXX/cxx11-special-members.cpp
clang/test/CodeGenCXX/explicit-instantiation.cpp

index 59c9b35..09abd2f 100644 (file)
@@ -2061,7 +2061,11 @@ public:
   /// True if this method is user-declared and was not
   /// deleted or defaulted on its first declaration.
   bool isUserProvided() const {
-    return !(isDeleted() || getCanonicalDecl()->isDefaulted());
+    auto *DeclAsWritten = this;
+    if (auto *Pattern = getTemplateInstantiationPattern())
+      DeclAsWritten = cast<CXXMethodDecl>(Pattern);
+    return !(DeclAsWritten->isDeleted() ||
+             DeclAsWritten->getCanonicalDecl()->isDefaulted());
   }
 
   void addOverriddenMethod(const CXXMethodDecl *MD);
index 8247d1a..d3a5f07 100644 (file)
@@ -8963,6 +8963,12 @@ static GVALinkage basicGVALinkageForFunction(const ASTContext &Context,
   if (!FD->isExternallyVisible())
     return GVA_Internal;
 
+  // Non-user-provided functions get emitted as weak definitions with every
+  // use, no matter whether they've been explicitly instantiated etc.
+  if (auto *MD = dyn_cast<CXXMethodDecl>(FD))
+    if (!MD->isUserProvided())
+      return GVA_DiscardableODR;
+
   GVALinkage External;
   switch (FD->getTemplateSpecializationKind()) {
   case TSK_Undeclared:
index 037e59a..96109ba 100644 (file)
@@ -39,7 +39,9 @@ void f3() {
   C<0> a;
   D b;
 }
-// CHECK: define {{.*}} @_ZN1CILi0EEC1Ev
+// Trivial default ctor, might or might not be defined, but we must not expect
+// someone else ot define it.
+// CHECK-NOT: declare {{.*}} @_ZN1CILi0EEC1Ev
 // CHECK: define {{.*}} @_ZN1DC1Ev
 
 // CHECK: define {{.*}} @_ZN1BC2EOS_(
index 85857fb..ad20abd 100644 (file)
@@ -170,3 +170,22 @@ void use() {
   f<int>();
 }
 }
+
+namespace DefaultedMembers {
+  struct B { B(); B(const B&); ~B(); };
+  template<typename T> struct A : B {
+    A() = default;
+    ~A() = default;
+  };
+  extern template struct A<int>;
+
+  // CHECK-LABEL: define {{.*}} @_ZN16DefaultedMembers1AIiEC2Ev
+  // CHECK-LABEL: define {{.*}} @_ZN16DefaultedMembers1AIiED2Ev
+  A<int> ai;
+
+  // CHECK-LABEL: define {{.*}} @_ZN16DefaultedMembers1AIiEC2ERKS1_
+  A<int> ai2(ai);
+
+  // CHECK-NOT: @_ZN16DefaultedMembers1AIcE
+  template struct A<char>;
+}