]> granicus.if.org Git - clang/commitdiff
[cxx2a] P0641R2: (Some) type mismatches on defaulted functions only
authorRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 28 Sep 2018 01:16:43 +0000 (01:16 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 28 Sep 2018 01:16:43 +0000 (01:16 +0000)
render the function deleted instead of rendering the program ill-formed.

This change also adds an enabled-by-default warning for the case where
an explicitly-defaulted special member function of a non-template class
is implicitly deleted by the type checking rules. (This fires either due
to this language change or due to pre-C++20 reasons for the member being
implicitly deleted). I've tested this on a large codebase and found only
bugs (where the program means something that's clearly different from
what the programmer intended), so this is enabled by default, but we
should revisit this if there are problems with this being enabled by
default.

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

16 files changed:
include/clang/Basic/DiagnosticSemaKinds.td
lib/Sema/SemaDeclCXX.cpp
test/CXX/class.derived/class.abstract/p16.cpp
test/CXX/dcl.decl/dcl.fct.def/dcl.fct.def.default/p1.cpp
test/CXX/drs/dr6xx.cpp
test/CXX/special/class.copy/p12-0x.cpp
test/CXX/special/class.copy/p23-cxx11.cpp
test/CXX/special/class.ctor/p5-0x.cpp
test/CXX/special/class.dtor/p5-0x.cpp
test/SemaCUDA/implicit-member-target.cu
test/SemaCXX/cxx0x-deleted-default-ctor.cpp
test/SemaCXX/cxx17-compat.cpp
test/SemaCXX/dr1301.cpp
test/SemaCXX/microsoft-dtor-lookup-cxx11.cpp
test/SemaTemplate/exception-spec-crash.cpp
www/cxx_status.html

index 508a0c41af7cc47963441c7b2cb61ce93f59beec..7c2f3d1833de7fc3506cecfbf08906048b6b0212 100644 (file)
@@ -7760,9 +7760,19 @@ def err_incorrect_defaulted_exception_spec : Error<
 def err_incorrect_defaulted_constexpr : Error<
   "defaulted definition of %sub{select_special_member_kind}0 "
   "is not constexpr">;
+def warn_defaulted_method_deleted : Warning<
+  "explicitly defaulted %sub{select_special_member_kind}0 is implicitly "
+  "deleted">, InGroup<DiagGroup<"defaulted-function-deleted">>;
 def err_out_of_line_default_deletes : Error<
   "defaulting this %sub{select_special_member_kind}0 "
   "would delete it after its first declaration">;
+def note_deleted_type_mismatch : Note<
+  "function is implicitly deleted because its declared type does not match "
+  "the type of an implicit %sub{select_special_member_kind}0">;
+def warn_cxx17_compat_defaulted_method_type_mismatch : Warning<
+  "explicitly defaulting this %sub{select_special_member_kind}0 with a type "
+  "different from the implicit type is incompatible with C++ standards before "
+  "C++2a">, InGroup<CXXPre2aCompat>, DefaultIgnore;
 def warn_vbase_moved_multiple_times : Warning<
   "defaulted move assignment operator of %0 will move assign virtual base "
   "class %1 multiple times">, InGroup<DiagGroup<"multiple-move-vbase">>;
index a18b8dd59676f824f55d2cbe94e3cab91842e343..3261a7031d5853ac6da7b1afc4f5723ed8d1f8d9 100644 (file)
@@ -6451,20 +6451,29 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
   //        copy operation can take a non-const reference) as an implicit
   //        declaration, and
   //     -- not have default arguments.
+  // C++2a changes the second bullet to instead delete the function if it's
+  // defaulted on its first declaration, unless it's "an assignment operator,
+  // and its return type differs or its parameter type is not a reference".
+  bool DeleteOnTypeMismatch = getLangOpts().CPlusPlus2a && First;
+  bool ShouldDeleteForTypeMismatch = false;
   unsigned ExpectedParams = 1;
   if (CSM == CXXDefaultConstructor || CSM == CXXDestructor)
     ExpectedParams = 0;
   if (MD->getNumParams() != ExpectedParams) {
-    // This also checks for default arguments: a copy or move constructor with a
+    // This checks for default arguments: a copy or move constructor with a
     // default argument is classified as a default constructor, and assignment
     // operations and destructors can't have default arguments.
     Diag(MD->getLocation(), diag::err_defaulted_special_member_params)
       << CSM << MD->getSourceRange();
     HadError = true;
   } else if (MD->isVariadic()) {
-    Diag(MD->getLocation(), diag::err_defaulted_special_member_variadic)
-      << CSM << MD->getSourceRange();
-    HadError = true;
+    if (DeleteOnTypeMismatch)
+      ShouldDeleteForTypeMismatch = true;
+    else {
+      Diag(MD->getLocation(), diag::err_defaulted_special_member_variadic)
+        << CSM << MD->getSourceRange();
+      HadError = true;
+    }
   }
 
   const FunctionProtoType *Type = MD->getType()->getAs<FunctionProtoType>();
@@ -6489,9 +6498,13 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
 
     // A defaulted special member cannot have cv-qualifiers.
     if (Type->getTypeQuals()) {
-      Diag(MD->getLocation(), diag::err_defaulted_special_member_quals)
-        << (CSM == CXXMoveAssignment) << getLangOpts().CPlusPlus14;
-      HadError = true;
+      if (DeleteOnTypeMismatch)
+        ShouldDeleteForTypeMismatch = true;
+      else {
+        Diag(MD->getLocation(), diag::err_defaulted_special_member_quals)
+          << (CSM == CXXMoveAssignment) << getLangOpts().CPlusPlus14;
+        HadError = true;
+      }
     }
   }
 
@@ -6504,23 +6517,30 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
     HasConstParam = ReferentType.isConstQualified();
 
     if (ReferentType.isVolatileQualified()) {
-      Diag(MD->getLocation(),
-           diag::err_defaulted_special_member_volatile_param) << CSM;
-      HadError = true;
+      if (DeleteOnTypeMismatch)
+        ShouldDeleteForTypeMismatch = true;
+      else {
+        Diag(MD->getLocation(),
+             diag::err_defaulted_special_member_volatile_param) << CSM;
+        HadError = true;
+      }
     }
 
     if (HasConstParam && !CanHaveConstParam) {
-      if (CSM == CXXCopyConstructor || CSM == CXXCopyAssignment) {
+      if (DeleteOnTypeMismatch)
+        ShouldDeleteForTypeMismatch = true;
+      else if (CSM == CXXCopyConstructor || CSM == CXXCopyAssignment) {
         Diag(MD->getLocation(),
              diag::err_defaulted_special_member_copy_const_param)
           << (CSM == CXXCopyAssignment);
         // FIXME: Explain why this special member can't be const.
+        HadError = true;
       } else {
         Diag(MD->getLocation(),
              diag::err_defaulted_special_member_move_const_param)
           << (CSM == CXXMoveAssignment);
+        HadError = true;
       }
-      HadError = true;
     }
   } else if (ExpectedParams) {
     // A copy assignment operator can take its argument by value, but a
@@ -6582,14 +6602,27 @@ void Sema::CheckExplicitlyDefaultedSpecialMember(CXXMethodDecl *MD) {
                                         EPI));
   }
 
-  if (ShouldDeleteSpecialMember(MD, CSM)) {
+  if (ShouldDeleteForTypeMismatch || ShouldDeleteSpecialMember(MD, CSM)) {
     if (First) {
       SetDeclDeleted(MD, MD->getLocation());
+      if (!inTemplateInstantiation() && !HadError) {
+        Diag(MD->getLocation(), diag::warn_defaulted_method_deleted) << CSM;
+        if (ShouldDeleteForTypeMismatch) {
+          Diag(MD->getLocation(), diag::note_deleted_type_mismatch) << CSM;
+        } else {
+          ShouldDeleteSpecialMember(MD, CSM, nullptr, /*Diagnose*/true);
+        }
+      }
+      if (ShouldDeleteForTypeMismatch && !HadError) {
+        Diag(MD->getLocation(),
+             diag::warn_cxx17_compat_defaulted_method_type_mismatch) << CSM;
+      }
     } else {
       // C++11 [dcl.fct.def.default]p4:
       //   [For a] user-provided explicitly-defaulted function [...] if such a
       //   function is implicitly defined as deleted, the program is ill-formed.
       Diag(MD->getLocation(), diag::err_out_of_line_default_deletes) << CSM;
+      assert(!ShouldDeleteForTypeMismatch && "deleted non-first decl");
       ShouldDeleteSpecialMember(MD, CSM, nullptr, /*Diagnose*/true);
       HadError = true;
     }
index 80396a96d9ec411a451061d53ed9483f20060f37..fe31b7321bd0f2970923761bb40c70303190038a 100644 (file)
@@ -44,8 +44,8 @@ struct G : D {};
 // expected-error@-3 {{deleted function 'operator=' cannot override a non-deleted function}}
 // expected-note@-4 {{while declaring the implicit move assignment operator for 'G'}}
 // expected-note@-5 {{move assignment operator of 'G' is implicitly deleted because base class 'D' has an inaccessible move assignment operator}}
-struct H : D {
-  H &operator=(H&&) = default;
+struct H : D { // expected-note {{deleted because base class 'D' has an inaccessible move assignment}}
+  H &operator=(H&&) = default; // expected-warning {{implicitly deleted}}
   // expected-error@-1 {{deleted function 'operator=' cannot override a non-deleted function}}
   // expected-note@-3 {{move assignment operator of 'H' is implicitly deleted because base class 'D' has an inaccessible move assignment operator}}
   ~H();
index 51993307cfff4dd31f51c5a2a410fdb11a9dd1d6..3f2bc569edf62fd7d8304f6961516af86baf3d4b 100644 (file)
@@ -1,15 +1,30 @@
 // RUN: %clang_cc1 -verify %s -std=c++11
+// RUN: %clang_cc1 -verify %s -std=c++17
+// RUN: %clang_cc1 -verify %s -std=c++2a
 
 // A function that is explicitly defaulted shall
 struct A {
   // -- be a special member function,
   A(int) = default; // expected-error {{only special member functions may be defaulted}}
+  A(A) = default; // expected-error {{must pass its first argument by reference}}
 
   // -- have the same declared function type as if it had been implicitly
   //    declared
   void operator=(const A &) = default; // expected-error {{must return 'A &'}}
-  A(...) = default; // expected-error {{cannot be variadic}}
-  A(const A &, ...) = default; // expected-error {{cannot be variadic}}
+  A(...) = default;
+  A(const A &, ...) = default;
+  A &operator=(const A&) const = default;
+  A &operator=(A) const = default; // expected-error {{must be an lvalue refe}}
+#if __cplusplus <= 201703L
+  // expected-error@-5 {{cannot be variadic}}
+  // expected-error@-5 {{cannot be variadic}}
+  // expected-error@-5 {{may not have 'const'}}
+  // expected-error@-5 {{may not have 'const'}}
+#else
+  // expected-warning@-10 {{implicitly deleted}} expected-note@-10 {{declared type does not match the type of an implicit default constructor}}
+  // expected-warning@-10 {{implicitly deleted}} expected-note@-10 {{declared type does not match the type of an implicit copy constructor}}
+  // expected-warning@-10 {{implicitly deleted}} expected-note@-10 {{declared type does not match the type of an implicit copy assignment}}
+#endif
 
   //    (except for possibly differing ref-qualifiers
   A &operator=(A &&) & = default;
@@ -23,3 +38,35 @@ struct A {
   A(double = 0.0) = default; // expected-error {{cannot have default arguments}}
   A(const A & = 0) = default; // expected-error {{cannot have default arguments}}
 };
+
+struct A2 {
+  A2(...);
+  A2(const A2 &, ...);
+  A2 &operator=(const A2&) const;
+};
+A2::A2(...) = default; // expected-error {{cannot be variadic}}
+A2::A2(const A2&, ...) = default; // expected-error {{cannot be variadic}}
+A2 &A2::operator=(const A2&) const = default; // expected-error {{may not have 'const'}}
+
+struct B {
+  B(B&);
+  B &operator=(B&);
+};
+struct C : B {
+  C(const C&) = default;
+  C &operator=(const C&) = default;
+#if __cplusplus <= 201703L
+  // expected-error@-3 {{is const, but a member or base requires it to be non-const}}
+  // expected-error@-3 {{is const, but a member or base requires it to be non-const}}
+#else
+  // expected-warning@-6 {{implicitly deleted}} expected-note@-6 {{type does not match}}
+  // expected-warning@-6 {{implicitly deleted}} expected-note@-6 {{type does not match}}
+#endif
+};
+
+struct D : B { // expected-note 2{{base class}}
+  D(const D&);
+  D &operator=(const D&);
+};
+D::D(const D&) = default; // expected-error {{would delete}} expected-error {{is const, but}}
+D &D::operator=(const D&) = default; // expected-error {{would delete}} expected-error {{is const, but}}
index c3c867b7a54d2f5b0ffb4864f14fba7a589148e0..0f072268ab57b4eed1b468e61a6ece9223f3b234 100644 (file)
@@ -757,8 +757,8 @@ namespace dr666 { // dr666: yes
 #if __cplusplus >= 201103L
 namespace dr667 { // dr667: yes
   struct A {
-    A() = default;
-    int &r;
+    A() = default; // expected-warning {{explicitly defaulted default constructor is implicitly deleted}}
+    int &r; // expected-note {{because field 'r' of reference type 'int &' would not be initialized}}
   };
   static_assert(!__is_trivially_constructible(A), "");
 
index 1b23b5a4b179ce1922d195a0d4b31c467d9dd4f9..a0ef49d9b64db6299c4f4bfb320e7f215deed14d 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++11 -verify %s
+// RUN: %clang_cc1 -std=c++11 -verify %s -Wno-defaulted-function-deleted
 
 // expected-no-diagnostics
 
index ac21cc61a2c556ba8ee06d0e4c75645871eca007..0b9652b50fcfb0942828fc96782a37d3ee61fd83 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -verify %s -std=c++11
+// RUN: %clang_cc1 -verify %s -std=c++11 -Wno-defaulted-function-deleted
 
 struct Trivial {};
 
index 061a3d1f0738a24316aca6d7d756f56bce080aaf..5fa61008e8b774a82b00f2af6f75d7fb833fa68f 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 -Wno-defaulted-function-deleted
 
 struct DefaultedDefCtor1 {};
 struct DefaultedDefCtor2 { DefaultedDefCtor2() = default; };
index 595784f0d5f9d369bcf01b3a6d5eca04ff16a127..f69baca53bf339a43aad645756e5fa9abd46c05f 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -verify -std=c++11 %s
+// RUN: %clang_cc1 -verify -std=c++11 %s -Wno-defaulted-function-deleted
 
 struct NonTrivDtor {
   ~NonTrivDtor();
index 242d345fb93ec41807fddb855fb4fc45b5b62c37..d87e69624043419c4f530abbc47fbd10719059d2 100644 (file)
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=gnu++11 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=gnu++11 -fsyntax-only -verify %s -Wno-defaulted-function-deleted
 
 #include "Inputs/cuda.h"
 
index b9af67948bfc9e457a71b02ec690394ca4f49261..80efd810f09842b17ea87a383576e8960604cab2 100644 (file)
@@ -59,7 +59,7 @@ struct good_const {
 good_const gc;
 
 struct no_default {
-  no_default() = delete; // expected-note 4{{deleted here}}
+  no_default() = delete; // expected-note 5{{deleted here}}
 };
 struct no_dtor {
   ~no_dtor() = delete; // expected-note 2{{deleted here}}
@@ -108,8 +108,8 @@ struct has_friend {
 has_friend hf;
 
 struct defaulted_delete {
-  no_default nd; // expected-note {{because field 'nd' has a deleted default constructor}}
-  defaulted_delete() = default; // expected-note{{implicitly deleted here}}
+  no_default nd; // expected-note 2{{because field 'nd' has a deleted default constructor}}
+  defaulted_delete() = default; // expected-note{{implicitly deleted here}} expected-warning {{implicitly deleted}}
 };
 defaulted_delete dd; // expected-error {{call to implicitly-deleted default constructor}}
 
index 9d628da662b8b6827fadfa092e02b0341055c7f8..93e052fe7e73b86bf9030a333c3f634ed8ace318 100644 (file)
@@ -1,5 +1,5 @@
 // RUN: %clang_cc1 -fsyntax-only -std=c++17 -pedantic -verify %s
-// RUN: %clang_cc1 -fsyntax-only -std=c++2a -Wc++17-compat-pedantic -verify %s
+// RUN: %clang_cc1 -fsyntax-only -std=c++2a -Wc++17-compat-pedantic -verify %s -Wno-defaulted-function-deleted
 
 struct A {};
 int (A::*pa)() const&;
@@ -42,3 +42,15 @@ void copy_lambda() { Lambda = Lambda; }
 #else
     // expected-warning@-4 {{assignment of lambda is incompatible with C++ standards before C++2a}}
 #endif
+
+struct DefaultDeleteWrongTypeBase {
+  DefaultDeleteWrongTypeBase(DefaultDeleteWrongTypeBase&);
+};
+struct DefaultDeleteWrongType : DefaultDeleteWrongTypeBase {
+  DefaultDeleteWrongType(const DefaultDeleteWrongType&) = default;
+#if __cplusplus <= 201703L
+    // expected-error@-2 {{a member or base requires it to be non-const}}
+#else
+    // expected-warning@-4 {{explicitly defaulting this copy constructor with a type different from the implicit type is incompatible with C++ standards before C++2a}}
+#endif
+};
index ec0db74554a870fd4d6ff863e35ae741feb88b84..b7dc91d563300058a74db3b5038dd825a5717163 100644 (file)
@@ -6,7 +6,7 @@ struct A { // expected-note 2{{candidate}}
 int a = A().n; // expected-error {{no matching constructor}}
 
 struct B {
-  B() = delete; // expected-note 3{{here}}
+  B() = delete; // expected-note 4{{here}}
   int n;
 };
 int b = B().n; // expected-error {{call to deleted}}
@@ -17,8 +17,8 @@ struct C {
 int c = C().b.n; // expected-error {{call to implicitly-deleted default}}
 
 struct D {
-  D() = default; // expected-note {{here}}
-  B b; // expected-note {{'b' has a deleted default constructor}}
+  D() = default; // expected-note {{here}} expected-warning {{implicitly deleted}}
+  B b; // expected-note 2{{'b' has a deleted default constructor}}
 };
 int d = D().b.n; // expected-error {{call to implicitly-deleted default}}
 
index 5a399aa7eac22e029cbbc270d0f7e9f4a5410266..000492e0edf195247ca3e2b3fb5282f1939018ab 100644 (file)
@@ -6,8 +6,8 @@ struct S {
   void operator delete(void*, double);
 } s; // expected-error {{attempt to use a deleted function}}
 
-struct T { // expected-note{{virtual destructor requires an unambiguous, accessible 'operator delete'}}
-  virtual ~T() = default; // expected-note {{explicitly defaulted function was implicitly deleted here}}
+struct T { // expected-note 2{{virtual destructor requires an unambiguous, accessible 'operator delete'}}
+  virtual ~T() = default; // expected-note {{explicitly defaulted function was implicitly deleted here}} expected-warning {{implicitly deleted}}
   void operator delete(void*, int);
   void operator delete(void*, double);
 } t; // expected-error {{attempt to use a deleted function}}
index 4d9355974c9e6e0919db3c833b0c032eafbe4968..ebbb30a2c23b6ce59cec2056a6b4ae32690d4ac2 100644 (file)
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
-// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -DCXX_EXCEPTIONS -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s -Wno-defaulted-function-deleted
+// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -DCXX_EXCEPTIONS -fsyntax-only -verify %s -Wno-defaulted-function-deleted
 
 template <class _Tp> struct is_nothrow_move_constructible {
   static const bool value = false;
index aed443c4f6c66ebfae0ab7c8817083c3e5a529f9..b7dbb2a36c847ccba0d8403709b46c6ebe3ea739 100755 (executable)
@@ -884,7 +884,7 @@ as the draft C++2a standard evolves.
     <tr>
       <td><tt>const</tt> mismatch with defaulted copy constructor</td>
       <td><a href="http://wg21.link/p0641r2">P0641R2</a></td>
-      <td class="none" align="center">No</td>
+      <td class="svn" align="center">SVN</td>
     </tr>
     <tr>
       <td rowspan="3">Consistent comparison (<tt>operator&lt;=&gt;</tt>)</td>