Author: rsmith Date: Tue Sep 25 15:12:44 2018 New Revision: 343036 URL: http://llvm.org/viewvc/llvm-project?rev=343036&view=rev Log: P0969R0: allow structured binding of accessible members, not only public members.
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td cfe/trunk/include/clang/Sema/Sema.h cfe/trunk/lib/Sema/SemaAccess.cpp cfe/trunk/lib/Sema/SemaDeclAttr.cpp cfe/trunk/lib/Sema/SemaDeclCXX.cpp cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p4.cpp cfe/trunk/www/cxx_status.html Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=343036&r1=343035&r2=343036&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Sep 25 15:12:44 2018 @@ -452,10 +452,12 @@ def err_decomp_decl_multiple_bases_with_ "have non-static data members">; def err_decomp_decl_ambiguous_base : Error< "cannot decompose members of ambiguous base class %1 of %0:%2">; -def err_decomp_decl_non_public_base : Error< - "cannot decompose members of non-public base class %1 of %0">; -def err_decomp_decl_non_public_member : Error< - "cannot decompose non-public member %0 of %1">; +def err_decomp_decl_inaccessible_base : Error< + "cannot decompose members of inaccessible base class %1 of %0">, + AccessControl; +def err_decomp_decl_inaccessible_field : Error< + "cannot decompose %select{private|protected}0 member %1 of %3">, + AccessControl; def err_decomp_decl_anon_union_member : Error< "cannot decompose class type %0 because it has an anonymous " "%select{struct|union}1 member">; Modified: cfe/trunk/include/clang/Sema/Sema.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=343036&r1=343035&r2=343036&view=diff ============================================================================== --- cfe/trunk/include/clang/Sema/Sema.h (original) +++ cfe/trunk/include/clang/Sema/Sema.h Tue Sep 25 15:12:44 2018 @@ -6036,6 +6036,10 @@ public: AccessResult CheckMemberAccess(SourceLocation UseLoc, CXXRecordDecl *NamingClass, DeclAccessPair Found); + AccessResult + CheckStructuredBindingMemberAccess(SourceLocation UseLoc, + CXXRecordDecl *DecomposedClass, + DeclAccessPair Field); AccessResult CheckMemberOperatorAccess(SourceLocation Loc, Expr *ObjectExpr, Expr *ArgExpr, Modified: cfe/trunk/lib/Sema/SemaAccess.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaAccess.cpp?rev=343036&r1=343035&r2=343036&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaAccess.cpp (original) +++ cfe/trunk/lib/Sema/SemaAccess.cpp Tue Sep 25 15:12:44 2018 @@ -1728,6 +1728,22 @@ Sema::AccessResult Sema::CheckMemberAcce return CheckAccess(*this, UseLoc, Entity); } +/// Checks implicit access to a member in a structured binding. +Sema::AccessResult +Sema::CheckStructuredBindingMemberAccess(SourceLocation UseLoc, + CXXRecordDecl *DecomposedClass, + DeclAccessPair Field) { + if (!getLangOpts().AccessControl || + Field.getAccess() == AS_public) + return AR_accessible; + + AccessTarget Entity(Context, AccessTarget::Member, DecomposedClass, Field, + Context.getRecordType(DecomposedClass)); + Entity.setDiag(diag::err_decomp_decl_inaccessible_field); + + return CheckAccess(*this, UseLoc, Entity); +} + /// Checks access to an overloaded member operator, including /// conversion operators. Sema::AccessResult Sema::CheckMemberOperatorAccess(SourceLocation OpLoc, Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=343036&r1=343035&r2=343036&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original) +++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Tue Sep 25 15:12:44 2018 @@ -7514,6 +7514,7 @@ void Sema::PopParsingDeclaration(Parsing // for each of the different declarations. const DelayedDiagnosticPool *pool = &poppedPool; do { + bool AnyAccessFailures = false; for (DelayedDiagnosticPool::pool_iterator i = pool->pool_begin(), e = pool->pool_end(); i != e; ++i) { // This const_cast is a bit lame. Really, Triggered should be mutable. @@ -7530,7 +7531,14 @@ void Sema::PopParsingDeclaration(Parsing break; case DelayedDiagnostic::Access: + // Only produce one access control diagnostic for a structured binding + // declaration: we don't need to tell the user that all the fields are + // inaccessible one at a time. + if (AnyAccessFailures && isa<DecompositionDecl>(decl)) + continue; HandleDelayedAccessCheck(diag, decl); + if (diag.Triggered) + AnyAccessFailures = true; break; case DelayedDiagnostic::ForbiddenType: Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=343036&r1=343035&r2=343036&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original) +++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Tue Sep 25 15:12:44 2018 @@ -1227,16 +1227,16 @@ static bool checkTupleLikeDecomposition( /// Find the base class to decompose in a built-in decomposition of a class type. /// This base class search is, unfortunately, not quite like any other that we /// perform anywhere else in C++. -static const CXXRecordDecl *findDecomposableBaseClass(Sema &S, - SourceLocation Loc, - const CXXRecordDecl *RD, - CXXCastPath &BasePath) { +static DeclAccessPair findDecomposableBaseClass(Sema &S, SourceLocation Loc, + const CXXRecordDecl *RD, + CXXCastPath &BasePath) { auto BaseHasFields = [](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) { return Specifier->getType()->getAsCXXRecordDecl()->hasDirectFields(); }; const CXXRecordDecl *ClassWithFields = nullptr; + AccessSpecifier AS = AS_public; if (RD->hasDirectFields()) // [dcl.decomp]p4: // Otherwise, all of E's non-static data members shall be public direct @@ -1249,7 +1249,7 @@ static const CXXRecordDecl *findDecompos if (!RD->lookupInBases(BaseHasFields, Paths)) { // If no classes have fields, just decompose RD itself. (This will work // if and only if zero bindings were provided.) - return RD; + return DeclAccessPair::make(const_cast<CXXRecordDecl*>(RD), AS_public); } CXXBasePath *BestPath = nullptr; @@ -1262,7 +1262,7 @@ static const CXXRecordDecl *findDecompos S.Diag(Loc, diag::err_decomp_decl_multiple_bases_with_members) << false << RD << BestPath->back().Base->getType() << P.back().Base->getType(); - return nullptr; + return DeclAccessPair(); } else if (P.Access < BestPath->Access) { BestPath = &P; } @@ -1273,23 +1273,13 @@ static const CXXRecordDecl *findDecompos if (Paths.isAmbiguous(S.Context.getCanonicalType(BaseType))) { S.Diag(Loc, diag::err_decomp_decl_ambiguous_base) << RD << BaseType << S.getAmbiguousPathsDisplayString(Paths); - return nullptr; + return DeclAccessPair(); } - // ... public base class of E. - if (BestPath->Access != AS_public) { - S.Diag(Loc, diag::err_decomp_decl_non_public_base) - << RD << BaseType; - for (auto &BS : *BestPath) { - if (BS.Base->getAccessSpecifier() != AS_public) { - S.Diag(BS.Base->getBeginLoc(), diag::note_access_constrained_by_path) - << (BS.Base->getAccessSpecifier() == AS_protected) - << (BS.Base->getAccessSpecifierAsWritten() == AS_none); - break; - } - } - return nullptr; - } + // ... [accessible, implied by other rules] base class of E. + S.CheckBaseClassAccess(Loc, BaseType, S.Context.getRecordType(RD), + *BestPath, diag::err_decomp_decl_inaccessible_base); + AS = BestPath->Access; ClassWithFields = BaseType->getAsCXXRecordDecl(); S.BuildBasePathArray(Paths, BasePath); @@ -1302,17 +1292,19 @@ static const CXXRecordDecl *findDecompos S.Diag(Loc, diag::err_decomp_decl_multiple_bases_with_members) << (ClassWithFields == RD) << RD << ClassWithFields << Paths.front().back().Base->getType(); - return nullptr; + return DeclAccessPair(); } - return ClassWithFields; + return DeclAccessPair::make(const_cast<CXXRecordDecl*>(ClassWithFields), AS); } static bool checkMemberDecomposition(Sema &S, ArrayRef<BindingDecl*> Bindings, ValueDecl *Src, QualType DecompType, - const CXXRecordDecl *RD) { + const CXXRecordDecl *OrigRD) { CXXCastPath BasePath; - RD = findDecomposableBaseClass(S, Src->getLocation(), RD, BasePath); + DeclAccessPair BasePair = + findDecomposableBaseClass(S, Src->getLocation(), OrigRD, BasePath); + const CXXRecordDecl *RD = cast_or_null<CXXRecordDecl>(BasePair.getDecl()); if (!RD) return true; QualType BaseType = S.Context.getQualifiedType(S.Context.getRecordType(RD), @@ -1329,7 +1321,8 @@ static bool checkMemberDecomposition(Sem return true; }; - // all of E's non-static data members shall be public [...] members, + // all of E's non-static data members shall be [...] well-formed + // when named as e.name in the context of the structured binding, // E shall not have an anonymous union member, ... unsigned I = 0; for (auto *FD : RD->fields()) { @@ -1347,26 +1340,16 @@ static bool checkMemberDecomposition(Sem if (I >= Bindings.size()) return DiagnoseBadNumberOfBindings(); auto *B = Bindings[I++]; - SourceLocation Loc = B->getLocation(); - if (FD->getAccess() != AS_public) { - S.Diag(Loc, diag::err_decomp_decl_non_public_member) << FD << DecompType; - // Determine whether the access specifier was explicit. - bool Implicit = true; - for (const auto *D : RD->decls()) { - if (declaresSameEntity(D, FD)) - break; - if (isa<AccessSpecDecl>(D)) { - Implicit = false; - break; - } - } - - S.Diag(FD->getLocation(), diag::note_access_natural) - << (FD->getAccess() == AS_protected) << Implicit; - return true; - } + // The field must be accessible in the context of the structured binding. + // We already checked that the base class is accessible. + // FIXME: Add 'const' to AccessedEntity's classes so we can remove the + // const_cast here. + S.CheckStructuredBindingMemberAccess( + Loc, const_cast<CXXRecordDecl *>(OrigRD), + DeclAccessPair::make(FD, CXXRecordDecl::MergeAccess( + BasePair.getAccess(), FD->getAccess()))); // Initialize the binding to Src.FD. ExprResult E = S.BuildDeclRefExpr(Src, DecompType, VK_LValue, Loc); Modified: cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p4.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p4.cpp?rev=343036&r1=343035&r2=343036&view=diff ============================================================================== --- cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p4.cpp (original) +++ cfe/trunk/test/CXX/dcl.decl/dcl.decomp/p4.cpp Tue Sep 25 15:12:44 2018 @@ -20,15 +20,15 @@ namespace NonPublicMembers { int a; // expected-note 2{{declared private here}} }; - struct NonPublic3 : private A {}; // expected-note {{constrained by private inheritance}} + struct NonPublic3 : private A {}; // expected-note {{declared private here}} struct NonPublic4 : NonPublic2 {}; void test() { - auto [a1] = NonPublic1(); // expected-error {{cannot decompose non-public member 'a' of 'NonPublicMembers::NonPublic1'}} - auto [a2] = NonPublic2(); // expected-error {{cannot decompose non-public member 'a' of 'NonPublicMembers::NonPublic2'}} - auto [a3] = NonPublic3(); // expected-error {{cannot decompose members of non-public base class 'A' of 'NonPublic3'}} - auto [a4] = NonPublic4(); // expected-error {{cannot decompose non-public member 'a' of 'NonPublicMembers::NonPublic4'}} + auto [a1] = NonPublic1(); // expected-error {{cannot decompose protected member 'a' of 'NonPublicMembers::NonPublic1'}} + auto [a2] = NonPublic2(); // expected-error {{cannot decompose private member 'a' of 'NonPublicMembers::NonPublic2'}} + auto [a3] = NonPublic3(); // expected-error {{cannot decompose members of inaccessible base class 'A' of 'NonPublicMembers::NonPublic3'}} + auto [a4] = NonPublic4(); // expected-error {{cannot decompose private member 'a' of 'NonPublicMembers::NonPublic2'}} } } @@ -198,3 +198,44 @@ namespace std_example { same<decltype((x)), const int&> same1; same<decltype((y)), const volatile double&> same2; } + +namespace p0969r0 { + struct A { + int x; + int y; + }; + struct B : private A { // expected-note {{declared private here}} + void test_member() { + auto &[x, y] = *this; + } + friend void test_friend(B); + }; + void test_friend(B b) { + auto &[x, y] = b; + } + void test_external(B b) { + auto &[x, y] = b; // expected-error {{cannot decompose members of inaccessible base class 'p0969r0::A' of 'p0969r0::B'}} + } + + struct C { + int x; + protected: + int y; // expected-note {{declared protected here}} expected-note {{can only access this member on an object of type 'p0969r0::D'}} + void test_member() { + auto &[x, y] = *this; + } + friend void test_friend(struct D); + }; + struct D : C { + static void test_member(D d, C c) { + auto &[x1, y1] = d; + auto &[x2, y2] = c; // expected-error {{cannot decompose protected member 'y' of 'p0969r0::C'}} + } + }; + void test_friend(D d) { + auto &[x, y] = d; + } + void test_external(D d) { + auto &[x, y] = d; // expected-error {{cannot decompose protected member 'y' of 'p0969r0::C'}} + } +} Modified: cfe/trunk/www/cxx_status.html URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=343036&r1=343035&r2=343036&view=diff ============================================================================== --- cfe/trunk/www/cxx_status.html (original) +++ cfe/trunk/www/cxx_status.html Tue Sep 25 15:12:44 2018 @@ -760,7 +760,7 @@ version 3.7. <tr> <!-- from Jacksonville 2018 --> <td><a href="http://wg21.link/p0969r0">P0969R0</a> (<a href="#dr">DR</a>)</td> - <td class="none" align="center">No</td> + <td class="svn" align="center">SVN</td> </tr> <tr> <td>Separate variable and condition for <tt>if</tt> and <tt>switch</tt></td> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits