r266561 - Implement CWG 941 - explicit specializations of deleted function templates

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Sun Apr 17 13:05:00 PDT 2016


On Sun, Apr 17, 2016 at 10:32 AM, Faisal Vali via cfe-commits <
cfe-commits at lists.llvm.org> wrote:

> Author: faisalv
> Date: Sun Apr 17 12:32:04 2016
> New Revision: 266561
>
> URL: http://llvm.org/viewvc/llvm-project?rev=266561&view=rev
> Log:
> Implement CWG 941 - explicit specializations of deleted function templates
>
>   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.
>
>
> Added:
>     cfe/trunk/test/SemaCXX/delete-and-function-templates.cpp
> Modified:
>     cfe/trunk/lib/Sema/SemaDecl.cpp
>     cfe/trunk/lib/Sema/SemaTemplate.cpp
>
> Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=266561&r1=266560&r2=266561&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaDecl.cpp Sun Apr 17 12:32:04 2016
> @@ -8630,6 +8630,14 @@ bool Sema::CheckFunctionDeclaration(Scop
>            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 {
>
> Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=266561&r1=266560&r2=266561&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaTemplate.cpp Sun Apr 17 12:32:04 2016
> @@ -6978,6 +6978,13 @@ bool Sema::CheckFunctionTemplateSpeciali
>    // 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
> +    // function template.
> +    if (Specialization->isDeleted()) {
> +      assert(!SpecInfo->isExplicitSpecialization());
> +      assert(Specialization->getCanonicalDecl() == Specialization);
> +      Specialization->setDeletedAsWritten(false);
> +    }
>      SpecInfo->setTemplateSpecializationKind(TSK_ExplicitSpecialization);
>      MarkUnusedFileScopedDecl(Specialization);
>    }
> @@ -7137,6 +7144,13 @@ Sema::CheckMemberSpecialization(NamedDec
>        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);
> +      }
>      }
>
>      cast<FunctionDecl>(Member)->setInstantiationOfMemberFunction(
>
> Added: cfe/trunk/test/SemaCXX/delete-and-function-templates.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/delete-and-function-templates.cpp?rev=266561&view=auto
>
> ==============================================================================
> --- cfe/trunk/test/SemaCXX/delete-and-function-templates.cpp (added)
> +++ cfe/trunk/test/SemaCXX/delete-and-function-templates.cpp Sun Apr 17
> 12:32:04 2016
> @@ -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) { }
>

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.


> +
> +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
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20160417/0c570391/attachment-0001.html>


More information about the cfe-commits mailing list