From 14274399e200fffae272d0c5f3bc4bdfff2e3a82 Mon Sep 17 00:00:00 2001 From: Erik Pilkington Date: Thu, 9 Aug 2018 20:11:13 +0000 Subject: [PATCH] [Sema] P0961R1: Relaxing the structured bindings customization point finding rules Differential revision: https://reviews.llvm.org/D50418 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@339375 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaDeclCXX.cpp | 17 ++++++- test/CXX/dcl.decl/dcl.decomp/p3.cpp | 77 ++++++++++++++++++++++++++--- www/cxx_status.html | 2 +- 3 files changed, 87 insertions(+), 9 deletions(-) diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index f119a6ef04..12b4557f72 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -1108,7 +1108,7 @@ static bool checkTupleLikeDecomposition(Sema &S, // [dcl.decomp]p3: // The unqualified-id get is looked up in the scope of E by class member - // access lookup + // access lookup ... LookupResult MemberGet(S, GetDN, Src->getLocation(), Sema::LookupMemberName); bool UseMemberGet = false; if (S.isCompleteType(Src->getLocation(), DecompType)) { @@ -1116,7 +1116,20 @@ static bool checkTupleLikeDecomposition(Sema &S, S.LookupQualifiedName(MemberGet, RD); if (MemberGet.isAmbiguous()) return true; - UseMemberGet = !MemberGet.empty(); + // ... and if that finds at least one declaration that is a function + // template whose first template parameter is a non-type parameter ... + for (NamedDecl *D : MemberGet) { + if (FunctionTemplateDecl *FTD = + dyn_cast(D->getUnderlyingDecl())) { + TemplateParameterList *TPL = FTD->getTemplateParameters(); + if (TPL->size() != 0 && + isa(TPL->getParam(0))) { + // ... the initializer is e.get(). + UseMemberGet = true; + break; + } + } + } S.FilterAcceptableTemplateNames(MemberGet); } diff --git a/test/CXX/dcl.decl/dcl.decomp/p3.cpp b/test/CXX/dcl.decl/dcl.decomp/p3.cpp index b7092e3af0..b3f0cf1874 100644 --- a/test/CXX/dcl.decl/dcl.decomp/p3.cpp +++ b/test/CXX/dcl.decl/dcl.decomp/p3.cpp @@ -36,7 +36,7 @@ void no_get_2() { auto [a0, a1, a2] = A(); // expected-error {{undeclared identifier 'get'}} expected-note {{in implicit initialization of binding declaration 'a0'}} } -template float &get(A); +template float &get(A); // expected-note 2 {{no known conversion}} void no_tuple_element_1() { auto [a0, a1, a2] = A(); // expected-error-re {{'std::tuple_element<0U{{L*}}, A>::type' does not name a type}} expected-note {{in implicit}} @@ -57,7 +57,7 @@ void no_tuple_element_3() { template<> struct std::tuple_element<1, A> { typedef float &type; }; template<> struct std::tuple_element<2, A> { typedef const float &type; }; -template auto get(B) -> int (&)[N + 1]; +template auto get(B) -> int (&)[N + 1]; // expected-note 2 {{no known conversion}} template struct std::tuple_element { typedef int type[N +1 ]; }; template struct std::tuple_size : std::tuple_size {}; @@ -138,19 +138,25 @@ int member_get() { return c; } -struct D { template struct get {}; }; // expected-note {{declared here}} +struct D { + // FIXME: Emit a note here explaining why this was ignored. + template struct get {}; +}; template<> struct std::tuple_size { static const int value = 1; }; template<> struct std::tuple_element<0, D> { typedef D::get<0> type; }; void member_get_class_template() { - auto [d] = D(); // expected-error {{cannot refer to member 'get' in 'D' with '.'}} expected-note {{in implicit init}} + auto [d] = D(); // expected-error {{no matching function for call to 'get'}} expected-note {{in implicit init}} } -struct E { int get(); }; +struct E { + // FIXME: Emit a note here explaining why this was ignored. + int get(); +}; template<> struct std::tuple_size { static const int value = 1; }; template<> struct std::tuple_element<0, E> { typedef int type; }; void member_get_non_template() { // FIXME: This diagnostic is not very good. - auto [e] = E(); // expected-error {{no member named 'get'}} expected-note {{in implicit init}} + auto [e] = E(); // expected-error {{no matching function for call to 'get'}} expected-note {{in implicit init}} } namespace ADL { @@ -230,3 +236,62 @@ namespace constant { } static_assert(g() == 4); // expected-error {{constant}} expected-note {{in call to 'g()'}} } + +// P0961R1 +struct InvalidMemberGet { + int get(); + template int get(); + struct get {}; +}; +template <> struct std::tuple_size { static constexpr size_t value = 1; }; +template <> struct std::tuple_element<0, InvalidMemberGet> { typedef float type; }; +template float get(InvalidMemberGet) { return 0; } +int f() { + InvalidMemberGet img; + auto [x] = img; + typedef decltype(x) same_as_float; + typedef float same_as_float; +} + +struct ValidMemberGet { + int get(); + template int get() { return 0; } + template float get() { return 0; } +}; +template <> struct std::tuple_size { static constexpr size_t value = 1; }; +template <> struct std::tuple_element<0, ValidMemberGet> { typedef float type; }; +// Don't use this one; we should use the member get. +template int get(ValidMemberGet) { static_assert(N && false, ""); } +int f2() { + ValidMemberGet img; + auto [x] = img; + typedef decltype(x) same_as_float; + typedef float same_as_float; +} + +struct Base1 { + int get(); // expected-note{{member found by ambiguous name lookup}} +}; +struct Base2 { + template int get(); // expected-note{{member found by ambiguous name lookup}} +}; +struct Derived : Base1, Base2 {}; + +template <> struct std::tuple_size { static constexpr size_t value = 1; }; +template <> struct std::tuple_element<0, Derived> { typedef int type; }; + +auto [x] = Derived(); // expected-error{{member 'get' found in multiple base classes of different types}} + +struct Base { + template int get(); +}; +struct UsingGet : Base { + using Base::get; +}; + +template <> struct std::tuple_size { + static constexpr size_t value = 1; +}; +template <> struct std::tuple_element<0, UsingGet> { typedef int type; }; + +auto [y] = UsingGet(); diff --git a/www/cxx_status.html b/www/cxx_status.html index ffc5fa5351..b86d67f07d 100755 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -755,7 +755,7 @@ version 3.7. P0961R1 (DR) - No + SVN -- 2.50.1