Skip to content

Commit 82923d5

Browse files
committed
AST: Expose DeclExportabilityVisitor.
Extract `ExternallyAccessibleDeclVisitor` from `Serialization.cpp` into its own header and rename it to `DeclExportabilityVisitor` to better align with terminology in other parts of the compiler. Ideally, `DeclExportabilityVisitor` should become the canoncial implementation of for exportability checks, consolidating logic that is currently spread between serialization, module interface printing, TBDGen, and type checking. For now, it is only used in serialization to implement serialization safety checks.
1 parent 1cec6a4 commit 82923d5

File tree

2 files changed

+182
-157
lines changed

2 files changed

+182
-157
lines changed
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
//===- DeclExportabilityVisitor.h - Swift Language Context ASTs -*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file defines the DeclExportabilityVisitor class. "Exportability" refers
14+
// to whether a declaration may be referenced from outside of the module it
15+
// is defined in.
16+
//
17+
//===----------------------------------------------------------------------===//
18+
19+
#ifndef SWIFT_DECLEXPORTABILITYVISITOR_H
20+
#define SWIFT_DECLEXPORTABILITYVISITOR_H
21+
22+
#include "swift/AST/ASTVisitor.h"
23+
#include "swift/AST/Decl.h"
24+
25+
namespace swift {
26+
27+
/// This visitor determines whether a declaration is "exportable", meaning whether
28+
/// it can be referenced by other modules. For example, a function with a public
29+
/// access level or with the `@usableFromInline` attribute is exportable.
30+
class DeclExportabilityVisitor
31+
: public DeclVisitor<DeclExportabilityVisitor, bool> {
32+
public:
33+
DeclExportabilityVisitor(){};
34+
35+
bool visit(const Decl *D) {
36+
if (auto value = dyn_cast<ValueDecl>(D)) {
37+
// A decl is exportable if it has a public access level.
38+
auto accessScope =
39+
value->getFormalAccessScope(/*useDC=*/nullptr,
40+
/*treatUsableFromInlineAsPublic=*/true);
41+
if (accessScope.isPublic() || accessScope.isPackage())
42+
return true;
43+
}
44+
45+
return DeclVisitor<DeclExportabilityVisitor, bool>::visit(
46+
const_cast<Decl *>(D));
47+
}
48+
49+
// Force all decl kinds to be handled explicitly.
50+
bool visitDecl(const Decl *D) = delete;
51+
bool visitValueDecl(const ValueDecl *valueDecl) = delete;
52+
53+
bool visitExtensionDecl(const ExtensionDecl *ext) {
54+
// Extensions must extend exportable types to be exportable.
55+
auto nominalType = ext->getExtendedNominal();
56+
if (!nominalType || !visit(nominalType))
57+
return false;
58+
59+
// If the extension has any exportable members, then it is exportable.
60+
auto members = ext->getMembers();
61+
auto hasSafeMembers =
62+
std::any_of(members.begin(), members.end(), [&](const Decl *D) -> bool {
63+
if (auto VD = dyn_cast<ValueDecl>(D))
64+
return visit(VD);
65+
return true;
66+
});
67+
if (hasSafeMembers)
68+
return true;
69+
70+
// If the extension has any exportable conformances, then it is exportable.
71+
auto protocols = ext->getLocalProtocols(ConformanceLookupKind::All);
72+
bool hasSafeConformances =
73+
std::any_of(protocols.begin(), protocols.end(),
74+
[this](ProtocolDecl *protocol) { return visit(protocol); });
75+
76+
if (hasSafeConformances)
77+
return true;
78+
79+
// Truly empty extensions are exportable. This can occur in swiftinterfaces,
80+
// for example.
81+
if (members.empty() && protocols.size() == 0)
82+
return true;
83+
84+
return false;
85+
}
86+
87+
bool visitPatternBindingDecl(const PatternBindingDecl *pbd) {
88+
// Pattern bindings are exportable if any of their var decls are exportable.
89+
for (auto i : range(pbd->getNumPatternEntries())) {
90+
if (auto *varDecl = pbd->getAnchoringVarDecl(i)) {
91+
if (visit(varDecl))
92+
return true;
93+
}
94+
}
95+
96+
return false;
97+
}
98+
99+
bool visitVarDecl(const VarDecl *var) {
100+
if (var->isLayoutExposedToClients())
101+
return true;
102+
103+
// Consider all lazy var storage as exportable.
104+
// FIXME: We should keep track of what lazy var is associated to the
105+
// storage for them to preserve the same accessibility.
106+
if (var->isLazyStorageProperty())
107+
return true;
108+
109+
// Property wrapper storage is as exportable as the wrapped property.
110+
if (VarDecl *wrapped = var->getOriginalWrappedProperty())
111+
if (visit(wrapped))
112+
return true;
113+
114+
return false;
115+
}
116+
117+
bool visitAccessorDecl(const AccessorDecl *accessor) {
118+
// Accessors are as exportable as their storage.
119+
return visit(accessor->getStorage());
120+
}
121+
122+
// ValueDecls with effectively public access are considered exportable and are
123+
// handled in visit(Decl *) above. Some specific kinds of ValueDecls with
124+
// additional, unique rules are handled individually above. ValueDecls that
125+
// are not effectively public and do not have unique rules are by default not
126+
// exportable.
127+
#define DEFAULT_TO_ACCESS_LEVEL(KIND) \
128+
bool visit##KIND##Decl(const KIND##Decl *D) { \
129+
static_assert(std::is_convertible<KIND##Decl *, ValueDecl *>::value, \
130+
"##KIND##Decl must be a ValueDecl"); \
131+
return false; \
132+
}
133+
DEFAULT_TO_ACCESS_LEVEL(NominalType);
134+
DEFAULT_TO_ACCESS_LEVEL(OpaqueType);
135+
DEFAULT_TO_ACCESS_LEVEL(TypeAlias);
136+
DEFAULT_TO_ACCESS_LEVEL(AssociatedType);
137+
DEFAULT_TO_ACCESS_LEVEL(AbstractStorage);
138+
DEFAULT_TO_ACCESS_LEVEL(AbstractFunction);
139+
DEFAULT_TO_ACCESS_LEVEL(Macro);
140+
DEFAULT_TO_ACCESS_LEVEL(EnumElement);
141+
142+
#undef DEFAULT_TO_ACCESS_LEVEL
143+
144+
// There are several kinds of decls which we never expect to encounter in
145+
// exportability queries.
146+
#define UNREACHABLE(KIND) \
147+
bool visit##KIND##Decl(const KIND##Decl *D) { \
148+
llvm_unreachable("unexpected decl kind"); \
149+
return true; \
150+
}
151+
UNREACHABLE(Module);
152+
UNREACHABLE(TopLevelCode);
153+
UNREACHABLE(Import);
154+
UNREACHABLE(PoundDiagnostic);
155+
UNREACHABLE(Missing);
156+
UNREACHABLE(MissingMember);
157+
UNREACHABLE(MacroExpansion);
158+
UNREACHABLE(GenericTypeParam);
159+
UNREACHABLE(Param);
160+
161+
#undef UNREACHABLE
162+
163+
// Uninteresting decls are always considered exportable. A kind of decl might
164+
// always be exportable if it is declared at the top level and access control
165+
// does not apply to it. Or, a kind of decl could be considered always
166+
// exportable because it is only found nested within other declarations that
167+
// have their own access level, in which case we assume that the declaration
168+
// context has already been checked.
169+
#define UNINTERESTING(KIND) \
170+
bool visit##KIND##Decl(const KIND##Decl *D) { return true; }
171+
UNINTERESTING(IfConfig);
172+
UNINTERESTING(PrecedenceGroup);
173+
UNINTERESTING(EnumCase);
174+
UNINTERESTING(Operator);
175+
176+
#undef UNINTERESTING
177+
};
178+
} // end namespace swift
179+
180+
#endif

lib/Serialization/Serialization.cpp

Lines changed: 2 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "swift/AST/ASTMangler.h"
1717
#include "swift/AST/ASTVisitor.h"
1818
#include "swift/AST/AutoDiff.h"
19+
#include "swift/AST/DeclExportabilityVisitor.h"
1920
#include "swift/AST/DiagnosticsCommon.h"
2021
#include "swift/AST/Expr.h"
2122
#include "swift/AST/FileSystem.h"
@@ -3276,162 +3277,6 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
32763277
return count;
32773278
}
32783279

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.
3305-
auto nominalType = ext->getExtendedNominal();
3306-
if (!nominalType ||
3307-
!isDeserializationSafe(nominalType))
3308-
return false;
3309-
3310-
// If the extension has any externally accessible members, then it is
3311-
// externally accessible.
3312-
auto members = ext->getMembers();
3313-
auto hasSafeMembers = std::any_of(members.begin(), members.end(),
3314-
[](const Decl *D) -> bool {
3315-
if (auto VD = dyn_cast<ValueDecl>(D))
3316-
return isDeserializationSafe(VD);
3317-
return true;
3318-
});
3319-
if (hasSafeMembers)
3320-
return true;
3321-
3322-
// If the extension has any externally accessible conformances, then it is
3323-
// externally accessible.
3324-
auto protocols = ext->getLocalProtocols(ConformanceLookupKind::All);
3325-
bool hasSafeConformances = std::any_of(
3326-
protocols.begin(), protocols.end(), [this](ProtocolDecl *protocol) {
3327-
return visit(protocol);
3328-
});
3329-
3330-
if (hasSafeConformances)
3331-
return true;
3332-
3333-
// Truly empty extensions are externally accessible. This can occur in
3334-
// swiftinterfaces, for example.
3335-
if (members.empty() && protocols.size() == 0)
3336-
return true;
3337-
3338-
return false;
3339-
}
3340-
3341-
bool visitPatternBindingDecl(const PatternBindingDecl *pbd) {
3342-
// Pattern bindings are externally accessible if any of their var decls
3343-
// are externally accessible.
3344-
for (auto i : range(pbd->getNumPatternEntries())) {
3345-
if (auto *varDecl = pbd->getAnchoringVarDecl(i)) {
3346-
if (isDeserializationSafe(varDecl))
3347-
return true;
3348-
}
3349-
}
3350-
3351-
return false;
3352-
}
3353-
3354-
bool visitVarDecl(const VarDecl *var) {
3355-
if (var->isLayoutExposedToClients())
3356-
return true;
3357-
3358-
// Consider all lazy var storage as externally accessible.
3359-
// FIXME: We should keep track of what lazy var is associated to the
3360-
// storage for them to preserve the same accessibility.
3361-
if (var->isLazyStorageProperty())
3362-
return true;
3363-
3364-
// Property wrapper storage is as externally accessible as the wrapped
3365-
// property.
3366-
if (VarDecl *wrapped = var->getOriginalWrappedProperty())
3367-
if (visit(wrapped))
3368-
return true;
3369-
3370-
return false;
3371-
}
3372-
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-
3417-
#undef UNREACHABLE
3418-
3419-
// Uninteresting decls are always considered external. A kind of decl might
3420-
// always be external if it is declared at the top level and access control
3421-
// does not apply to it. Or, a kind of decl could be considered always
3422-
// external because it is only found nested within other declarations that
3423-
// have their own access level, in which case we assume that the declaration
3424-
// context has already been checked.
3425-
#define UNINTERESTING(KIND) \
3426-
bool visit##KIND##Decl(const KIND##Decl *D) { return true; }
3427-
UNINTERESTING(IfConfig);
3428-
UNINTERESTING(PrecedenceGroup);
3429-
UNINTERESTING(EnumCase);
3430-
UNINTERESTING(Operator);
3431-
3432-
#undef UNINTERESTING
3433-
};
3434-
34353280
public:
34363281
/// Determine if \p decl is safe to deserialize when it's public
34373282
/// or otherwise needed by the client in normal builds, this should usually
@@ -3441,7 +3286,7 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
34413286
/// it, but at the same time keep the safety checks precise to avoid
34423287
/// XRef errors and such.
34433288
static bool isDeserializationSafe(const Decl *decl) {
3444-
return ExternallyAccessibleDeclVisitor().visit(decl);
3289+
return DeclExportabilityVisitor().visit(decl);
34453290
}
34463291

34473292
private:

0 commit comments

Comments
 (0)