Skip to content

Commit ee479fe

Browse files
committed
Serialization: Use a DeclVisitor to implement deserialization safety.
Refactor deserialization safety to use a `DeclVisitor` CRTP instead of ad-hoc casts. This ensures every kind of decl is handled explicitly. Resolves rdar://115456536
1 parent 58b0ea9 commit ee479fe

File tree

1 file changed

+106
-46
lines changed

1 file changed

+106
-46
lines changed

lib/Serialization/Serialization.cpp

Lines changed: 106 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//===--- Serialization.cpp - Read and write Swift modules -----------------===//
1+
//===--- Serialization.cpp - Read and write Swift modules -----------------===//
22
//
33
// This source file is part of the Swift.org open source project
44
//
@@ -3276,23 +3276,39 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
32763276
return count;
32773277
}
32783278

3279-
public:
3280-
/// Determine if \p decl is safe to deserialize when it's public
3281-
/// or otherwise needed by the client in normal builds, this should usually
3282-
/// correspond to logic in type-checking ensuring these safe decls don't
3283-
/// refer to implementation details. We have to be careful not to mark
3284-
/// anything needed by a client as unsafe as the client will reject reading
3285-
/// it, but at the same time keep the safety checks precise to avoid
3286-
/// XRef errors and such.
3287-
static bool isDeserializationSafe(const Decl *decl) {
3288-
if (auto ext = dyn_cast<ExtensionDecl>(decl)) {
3289-
// Consider extensions as safe as their extended type.
3279+
class ExternallyAccessibleDeclVisitor
3280+
: public DeclVisitor<ExternallyAccessibleDeclVisitor, bool> {
3281+
public:
3282+
ExternallyAccessibleDeclVisitor(){};
3283+
3284+
bool visit(const Decl *D) {
3285+
if (auto value = dyn_cast<ValueDecl>(D)) {
3286+
// A decl is externally accessible if it has a public access level.
3287+
auto accessScope =
3288+
value->getFormalAccessScope(/*useDC=*/nullptr,
3289+
/*treatUsableFromInlineAsPublic=*/true);
3290+
if (accessScope.isPublic() || accessScope.isPackage())
3291+
return true;
3292+
}
3293+
3294+
return DeclVisitor<ExternallyAccessibleDeclVisitor, bool>::visit(
3295+
const_cast<Decl *>(D));
3296+
}
3297+
3298+
// Force all decl kinds to be handled explicitly.
3299+
bool visitDecl(const Decl *D) = delete;
3300+
bool visitValueDecl(const ValueDecl *valueDecl) = delete;
3301+
3302+
bool visitExtensionDecl(const ExtensionDecl *ext) {
3303+
// Extensions must extend externally accessible types to be externally
3304+
// accessible.
32903305
auto nominalType = ext->getExtendedNominal();
32913306
if (!nominalType ||
32923307
!isDeserializationSafe(nominalType))
32933308
return false;
32943309

3295-
// We can mark the extension unsafe only if it has no public members.
3310+
// If the extension has any externally accessible members, then it is
3311+
// externally accessible.
32963312
auto members = ext->getMembers();
32973313
auto hasSafeMembers = std::any_of(members.begin(), members.end(),
32983314
[](const Decl *D) -> bool {
@@ -3303,26 +3319,28 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
33033319
if (hasSafeMembers)
33043320
return true;
33053321

3306-
// We can mark the extension unsafe only if it has no public
3307-
// conformances.
3322+
// If the extension has any externally accessible conformances, then it is
3323+
// externally accessible.
33083324
auto protocols = ext->getLocalProtocols(ConformanceLookupKind::All);
33093325
bool hasSafeConformances = std::any_of(
3310-
protocols.begin(), protocols.end(), [](ProtocolDecl *protocol) {
3311-
return isDeserializationSafe(protocol);
3326+
protocols.begin(), protocols.end(), [this](ProtocolDecl *protocol) {
3327+
return visit(protocol);
33123328
});
33133329

33143330
if (hasSafeConformances)
33153331
return true;
33163332

3317-
// Truly empty extensions are safe, it may happen in swiftinterfaces.
3333+
// Truly empty extensions are externally accessible. This can occur in
3334+
// swiftinterfaces, for example.
33183335
if (members.empty() && protocols.size() == 0)
33193336
return true;
33203337

33213338
return false;
33223339
}
33233340

3324-
if (auto pbd = dyn_cast<PatternBindingDecl>(decl)) {
3325-
// Pattern bindings are safe if any of their var decls are safe.
3341+
bool visitPatternBindingDecl(const PatternBindingDecl *pbd) {
3342+
// Pattern bindings are externally accessible if any of their var decls
3343+
// are externally accessible.
33263344
for (auto i : range(pbd->getNumPatternEntries())) {
33273345
if (auto *varDecl = pbd->getAnchoringVarDecl(i)) {
33283346
if (isDeserializationSafe(varDecl))
@@ -3333,44 +3351,86 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
33333351
return false;
33343352
}
33353353

3336-
return isDeserializationSafe(cast<ValueDecl>(decl));
3337-
}
3338-
3339-
static bool isDeserializationSafe(const ValueDecl *value) {
3340-
// A decl is safe if formally accessible publicly.
3341-
auto accessScope =
3342-
value->getFormalAccessScope(/*useDC=*/nullptr,
3343-
/*treatUsableFromInlineAsPublic=*/true);
3344-
if (accessScope.isPublic() || accessScope.isPackage())
3345-
return true;
3346-
3347-
if (auto accessor = dyn_cast<AccessorDecl>(value))
3348-
// Accessors are as safe as their storage.
3349-
if (isDeserializationSafe(accessor->getStorage()))
3350-
return true;
3351-
3352-
// Frozen fields are always safe.
3353-
if (auto var = dyn_cast<VarDecl>(value)) {
3354+
bool visitVarDecl(const VarDecl *var) {
33543355
if (var->isLayoutExposedToClients())
33553356
return true;
33563357

3357-
// Consider all lazy var storage as "safe".
3358+
// Consider all lazy var storage as externally accessible.
33583359
// FIXME: We should keep track of what lazy var is associated to the
3359-
// storage for them to preserve the same safeness.
3360+
// storage for them to preserve the same accessibility.
33603361
if (var->isLazyStorageProperty())
33613362
return true;
33623363

3363-
// Property wrappers storage is as safe as the wrapped property.
3364+
// Property wrapper storage is as externally accessible as the wrapped
3365+
// property.
33643366
if (VarDecl *wrapped = var->getOriginalWrappedProperty())
3365-
if (isDeserializationSafe(wrapped))
3367+
if (visit(wrapped))
33663368
return true;
3369+
3370+
return false;
33673371
}
33683372

3369-
// Paramters don't have meaningful access control.
3370-
if (isa<ParamDecl>(value) || isa<GenericTypeParamDecl>(value))
3371-
return true;
3373+
bool visitAccessorDecl(const AccessorDecl *accessor) {
3374+
// Accessors are as externally accessible as the storage.
3375+
return visit(accessor->getStorage());
3376+
}
3377+
3378+
// ValueDecls with effectively public access are considered externally
3379+
// accessible and are handled in visit(Decl *) above. Some specific kinds of
3380+
// ValueDecls with additional, unique rules are handled individually above.
3381+
// ValueDecls that are not effectively public and do not have unique rules
3382+
// are by default externally inaccessable.
3383+
#define DEFAULT_TO_ACCESS_LEVEL(KIND) \
3384+
bool visit##KIND##Decl(const KIND##Decl *D) { \
3385+
static_assert(std::is_convertible<KIND##Decl *, ValueDecl *>::value, \
3386+
"##KIND##Decl must be a ValueDecl"); \
3387+
return false; \
3388+
}
3389+
DEFAULT_TO_ACCESS_LEVEL(NominalType);
3390+
DEFAULT_TO_ACCESS_LEVEL(OpaqueType);
3391+
DEFAULT_TO_ACCESS_LEVEL(TypeAlias);
3392+
DEFAULT_TO_ACCESS_LEVEL(AssociatedType);
3393+
DEFAULT_TO_ACCESS_LEVEL(AbstractStorage);
3394+
DEFAULT_TO_ACCESS_LEVEL(AbstractFunction);
3395+
DEFAULT_TO_ACCESS_LEVEL(Macro);
3396+
DEFAULT_TO_ACCESS_LEVEL(EnumElement);
3397+
3398+
#undef DEFAULT_TO_ACCESS_LEVEL
3399+
3400+
// There are several kinds of decls which we never expect to encounter in
3401+
// external accessibility queries.
3402+
#define UNREACHABLE(KIND) \
3403+
bool visit##KIND##Decl(const KIND##Decl *D) { \
3404+
llvm_unreachable("unexpected decl kind"); \
3405+
return true; \
3406+
}
3407+
UNREACHABLE(Module);
3408+
UNREACHABLE(TopLevelCode);
3409+
UNREACHABLE(Import);
3410+
UNREACHABLE(PoundDiagnostic);
3411+
UNREACHABLE(Missing);
3412+
UNREACHABLE(MissingMember);
3413+
UNREACHABLE(MacroExpansion);
3414+
UNREACHABLE(GenericTypeParam);
3415+
UNREACHABLE(Param);
3416+
UNREACHABLE(IfConfig);
3417+
UNREACHABLE(PrecedenceGroup);
3418+
UNREACHABLE(Operator);
3419+
UNREACHABLE(EnumCase);
3420+
3421+
#undef UNREACHABLE
3422+
};
33723423

3373-
return false;
3424+
public:
3425+
/// Determine if \p decl is safe to deserialize when it's public
3426+
/// or otherwise needed by the client in normal builds, this should usually
3427+
/// correspond to logic in type-checking ensuring these safe decls don't
3428+
/// refer to implementation details. We have to be careful not to mark
3429+
/// anything needed by a client as unsafe as the client will reject reading
3430+
/// it, but at the same time keep the safety checks precise to avoid
3431+
/// XRef errors and such.
3432+
static bool isDeserializationSafe(const Decl *decl) {
3433+
return ExternallyAccessibleDeclVisitor().visit(decl);
33743434
}
33753435

33763436
private:

0 commit comments

Comments
 (0)