]> granicus.if.org Git - clang/commitdiff
Implement CWG 941 - explicit specializations of deleted function templates
authorFaisal Vali <faisalv@yahoo.com>
Sun, 17 Apr 2016 17:32:04 +0000 (17:32 +0000)
committerFaisal Vali <faisalv@yahoo.com>
Sun, 17 Apr 2016 17:32:04 +0000 (17:32 +0000)
  template<class T> void f(T) = delete;
  template<> void f(int); // OK.

  f(3); // OK

Implementation strategy:

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.

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).

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.

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.

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

lib/Sema/SemaDecl.cpp
lib/Sema/SemaTemplate.cpp
test/SemaCXX/delete-and-function-templates.cpp [new file with mode: 0644]

index b0e79f6f0e79afa38ad26165041065d71ca80865..03f235d4399c20a047f6ac5a88236b7ba66d1873 100644 (file)
@@ -8630,6 +8630,14 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
           NewTemplateDecl->getInstantiatedFromMemberTemplate()) {
         NewTemplateDecl->setMemberSpecialization();
         assert(OldTemplateDecl->isMemberSpecialization());
+        // Explicit specializations of a member template do not inherit deleted
+        // status from the parent member template that they are specializing.
+        if (OldTemplateDecl->getTemplatedDecl()->isDeleted()) {
+          FunctionDecl *const OldTemplatedDecl =
+              OldTemplateDecl->getTemplatedDecl();
+          assert(OldTemplatedDecl->getCanonicalDecl() == OldTemplatedDecl);
+          OldTemplatedDecl->setDeletedAsWritten(false);
+        }
       }
       
     } else {
index d840e426d7a63197b95970bc1c9325bf478d3fc3..3833e2c3f079275891cef426e5665cb0ce3df372 100644 (file)
@@ -6978,6 +6978,13 @@ bool Sema::CheckFunctionTemplateSpecialization(
   // Mark the prior declaration as an explicit specialization, so that later
   // clients know that this is an explicit specialization.
   if (!isFriend) {
+    // Explicit specializations do not inherit '=delete' from their primary\r
+    // function template.\r
+    if (Specialization->isDeleted()) {\r
+      assert(!SpecInfo->isExplicitSpecialization());\r
+      assert(Specialization->getCanonicalDecl() == Specialization);\r
+      Specialization->setDeletedAsWritten(false);\r
+    }
     SpecInfo->setTemplateSpecializationKind(TSK_ExplicitSpecialization);
     MarkUnusedFileScopedDecl(Specialization);
   }
@@ -7137,6 +7144,13 @@ Sema::CheckMemberSpecialization(NamedDecl *Member, LookupResult &Previous) {
       InstantiationFunction->setTemplateSpecializationKind(
                                                   TSK_ExplicitSpecialization);
       InstantiationFunction->setLocation(Member->getLocation());
+      // Explicit specializations of member functions of class templates do not
+      // inherit '=delete' from the member function they are specializing.
+      if (InstantiationFunction->isDeleted()) {
+        assert(InstantiationFunction->getCanonicalDecl() ==
+               InstantiationFunction);
+        InstantiationFunction->setDeletedAsWritten(false);\r
+      }
     }
 
     cast<FunctionDecl>(Member)->setInstantiationOfMemberFunction(
diff --git a/test/SemaCXX/delete-and-function-templates.cpp b/test/SemaCXX/delete-and-function-templates.cpp
new file mode 100644 (file)
index 0000000..418aa80
--- /dev/null
@@ -0,0 +1,115 @@
+// RUN: %clang_cc1 -std=c++11 -verify -fsyntax-only  -emit-llvm-only %s
+// RUN: %clang_cc1 -std=c++11 -verify -fsyntax-only  -fdelayed-template-parsing %s 
+// RUN: %clang_cc1 -std=c++11 -verify -fsyntax-only  -fms-extensions %s 
+// RUN: %clang_cc1 -std=c++11 -verify -fsyntax-only  -fdelayed-template-parsing -fms-extensions %s 
+
+template<class T, class U> struct is_same { enum { value = false }; };
+template<class T> struct is_same<T, T> { enum { value = true }; };
+
+namespace test_sfinae_and_delete {
+
+namespace ns1 {
+template<class T> double f(T) = delete; //expected-note{{candidate}}
+char f(...); //expected-note{{candidate}}
+
+static_assert(is_same<decltype(f(3)),char>::value, ""); //expected-error{{call to deleted function}} expected-error{{static_assert failed}}
+
+template<class T> decltype(f(T{})) g(T); // this one sfinae's out.
+template<class T> int *g(T);
+void foo() {
+  int *ip = g(3);
+}
+} //end ns1
+
+namespace ns2 {
+template<class T> double* f(T);
+template<> double* f(double) = delete;
+
+template<class T> decltype(f(T{})) g(T); // expected-note{{candidate}}
+template<class T> int *g(T); //expected-note{{candidate}}
+void foo() {
+  double *dp = g(3); //expected-error{{ambiguous}}
+  int *ip = g(3.14); // this is OK - because the explicit specialization is deleted and sfinae's out one of the template candidates
+}
+
+} // end ns2
+
+namespace ns3 {
+template<class T> double* f(T) = delete;
+template<> double* f(double);
+
+template<class T> decltype(f(T{})) g(T); // expected-note{{candidate}}
+template<class T> int *g(T); //expected-note{{candidate}}
+
+void foo() {
+  int *dp = g(3); // this is OK - because the non-double specializations are deleted and sfinae's out one of the template candidates
+  double *ip = g(3.14); //expected-error{{ambiguous}}
+}
+
+} // end ns3
+} // end ns test_sfinae_and_delete
+
+namespace test_explicit_specialization_of_member {
+namespace ns1 {
+template<class T> struct X {
+  int* f(T) = delete;
+}; 
+template<> int* X<int>::f(int) { }
+
+template<class T> decltype(X<T>{}.f(T{})) g(T); // expected-note{{candidate}}
+template<class T> int *g(T); //expected-note{{candidate}}
+
+void foo() {
+  int *ip2 = g(3.14); // this is OK - because the non-int specializations are deleted and sfinae's out one of the template candidates
+  int *ip = g(3); //expected-error{{ambiguous}}
+}
+
+} // end ns1
+
+namespace ns2 {
+struct X {
+template<class T> double* f(T) = delete;
+}; 
+template<> double* X::f(int);
+
+template<class T> decltype(X{}.f(T{})) g(T); // expected-note{{candidate}}
+template<class T> int *g(T); //expected-note{{candidate}}
+
+void foo() {
+  int *ip2 = g(3.14); // this is OK - because the non-int specializations are deleted and sfinae's out one of the template candidates
+  int *ip = g(3); //expected-error{{ambiguous}}
+}
+
+} // end ns2
+
+namespace ns3 {
+template<class T> struct X {
+  template<class U> double *f1(U, T) = delete;
+  template<class U> double *f2(U, T) = delete;
+};
+template<> template<> double* X<int>::f1(int, int);
+template<> template<class U> double* X<int>::f2(U, int);
+
+template<class T, class U> decltype(X<T>{}.f1(U{}, T{})) g1(U, T); // expected-note{{candidate}}
+template<class T, class U> int *g1(U, T); //expected-note{{candidate}}
+
+template<class T, class U> decltype(X<T>{}.f2(U{}, T{})) g2(U, T); // expected-note2{{candidate}}
+template<class T, class U> int *g2(U, T); //expected-note2{{candidate}}
+
+
+void foo() {
+  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
+  int *ip = g1(3, 3); //expected-error{{ambiguous}}
+  {
+   int *ip3 = g2(3.14, 3); //expected-error{{ambiguous}}
+   int *ip4 = g2(3, 3); //expected-error{{ambiguous}}
+  }
+  {
+   int *ip3 = g2(3.14, 3.14); 
+   int *ip4 = g2(3, 3.14); 
+  }
+}
+
+
+} // end ns3
+} // end test_explicit_specializations_and_delete