royjacobson updated this revision to Diff 431333.
royjacobson added a comment.

Fix typo


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D126194/new/

https://reviews.llvm.org/D126194

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Sema/SemaTemplateInstantiate.cpp
  clang/test/CXX/class/class.dtor/p4.cpp
  clang/test/CXX/over/over.match/over.match.viable/p3.cpp

Index: clang/test/CXX/over/over.match/over.match.viable/p3.cpp
===================================================================
--- clang/test/CXX/over/over.match/over.match.viable/p3.cpp
+++ clang/test/CXX/over/over.match/over.match.viable/p3.cpp
@@ -49,7 +49,6 @@
   S(A) requires false;
   S(double) requires true;
   ~S() requires false;
-  // expected-note@-1 2{{because 'false' evaluated to false}}
   ~S() requires true;
   operator int() requires true;
   operator int() requires false;
@@ -58,11 +57,7 @@
 void bar() {
   WrapsStatics<int>::foo(A{});
   S<int>{1.}.foo(A{});
-  // expected-error@-1{{invalid reference to function '~S': constraints not satisfied}}
-  // Note - this behavior w.r.t. constrained dtors is a consequence of current
-  // wording, which does not invoke overload resolution when a dtor is called.
-  // P0848 is set to address this issue.
+
   S<int> s = 1;
-  // expected-error@-1{{invalid reference to function '~S': constraints not satisfied}}
   int a = s;
 }
Index: clang/test/CXX/class/class.dtor/p4.cpp
===================================================================
--- /dev/null
+++ clang/test/CXX/class/class.dtor/p4.cpp
@@ -0,0 +1,74 @@
+// RUN: %clang_cc1 -std=c++20 -verify %s
+
+template <int N>
+struct A {
+  ~A() = delete; // expected-note {{explicitly marked deleted}}
+  ~A() requires(N == 1) = delete; // expected-note {{explicitly marked deleted}}
+};
+
+template <int N>
+struct B {
+  ~B() requires(N == 1) = delete; // expected-note {{explicitly marked deleted}}
+  virtual ~B() = delete; // expected-note {{explicitly marked deleted}}
+};
+
+template<int N>
+concept CO1 = N == 1;
+
+template<int N>
+concept CO2 = N > 0;
+
+template <int N>
+struct C {
+  ~C() = delete; // expected-note {{explicitly marked deleted}}
+  ~C() requires(CO1<N>) = delete;
+  ~C() requires(CO1<N> && CO2<N>) = delete; // expected-note {{explicitly marked deleted}}
+};
+
+template <int N>
+struct D {
+  ~D() requires(N != 0) = delete; // expected-note {{explicitly marked deleted}}
+  // expected-note@-1 {{candidate function has been explicitly deleted}}
+  // expected-note@-2 {{candidate function not viable: constraints not satisfied}}
+  // expected-note@-3 {{evaluated to false}}
+  ~D() requires(N == 1) = delete;
+  // expected-note@-1 {{candidate function has been explicitly deleted}}
+  // expected-note@-2 {{candidate function not viable: constraints not satisfied}}
+  // expected-note@-3 {{evaluated to false}}
+};
+
+template <class T>
+concept Foo = requires (T t) {
+  { t.foo() };
+};
+
+template<int N>
+struct E {
+  void foo();
+  ~E();
+  ~E() requires Foo<E> = delete;  // expected-note {{explicitly marked deleted}}
+};
+
+template struct A<1>;
+template struct A<2>;
+template struct B<1>;
+template struct B<2>;
+template struct C<1>;
+template struct C<2>;
+template struct D<0>;  // expected-error {{no viable destructor found for class 'D<0>'}} expected-note {{in instantiation of template}}
+template struct D<1>;  // expected-error {{destructor of class 'D<1>' is ambiguous}} expected-note {{in instantiation of template}}
+template struct D<2>;
+template struct E<1>;
+
+int main() {
+  A<1> a1; // expected-error {{attempt to use a deleted function}}
+  A<2> a2; // expected-error {{attempt to use a deleted function}}
+  B<1> b1; // expected-error {{attempt to use a deleted function}}
+  B<2> b2; // expected-error {{attempt to use a deleted function}}
+  C<1> c1; // expected-error {{attempt to use a deleted function}}
+  C<2> c2; // expected-error {{attempt to use a deleted function}}
+  D<0> d0;
+  D<1> d1;
+  D<2> d2; // expected-error {{attempt to use a deleted function}}
+  E<1> e1; // expected-error {{attempt to use a deleted function}}
+}
Index: clang/lib/Sema/SemaTemplateInstantiate.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -2320,6 +2320,81 @@
     }
   };
 
+  /// [class.dtor]p4:
+  ///   At the end of the definition of a class, overload resolution is
+  ///   performed among the prospective destructors declared in that class with
+  ///   an empty argument list to select the destructor for the class, also
+  ///   known as the selected destructor.
+  ///
+  /// To achieve that, we remove all non-selected destructors from the AST,
+  /// which is a bit unusual. We can't let those declarations be in AST and rely
+  /// on LookupSpecialMember to return the correct declaration because a lot of
+  /// code relies on CXXRecordDecl::getDestructor() which assumes there can only
+  /// be one destructor for a class.
+  /// The reason that we actually have to insert those declarations in the first
+  /// place (instead of keeping them at the side and adding the selected one
+  /// later) is that this will change the order of declarations in the class
+  /// which determines the vtable order, so this would break ABI unless we
+  /// insert the destructor at the original location, at which point it is
+  /// easier to just remove the unselected ones.
+  LLVM_ATTRIBUTE_NOINLINE
+  // NOINLINE to keep the stack size of `InstantiateClass` small.
+  void ComputeSelectedDestructor(Sema &S, CXXRecordDecl *Instantiation) {
+
+    if (!Instantiation->hasUserDeclaredDestructor())
+      return;
+
+    // FIXME: Is this the correct location?
+    SourceLocation Loc = Instantiation->getLocation();
+    OverloadCandidateSet OCS(Loc, OverloadCandidateSet::CSK_Normal);
+
+    SmallVector<CXXDestructorDecl *, 4> ProspectiveDestructors;
+    for (auto *Decl : Instantiation->decls()) {
+      if (auto *DD = dyn_cast<CXXDestructorDecl>(Decl)) {
+        if (DD->isInvalidDecl())
+          continue;
+        ProspectiveDestructors.push_back(DD);
+        S.AddOverloadCandidate(DD, DeclAccessPair::make(DD, DD->getAccess()),
+                               {}, OCS);
+      }
+    }
+    assert(
+        !OCS.empty() &&
+        "No candidates found even though hasUserDeclaredDestructor is true.");
+
+    OverloadCandidateSet::iterator Best;
+    unsigned Msg = 0;
+    OverloadCandidateDisplayKind DisplayKind;
+
+    switch (OCS.BestViableFunction(S, Loc, Best)) {
+    case OR_Success:
+    case OR_Deleted:
+      for (auto *DD : ProspectiveDestructors) {
+        if (DD != Best->Function) {
+          Instantiation->removeDecl(DD);
+          // FIXME: We should maybe `delete DD` here, but it segfaults.
+        }
+      }
+      break;
+
+    case OR_Ambiguous:
+      Msg = diag::err_ambiguous_destructor;
+      DisplayKind = OCD_AmbiguousCandidates;
+      break;
+
+    case OR_No_Viable_Function:
+      Msg = diag::err_no_viable_destructor;
+      DisplayKind = OCD_AllCandidates;
+      break;
+    }
+
+    if (Msg) {
+      PartialDiagnostic Diag = S.PDiag(Msg) << Instantiation;
+      OCS.NoteCandidates(PartialDiagnosticAt(Loc, Diag), S, DisplayKind, {});
+      Instantiation->setInvalidDecl();
+    }
+  }
+
 } // namespace
 
 bool Sema::SubstTypeConstraint(
@@ -2789,6 +2864,9 @@
     }
   }
 
+  if (getLangOpts().CPlusPlus20)
+    ComputeSelectedDestructor(*this, Instantiation);
+
   // Finish checking fields.
   ActOnFields(nullptr, Instantiation->getLocation(), Instantiation, Fields,
               SourceLocation(), SourceLocation(), ParsedAttributesView());
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4715,6 +4715,10 @@
   "reference to non-static member function must be called"
   "%select{|; did you mean to call it with no arguments?}0">;
 def note_possible_target_of_call : Note<"possible target for call">;
+def err_no_viable_destructor : Error<
+  "no viable destructor found for class %0">;
+def err_ambiguous_destructor : Error<
+  "destructor of class %0 is ambiguous">;
 
 def err_ovl_no_viable_object_call : Error<
   "no matching function for call to object of type %0">;
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -400,6 +400,11 @@
   that can be used for such compatibility. The demangler now demangles
   symbols with named module attachment.
 
+- As per "Conditionally Trivial Special Member Functions" (P0848), it is
+  now possible to overload destructors using concepts. Note that the
+  type traits support for trivial and trivially copyable types is not
+  yet implemented.
+
 C++2b Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to