diff --git a/clang/lib/AST/ExprConstantMeta.cpp b/clang/lib/AST/ExprConstantMeta.cpp index c04c6d9c6432..3056b360852a 100644 --- a/clang/lib/AST/ExprConstantMeta.cpp +++ b/clang/lib/AST/ExprConstantMeta.cpp @@ -5628,27 +5628,25 @@ bool is_accessible(APValue &Result, ASTContext &C, MetaActions &Meta, if (!Evaluator(RV, Args[0], true)) return true; - auto validate = [&](Decl *D) -> bool { - if (!D || !D->getDeclContext() || !isa(D->getDeclContext())) - return DiagnoseReflectionKind(Diagnoser, Range, "a class member"); + auto validate = [&](Decl *D, CXXRecordDecl *&NamingCls) -> bool { + auto *DC = dyn_cast(D->getNonTransparentDeclContext()); + if (!NamingCls) + NamingCls = DC; - if (auto *Ctx = cast(D->getDeclContext()); - Ctx->isBeingDefined()) + if (DC && DC->isBeingDefined()) return Diagnoser(Range.getBegin(), diag::metafn_access_query_class_being_defined) - << Ctx << Range; - + << DC << Range; return false; }; switch (RV.getReflectionKind()) { case ReflectionKind::Type: { NamedDecl *D = findTypeDecl(RV.getReflectedType()); - if (validate(D)) + if (validate(D, NamingCls)) return true; - - if (!NamingCls) - NamingCls = cast(D->getDeclContext()); + else if (!NamingCls) + return SetAndSucceed(Result, makeBool(C, true)); bool Accessible = UnconditionalAccess || Meta.IsAccessible(D, AccessDC, NamingCls); @@ -5656,11 +5654,10 @@ bool is_accessible(APValue &Result, ASTContext &C, MetaActions &Meta, } case ReflectionKind::Declaration: { ValueDecl *D = RV.getReflectedDecl(); - if (validate(D)) + if (validate(D, NamingCls)) return true; - - if (!NamingCls) - NamingCls = cast(D->getDeclContext()); + else if (!NamingCls) + return SetAndSucceed(Result, makeBool(C, true)); bool Accessible = UnconditionalAccess || Meta.IsAccessible(RV.getReflectedDecl(), AccessDC, @@ -5669,11 +5666,10 @@ bool is_accessible(APValue &Result, ASTContext &C, MetaActions &Meta, } case ReflectionKind::Template: { TemplateDecl *D = RV.getReflectedTemplate().getAsTemplateDecl(); - if (validate(D)) + if (validate(D, NamingCls)) return true; - - if (!NamingCls) - NamingCls = cast(D->getDeclContext()); + else if (!NamingCls) + return SetAndSucceed(Result, makeBool(C, true)); bool Accessible = UnconditionalAccess || Meta.IsAccessible(D, AccessDC, NamingCls); @@ -5710,8 +5706,7 @@ bool is_accessible(APValue &Result, ASTContext &C, MetaActions &Meta, case ReflectionKind::Namespace: case ReflectionKind::DataMemberSpec: case ReflectionKind::Annotation: - return DiagnoseReflectionKind(Diagnoser, Range, "a class member", - DescriptionOf(RV)); + return SetAndSucceed(Result, makeBool(C, true)); } llvm_unreachable("invalid reflection type"); } diff --git a/clang/lib/Sema/SemaReflect.cpp b/clang/lib/Sema/SemaReflect.cpp index 7ff4335661ef..f0f42322ab34 100644 --- a/clang/lib/Sema/SemaReflect.cpp +++ b/clang/lib/Sema/SemaReflect.cpp @@ -178,12 +178,34 @@ public: bool IsAccessible(NamedDecl *Target, DeclContext *Ctx, CXXRecordDecl *NamingCls) override { bool Result = false; + + // If 'Target' is a (possibly nested) anonymous struct/union or unscoped + // enumerator, replace it with its parent recursively until it's no longer + // such a member. + while (Target && Target->getDeclContext() && + Target->getDeclContext() != NamingCls && + [](DeclContext *DC) { + if (auto *RD = dyn_cast(DC)) + return RD->isAnonymousStructOrUnion(); + else return DC->isTransparentContext(); + }(Target->getDeclContext())) + if (isa(Target->getDeclContext())) + // Can happen if Target was a member of a static anonymous union at + // namespace scope. + return true; + else + Target = cast(Target->getDeclContext()); + if (auto *Cls = dyn_cast_or_null(Target->getDeclContext())) { if (Cls != NamingCls && !S.IsDerivedFrom(SourceLocation{}, QualType(NamingCls->getTypeForDecl(), 0), QualType(Cls->getTypeForDecl(), 0))) return false; + else if (NamingCls->isAnonymousStructOrUnion()) + // Clang's access-checking machinery isn't equipped to deal with checks + // where the "naming" class (ha!) is anonymous - can't imagine why! + return true; DeclContext *PreviousDC = S.CurContext; { diff --git a/libcxx/test/std/experimental/reflection/member-visibility.pass.cpp b/libcxx/test/std/experimental/reflection/member-visibility.pass.cpp index ab288a3ca1da..ee00325ac62d 100644 --- a/libcxx/test/std/experimental/reflection/member-visibility.pass.cpp +++ b/libcxx/test/std/experimental/reflection/member-visibility.pass.cpp @@ -288,4 +288,55 @@ static_assert(!is_private(std::meta::info{})); static_assert(!is_private(^^int)); } // namespace queries + // ============== + // unscoped_enums + // ============== + +namespace unscoped_enums { +class C { + enum { E }; +public: + enum { F }; + static constexpr auto r = ^^E; +}; + +static_assert(!is_accessible(C::r, + std::meta::access_context::unprivileged())); +static_assert(!is_accessible(C::r, + std::meta::access_context::unprivileged().via(^^C))); + +static_assert(is_accessible(^^C::F, + std::meta::access_context::unprivileged())); +static_assert(is_accessible(^^C::F, + std::meta::access_context::unprivileged().via(^^C))); +} // namespace unscoped_enums + + // ======================== + // anonymous_structs_unions + // ======================== + +namespace anonymous_structs_unions { +class C { + struct { struct { struct { int a; }; }; }; +public: + union { union { union { int b; }; }; }; + static constexpr auto r_a = ^^a; +}; + +static_assert(is_accessible(C::r_a, + std::meta::access_context::unprivileged())); +static_assert(!is_accessible(C::r_a, + std::meta::access_context::unprivileged().via(^^C))); + +static_assert(is_accessible(^^C::b, + std::meta::access_context::unprivileged())); +static_assert(is_accessible(^^C::b, + std::meta::access_context::unprivileged().via(^^C))); + +static union { int a; }; + +static_assert(is_accessible(^^a, + std::meta::access_context::unprivileged())); +} // namespace anonymous_structs_unions + int main() { }