Fix some accessibility corner case bugs.
Anonymous structs, anonymous unions, unscoped enums. You know, all of the most important aspects of this wonderful language of ours.
This commit is contained in:
@@ -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<CXXRecordDecl>(D->getDeclContext()))
|
||||
return DiagnoseReflectionKind(Diagnoser, Range, "a class member");
|
||||
auto validate = [&](Decl *D, CXXRecordDecl *&NamingCls) -> bool {
|
||||
auto *DC = dyn_cast<CXXRecordDecl>(D->getNonTransparentDeclContext());
|
||||
if (!NamingCls)
|
||||
NamingCls = DC;
|
||||
|
||||
if (auto *Ctx = cast<CXXRecordDecl>(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<CXXRecordDecl>(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<CXXRecordDecl>(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<CXXRecordDecl>(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");
|
||||
}
|
||||
|
||||
@@ -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<CXXRecordDecl>(DC))
|
||||
return RD->isAnonymousStructOrUnion();
|
||||
else return DC->isTransparentContext();
|
||||
}(Target->getDeclContext()))
|
||||
if (isa<TranslationUnitDecl>(Target->getDeclContext()))
|
||||
// Can happen if Target was a member of a static anonymous union at
|
||||
// namespace scope.
|
||||
return true;
|
||||
else
|
||||
Target = cast<NamedDecl>(Target->getDeclContext());
|
||||
|
||||
if (auto *Cls = dyn_cast_or_null<CXXRecordDecl>(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;
|
||||
{
|
||||
|
||||
@@ -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() { }
|
||||
|
||||
Reference in New Issue
Block a user