On Sun, Apr 17, 2016 at 10:32 AM, Faisal Vali via cfe-commits < cfe-commits@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@lists.llvm.org > http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits >
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits