Skip to content

Commit 6170714

Browse files
authored
Merge pull request #68533 from tshortli/serialize-externally-accessible-decls-refactor
Serialization: Use a DeclVisitor to implement deserialization safety
2 parents f13efab + 82923d5 commit 6170714

File tree

7 files changed

+288
-98
lines changed

7 files changed

+288
-98
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: 3 additions & 87 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
//
@@ -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"
@@ -3285,92 +3286,7 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
32853286
/// it, but at the same time keep the safety checks precise to avoid
32863287
/// XRef errors and such.
32873288
static bool isDeserializationSafe(const Decl *decl) {
3288-
if (auto ext = dyn_cast<ExtensionDecl>(decl)) {
3289-
// Consider extensions as safe as their extended type.
3290-
auto nominalType = ext->getExtendedNominal();
3291-
if (!nominalType ||
3292-
!isDeserializationSafe(nominalType))
3293-
return false;
3294-
3295-
// We can mark the extension unsafe only if it has no public members.
3296-
auto members = ext->getMembers();
3297-
auto hasSafeMembers = std::any_of(members.begin(), members.end(),
3298-
[](const Decl *D) -> bool {
3299-
if (auto VD = dyn_cast<ValueDecl>(D))
3300-
return isDeserializationSafe(VD);
3301-
return true;
3302-
});
3303-
if (hasSafeMembers)
3304-
return true;
3305-
3306-
// We can mark the extension unsafe only if it has no public
3307-
// conformances.
3308-
auto protocols = ext->getLocalProtocols(ConformanceLookupKind::All);
3309-
bool hasSafeConformances = std::any_of(
3310-
protocols.begin(), protocols.end(), [](ProtocolDecl *protocol) {
3311-
return isDeserializationSafe(protocol);
3312-
});
3313-
3314-
if (hasSafeConformances)
3315-
return true;
3316-
3317-
// Truly empty extensions are safe, it may happen in swiftinterfaces.
3318-
if (members.empty() && protocols.size() == 0)
3319-
return true;
3320-
3321-
return false;
3322-
}
3323-
3324-
if (auto pbd = dyn_cast<PatternBindingDecl>(decl)) {
3325-
// Pattern bindings are safe if any of their var decls are safe.
3326-
for (auto i : range(pbd->getNumPatternEntries())) {
3327-
if (auto *varDecl = pbd->getAnchoringVarDecl(i)) {
3328-
if (isDeserializationSafe(varDecl))
3329-
return true;
3330-
}
3331-
}
3332-
3333-
return false;
3334-
}
3335-
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-
if (var->isLayoutExposedToClients())
3355-
return true;
3356-
3357-
// Consider all lazy var storage as "safe".
3358-
// FIXME: We should keep track of what lazy var is associated to the
3359-
// storage for them to preserve the same safeness.
3360-
if (var->isLazyStorageProperty())
3361-
return true;
3362-
3363-
// Property wrappers storage is as safe as the wrapped property.
3364-
if (VarDecl *wrapped = var->getOriginalWrappedProperty())
3365-
if (isDeserializationSafe(wrapped))
3366-
return true;
3367-
}
3368-
3369-
// Paramters don't have meaningful access control.
3370-
if (isa<ParamDecl>(value) || isa<GenericTypeParamDecl>(value))
3371-
return true;
3372-
3373-
return false;
3289+
return DeclExportabilityVisitor().visit(decl);
33743290
}
33753291

33763292
private:

test/Inputs/lazy_typecheck.swift

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,26 @@ class InternalClass: DoesNotExist { // expected-error {{cannot find type 'DoesNo
168168
init(x: DoesNotExist) {} // expected-error {{cannot find type 'DoesNotExist' in scope}}
169169
}
170170

171+
public enum PublicEnum {
172+
case a
173+
case b(x: Int)
174+
175+
public func publicMethod() -> Int {
176+
return true // expected-error {{cannot convert return expression of type 'Bool' to return type 'Int'}}
177+
}
178+
179+
public var publicComputedVar: Int {
180+
return true // expected-error {{cannot convert return expression of type 'Bool' to return type 'Int'}}
181+
}
182+
}
183+
184+
enum InternalEnum {
185+
case bad(DoesNotExist) // expected-error {{cannot find type 'DoesNotExist' in scope}}
186+
187+
func method() -> DoesNotExist { // expected-error {{cannot find type 'DoesNotExist' in scope}}
188+
}
189+
}
190+
171191
// MARK: - Conformances
172192

173193
public struct PublicStructConformingToPublicProto: PublicProto {
@@ -222,4 +242,32 @@ extension PublicGenericStruct where T == InternalStructForConstraint {}
222242

223243
extension PublicGenericStruct: EmptyPublicProto where T == InternalStructForConstraint {}
224244

225-
// FIXME: Test enums
245+
// MARK: - Type aliases
246+
247+
public typealias PublicStructAlias = PublicStruct
248+
typealias InternalTypeAlias = DoesNotExist // expected-error {{cannot find type 'DoesNotExist' in scope}}
249+
250+
// MARK: - Compiler directives
251+
252+
extension PublicStruct {
253+
#if FLAG
254+
public static func activeMethod() {}
255+
#else
256+
public static func inactiveMethod() -> DoesNotExist {}
257+
#endif
258+
}
259+
260+
// MARK: - Operators & Precedence Groups
261+
262+
precedencegroup FooPrecedence {
263+
assignment: true
264+
associativity: right
265+
}
266+
267+
infix operator <<<: FooPrecedence
268+
269+
extension PublicStruct {
270+
public static func <<<(lhs: inout Self, rhs: Self) {
271+
lhs = rhs
272+
}
273+
}

test/Inputs/lazy_typecheck_client.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func testPublicStruct() {
3434
let _: Double = s.publicWrappedProperty
3535
let _: Double = s.$publicWrappedProperty.wrappedValue
3636
PublicStruct.publicStaticMethod()
37+
PublicStruct.activeMethod()
3738
}
3839

3940
func testPublicClass() {
@@ -50,6 +51,16 @@ func testPublicClass() {
5051
PublicDerivedClass.publicClassMethod()
5152
}
5253

54+
func testPublicEnum(_ e: PublicEnum) {
55+
switch e {
56+
case .a: ()
57+
case .b(let x): let _: Int = x
58+
}
59+
60+
let _: Int = e.publicMethod()
61+
let _: Int = e.publicComputedVar
62+
}
63+
5364
func testConformances() {
5465
let array: [any PublicProto] = [
5566
PublicStructConformingToPublicProto(),
@@ -79,3 +90,12 @@ func takesEmptyProto<T: EmptyPublicProto>(_ t: T) {}
7990
func testConditionalConformance<T>(_ s: PublicGenericStruct<T>) {
8091
takesEmptyProto(s) // expected-error {{global function 'takesEmptyProto' requires}}
8192
}
93+
94+
func testTypealiases() {
95+
let _: PublicStruct = PublicStructAlias(x: 1)
96+
}
97+
98+
func testOperators() {
99+
var a: PublicStruct
100+
a <<< PublicStruct(x: 2)
101+
}

0 commit comments

Comments
 (0)