]> granicus.if.org Git - clang/commitdiff
Implement DR257 / fix PR16659:
authorRichard Smith <richard-llvm@metafoo.co.uk>
Mon, 22 Jul 2013 02:56:56 +0000 (02:56 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Mon, 22 Jul 2013 02:56:56 +0000 (02:56 +0000)
  A constructor for an abstract class does not call constructors for virtual
  base classes, so it is not an error if no initializer is present for the
  virtual base and the virtual base cannot be default initialized.

Also provide a (disabled by default, for now) warning for the case where a
virtual base class's initializer is ignored in an abstract class's constructor,
and address a defect in DR257 where it was not carried through to C++11's rules
for implicit deletion of special member functions.

Based on a patch by Maurice Bos.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@186803 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/Diagnostic.h
include/clang/Basic/DiagnosticSemaKinds.td
lib/Sema/SemaDeclCXX.cpp
test/SemaCXX/abstract.cpp

index 1354120ea59f29ae9bd42604ee4b87b6c98c09fa..0fcfdf7cb330daff44c2a845ab52489a8042963c 100644 (file)
@@ -490,7 +490,14 @@ public:
       FatalErrorOccurred = true;
     LastDiagLevel = DiagnosticIDs::Ignored;
   }
-  
+
+  /// \brief Determine whether the previous diagnostic was ignored. This can
+  /// be used by clients that want to determine whether notes attached to a
+  /// diagnostic will be suppressed.
+  bool isLastDiagnosticIgnored() const {
+    return LastDiagLevel == DiagnosticIDs::Ignored;
+  }
+
   /// \brief Controls whether otherwise-unmapped extension diagnostics are
   /// mapped onto ignore/warning/error. 
   ///
index 8acdf58dbc91a1fb8ef243e3c591cfbd9830dee7..1ca349920e87f2b2783f04701aed5d28d8459615 100644 (file)
@@ -5613,6 +5613,10 @@ def warn_initializer_out_of_order : Warning<
   "%select{field|base class}0 %1 will be initialized after "
   "%select{field|base}2 %3">,
   InGroup<Reorder>, DefaultIgnore;
+def warn_abstract_vbase_init_ignored : Warning<
+  "initializer for virtual base class %0 of abstract class %1 "
+  "will never be used">,
+  InGroup<DiagGroup<"abstract-vbase-init">>, DefaultIgnore;
 
 def err_base_init_does_not_name_class : Error<
   "constructor initializer %0 does not name a class">;
index 25dbd07b41032096091881f8d21ba0b7dad064ed..9b85c10d594878eb2f9907876b6768b0c4ce012a 100644 (file)
@@ -3346,7 +3346,7 @@ bool Sema::SetCtorInitializers(CXXConstructorDecl *Constructor, bool AnyErrors,
 
   for (unsigned i = 0; i < Initializers.size(); i++) {
     CXXCtorInitializer *Member = Initializers[i];
-    
+
     if (Member->isBaseInitializer())
       Info.AllBaseFields[Member->getBaseClass()->getAs<RecordType>()] = Member;
     else
@@ -3367,12 +3367,28 @@ bool Sema::SetCtorInitializers(CXXConstructorDecl *Constructor, bool AnyErrors,
 
     if (CXXCtorInitializer *Value
         = Info.AllBaseFields.lookup(VBase->getType()->getAs<RecordType>())) {
+      // [class.base.init]p7, per DR257:
+      //   A mem-initializer where the mem-initializer-id names a virtual base
+      //   class is ignored during execution of a constructor of any class that
+      //   is not the most derived class.
+      if (ClassDecl->isAbstract()) {
+        // FIXME: Provide a fixit to remove the base specifier. This requires
+        // tracking the location of the associated comma for a base specifier.
+        Diag(Value->getSourceLocation(), diag::warn_abstract_vbase_init_ignored)
+          << VBase->getType() << ClassDecl;
+        DiagnoseAbstractType(ClassDecl);
+      }
+
       Info.AllToInit.push_back(Value);
-    } else if (!AnyErrors) {
+    } else if (!AnyErrors && !ClassDecl->isAbstract()) {
+      // [class.base.init]p8, per DR257:
+      //   If a given [...] base class is not named by a mem-initializer-id
+      //   [...] and the entity is not a virtual base class of an abstract
+      //   class, then [...] the entity is default-initialized.
       bool IsInheritedVirtualBase = !DirectVBases.count(VBase);
       CXXCtorInitializer *CXXBaseInit;
       if (BuildImplicitBaseInitializer(*this, Constructor, Info.IIK,
-                                       VBase, IsInheritedVirtualBase, 
+                                       VBase, IsInheritedVirtualBase,
                                        CXXBaseInit)) {
         HadError = true;
         continue;
@@ -3913,6 +3929,12 @@ void Sema::DiagnoseAbstractType(const CXXRecordDecl *RD) {
   if (PureVirtualClassDiagSet && PureVirtualClassDiagSet->count(RD))
     return;
 
+  // If the diagnostic is suppressed, don't emit the notes. We're only
+  // going to emit them once, so try to attach them to a diagnostic we're
+  // actually going to show.
+  if (Diags.isLastDiagnosticIgnored())
+    return;
+
   CXXFinalOverriderMap FinalOverriders;
   RD->getFinalOverriders(FinalOverriders);
 
@@ -5055,10 +5077,17 @@ bool Sema::ShouldDeleteSpecialMember(CXXMethodDecl *MD, CXXSpecialMember CSM,
         SMI.shouldDeleteForBase(BI))
       return true;
 
-  for (CXXRecordDecl::base_class_iterator BI = RD->vbases_begin(),
-                                          BE = RD->vbases_end(); BI != BE; ++BI)
-    if (SMI.shouldDeleteForBase(BI))
-      return true;
+  // Defect report (no number yet): do not consider virtual bases of
+  // constructors of abstract classes, since we are not going to construct
+  // them. This is an extension of DR257 into the C++11 behavior for special
+  // members.
+  if (!RD->isAbstract() || !SMI.IsConstructor) {
+    for (CXXRecordDecl::base_class_iterator BI = RD->vbases_begin(),
+                                            BE = RD->vbases_end();
+         BI != BE; ++BI)
+      if (SMI.shouldDeleteForBase(BI))
+        return true;
+  }
 
   for (CXXRecordDecl::field_iterator FI = RD->field_begin(),
                                      FE = RD->field_end(); FI != FE; ++FI)
index 1c5b715775d5924de635ccf60d41899975cc6796..d7e2d0a3dcf9a19c06c7be3241bdca89aafad5d7 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11
+// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++11 -Wabstract-vbase-init
 
 #ifndef __GXX_EXPERIMENTAL_CXX0X__
 #define __CONCAT(__X, __Y) __CONCAT1(__X, __Y)
@@ -280,3 +280,30 @@ namespace pr12658 {
     foo(C(99)); // expected-error {{allocating an object of abstract class type 'pr12658::C'}}\r
   }
 }
+
+namespace pr16659 {
+  struct A {
+    A(int);
+    virtual void x() = 0; // expected-note {{unimplemented pure virtual method 'x' in 'RedundantInit'}}
+  };
+  struct B : virtual A {};
+  struct C : B {
+    C() : A(37) {}
+    void x() override {}
+  };
+
+  struct X {
+    friend class Z;
+  private:
+    X &operator=(const X&);
+  };
+  struct Y : virtual X { // expected-note {{::X' has an inaccessible copy assignment}}
+    virtual ~Y() = 0;
+  };
+  struct Z : Y {}; // expected-note {{::Y' has a deleted copy assignment}}
+  void f(Z &a, const Z &b) { a = b; } // expected-error {{copy assignment operator is implicitly deleted}}
+
+  struct RedundantInit : virtual A {
+    RedundantInit() : A(0) {} // expected-warning {{initializer for virtual base class 'pr16659::A' of abstract class 'RedundantInit' will never be used}}
+  };
+}