<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Sun, Apr 17, 2016 at 10:32 AM, Faisal Vali via cfe-commits <span dir="ltr"><<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: faisalv<br>
Date: Sun Apr 17 12:32:04 2016<br>
New Revision: 266561<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=266561&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=266561&view=rev</a><br>
Log:<br>
Implement CWG 941 - explicit specializations of deleted function templates<br>
<br>
  template<class T> void f(T) = delete;<br>
  template<> void f(int); // OK.<br>
<br>
  f(3); // OK<br>
<br>
Implementation strategy:<br>
<br>
When an explicit specialization of a function template, a member function template or a member function of a class template is declared, clang first implicitly instantiates the declaration of a specialization from the templated-entity being explicitly specialized (since their signatures must be the same) and then links the explicit specialization being declared as a redeclaration of the aforementioned specialization.<br>
<br>
The problem was that when clang 'implicitly instantiates' the initial specialization, it marks the corresponding FunctionDecl as deleted if the corresponding templated-entity was deleted, rather than waiting to see whether the explicit specialization being declared provides a non-deleted body. (The eager marking of delete has advantages during overload resolution I suppose, where we don't have to try and instantiate a definition of the function to see if it is deleted).<br>
<br>
The present fix entails recognizing that when clang knows that an explicit specialization is being declared (for whichever templated-entity), the prior implicit instantiation should not inherit the 'deleted' status, and so we reset it to false.<br>
<br>
I suppose an alternative fix (amongst others) could consider creating a new context (ExplicitSpecializationDeclarationSubstitution or some such) that is checked during template-argument-deduction and final substitution, and avoid inheriting the deleted status during declaration substitution.  But while conceptually cleaner, that would be a slightly more involved change (as could be some of the other alternatives: such as avoid tagging implicit specializations as deleted, and check their primary templates for the deleted status where needed), and so I chose a different path.  Hopefully it'll prove to not be a bad choice.<br>
<br>
<br>
Added:<br>
    cfe/trunk/test/SemaCXX/delete-and-function-templates.cpp<br>
Modified:<br>
    cfe/trunk/lib/Sema/SemaDecl.cpp<br>
    cfe/trunk/lib/Sema/SemaTemplate.cpp<br>
<br>
Modified: cfe/trunk/lib/Sema/SemaDecl.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=266561&r1=266560&r2=266561&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=266561&r1=266560&r2=266561&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)<br>
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Sun Apr 17 12:32:04 2016<br>
@@ -8630,6 +8630,14 @@ bool Sema::CheckFunctionDeclaration(Scop<br>
           NewTemplateDecl->getInstantiatedFromMemberTemplate()) {<br>
         NewTemplateDecl->setMemberSpecialization();<br>
         assert(OldTemplateDecl->isMemberSpecialization());<br>
+        // Explicit specializations of a member template do not inherit deleted<br>
+        // status from the parent member template that they are specializing.<br>
+        if (OldTemplateDecl->getTemplatedDecl()->isDeleted()) {<br>
+          FunctionDecl *const OldTemplatedDecl =<br>
+              OldTemplateDecl->getTemplatedDecl();<br>
+          assert(OldTemplatedDecl->getCanonicalDecl() == OldTemplatedDecl);<br>
+          OldTemplatedDecl->setDeletedAsWritten(false);<br>
+        }<br>
       }<br>
<br>
     } else {<br>
<br>
Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=266561&r1=266560&r2=266561&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=266561&r1=266560&r2=266561&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)<br>
+++ cfe/trunk/lib/Sema/SemaTemplate.cpp Sun Apr 17 12:32:04 2016<br>
@@ -6978,6 +6978,13 @@ bool Sema::CheckFunctionTemplateSpeciali<br>
   // Mark the prior declaration as an explicit specialization, so that later<br>
   // clients know that this is an explicit specialization.<br>
   if (!isFriend) {<br>
+    // Explicit specializations do not inherit '=delete' from their primary<br>
+    // function template.<br>
+    if (Specialization->isDeleted()) {<br>
+      assert(!SpecInfo->isExplicitSpecialization());<br>
+      assert(Specialization->getCanonicalDecl() == Specialization);<br>
+      Specialization->setDeletedAsWritten(false);<br>
+    }<br>
     SpecInfo->setTemplateSpecializationKind(TSK_ExplicitSpecialization);<br>
     MarkUnusedFileScopedDecl(Specialization);<br>
   }<br>
@@ -7137,6 +7144,13 @@ Sema::CheckMemberSpecialization(NamedDec<br>
       InstantiationFunction->setTemplateSpecializationKind(<br>
                                                   TSK_ExplicitSpecialization);<br>
       InstantiationFunction->setLocation(Member->getLocation());<br>
+      // Explicit specializations of member functions of class templates do not<br>
+      // inherit '=delete' from the member function they are specializing.<br>
+      if (InstantiationFunction->isDeleted()) {<br>
+        assert(InstantiationFunction->getCanonicalDecl() ==<br>
+               InstantiationFunction);<br>
+        InstantiationFunction->setDeletedAsWritten(false);<br>
+      }<br>
     }<br>
<br>
     cast<FunctionDecl>(Member)->setInstantiationOfMemberFunction(<br>
<br>
Added: cfe/trunk/test/SemaCXX/delete-and-function-templates.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/delete-and-function-templates.cpp?rev=266561&view=auto" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/delete-and-function-templates.cpp?rev=266561&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/SemaCXX/delete-and-function-templates.cpp (added)<br>
+++ cfe/trunk/test/SemaCXX/delete-and-function-templates.cpp Sun Apr 17 12:32:04 2016<br>
@@ -0,0 +1,115 @@<br>
+// RUN: %clang_cc1 -std=c++11 -verify -fsyntax-only  -emit-llvm-only %s<br>
+// RUN: %clang_cc1 -std=c++11 -verify -fsyntax-only  -fdelayed-template-parsing %s<br>
+// RUN: %clang_cc1 -std=c++11 -verify -fsyntax-only  -fms-extensions %s<br>
+// RUN: %clang_cc1 -std=c++11 -verify -fsyntax-only  -fdelayed-template-parsing -fms-extensions %s<br>
+<br>
+template<class T, class U> struct is_same { enum { value = false }; };<br>
+template<class T> struct is_same<T, T> { enum { value = true }; };<br>
+<br>
+namespace test_sfinae_and_delete {<br>
+<br>
+namespace ns1 {<br>
+template<class T> double f(T) = delete; //expected-note{{candidate}}<br>
+char f(...); //expected-note{{candidate}}<br>
+<br>
+static_assert(is_same<decltype(f(3)),char>::value, ""); //expected-error{{call to deleted function}} expected-error{{static_assert failed}}<br>
+<br>
+template<class T> decltype(f(T{})) g(T); // this one sfinae's out.<br>
+template<class T> int *g(T);<br>
+void foo() {<br>
+  int *ip = g(3);<br>
+}<br>
+} //end ns1<br>
+<br>
+namespace ns2 {<br>
+template<class T> double* f(T);<br>
+template<> double* f(double) = delete;<br>
+<br>
+template<class T> decltype(f(T{})) g(T); // expected-note{{candidate}}<br>
+template<class T> int *g(T); //expected-note{{candidate}}<br>
+void foo() {<br>
+  double *dp = g(3); //expected-error{{ambiguous}}<br>
+  int *ip = g(3.14); // this is OK - because the explicit specialization is deleted and sfinae's out one of the template candidates<br>
+}<br>
+<br>
+} // end ns2<br>
+<br>
+namespace ns3 {<br>
+template<class T> double* f(T) = delete;<br>
+template<> double* f(double);<br>
+<br>
+template<class T> decltype(f(T{})) g(T); // expected-note{{candidate}}<br>
+template<class T> int *g(T); //expected-note{{candidate}}<br>
+<br>
+void foo() {<br>
+  int *dp = g(3); // this is OK - because the non-double specializations are deleted and sfinae's out one of the template candidates<br>
+  double *ip = g(3.14); //expected-error{{ambiguous}}<br>
+}<br>
+<br>
+} // end ns3<br>
+} // end ns test_sfinae_and_delete<br>
+<br>
+namespace test_explicit_specialization_of_member {<br>
+namespace ns1 {<br>
+template<class T> struct X {<br>
+  int* f(T) = delete;<br>
+};<br>
+template<> int* X<int>::f(int) { }<br></blockquote><div><br></div><div>I think this should be ill-formed; see my message to the core reflector. We should only strip the '= delete' from a declaration we (incorrectly) instantiate to check the explicit specialization. We shouldn't strip it from the declaration we instantiate as part of instantiating X<int> in this case.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+template<class T> decltype(X<T>{}.f(T{})) g(T); // expected-note{{candidate}}<br>
+template<class T> int *g(T); //expected-note{{candidate}}<br>
+<br>
+void foo() {<br>
+  int *ip2 = g(3.14); // this is OK - because the non-int specializations are deleted and sfinae's out one of the template candidates<br>
+  int *ip = g(3); //expected-error{{ambiguous}}<br>
+}<br>
+<br>
+} // end ns1<br>
+<br>
+namespace ns2 {<br>
+struct X {<br>
+template<class T> double* f(T) = delete;<br>
+};<br>
+template<> double* X::f(int);<br>
+<br>
+template<class T> decltype(X{}.f(T{})) g(T); // expected-note{{candidate}}<br>
+template<class T> int *g(T); //expected-note{{candidate}}<br>
+<br>
+void foo() {<br>
+  int *ip2 = g(3.14); // this is OK - because the non-int specializations are deleted and sfinae's out one of the template candidates<br>
+  int *ip = g(3); //expected-error{{ambiguous}}<br>
+}<br>
+<br>
+} // end ns2<br>
+<br>
+namespace ns3 {<br>
+template<class T> struct X {<br>
+  template<class U> double *f1(U, T) = delete;<br>
+  template<class U> double *f2(U, T) = delete;<br>
+};<br>
+template<> template<> double* X<int>::f1(int, int);<br>
+template<> template<class U> double* X<int>::f2(U, int);<br>
+<br>
+template<class T, class U> decltype(X<T>{}.f1(U{}, T{})) g1(U, T); // expected-note{{candidate}}<br>
+template<class T, class U> int *g1(U, T); //expected-note{{candidate}}<br>
+<br>
+template<class T, class U> decltype(X<T>{}.f2(U{}, T{})) g2(U, T); // expected-note2{{candidate}}<br>
+template<class T, class U> int *g2(U, T); //expected-note2{{candidate}}<br>
+<br>
+<br>
+void foo() {<br>
+  int *ip2 = g1(3.14, 3); // this is OK - because the non-int specializations are deleted and sfinae's out one of the template candidates<br>
+  int *ip = g1(3, 3); //expected-error{{ambiguous}}<br>
+  {<br>
+   int *ip3 = g2(3.14, 3); //expected-error{{ambiguous}}<br>
+   int *ip4 = g2(3, 3); //expected-error{{ambiguous}}<br>
+  }<br>
+  {<br>
+   int *ip3 = g2(3.14, 3.14);<br>
+   int *ip4 = g2(3, 3.14);<br>
+  }<br>
+}<br>
+<br>
+<br>
+} // end ns3<br>
+} // end test_explicit_specializations_and_delete<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org">cfe-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote></div><br></div></div>