Skip to content

Commit 74f7f31

Browse files
committed
Lift restrictions of access check for inlinalbe package symbols referenced in interfaces.
Package decls are only printed in interface files if they are inlinable (@usableFromInline, @inlinable, @_alwaysEmitIntoClient). They could be referenced by a module outside of its defining module that belong to the same package determined by the `package-name` flag. However, the flag is only in .swiftmodule and .private.swiftinterface, thus type checking references of inlinable package symbols in public interfaces fails due to the missing flag. Instead of adding the package-name flag to the public interfaces, which could raise a security concern, this PR grants access to such cases. Resolves rdar://116142791
1 parent a66f28e commit 74f7f31

File tree

4 files changed

+145
-15
lines changed

4 files changed

+145
-15
lines changed

include/swift/AST/Decl.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2684,8 +2684,29 @@ class ValueDecl : public Decl {
26842684

26852685
SourceLoc getNameLoc() const { return NameLoc; }
26862686

2687+
/// Returns \c true if this value decl is inlinable with attributes
2688+
/// \c \@usableFromInline, \c \@inlinalbe, and \c \@_alwaysEmitIntoClient
26872689
bool isUsableFromInline() const;
26882690

2691+
/// Returns \c true if this value decl needs a special case handling for an
2692+
/// interface file.
2693+
///
2694+
/// One such case is a reference of an inlinable decl with a `package` access level
2695+
/// in an interface file as follows: Package decls are only printed in interface files if
2696+
/// they are inlinable (as defined in \c isUsableFromInline). They could be
2697+
/// referenced by a module outside of its defining module that belong to the same
2698+
/// package determined by the `package-name` flag. However, the flag is only in
2699+
/// .swiftmodule and .private.swiftinterface, thus type checking references of inlinable
2700+
/// package symbols in public interfaces fails due to the missing flag.
2701+
/// Instead of adding the package-name flag to the public interfaces, which
2702+
/// could raise a security concern, we grant access to such cases.
2703+
///
2704+
/// \sa useDC The use site where this value decl is referenced.
2705+
/// \sa useAcl The access level of its use site.
2706+
/// \sa declScope The access scope of this decl site.
2707+
bool skipAccessCheckIfInterface(const DeclContext *useDC, AccessLevel useAcl,
2708+
AccessScope declScope) const;
2709+
26892710
/// Returns \c true if this declaration is *not* intended to be used directly
26902711
/// by application developers despite the visibility.
26912712
bool shouldHideFromEditor() const;

lib/AST/Decl.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3818,6 +3818,17 @@ bool ValueDecl::isUsableFromInline() const {
38183818
return false;
38193819
}
38203820

3821+
bool ValueDecl::skipAccessCheckIfInterface(const DeclContext *useDC,
3822+
AccessLevel useAcl,
3823+
AccessScope declScope) const {
3824+
if (!useDC || useAcl != AccessLevel::Package || !declScope.isPackage() ||
3825+
!isUsableFromInline() ||
3826+
getDeclContext()->getParentModule() == useDC->getParentModule())
3827+
return false;
3828+
auto useSF = useDC->getParentSourceFile();
3829+
return useSF && useSF->Kind == SourceFileKind::Interface;
3830+
}
3831+
38213832
bool ValueDecl::shouldHideFromEditor() const {
38223833
// Hide private stdlib declarations.
38233834
if (isPrivateStdlibDecl(/*treatNonBuiltinProtocolsAsPublic*/ false) ||
@@ -4154,8 +4165,13 @@ static bool checkAccessUsingAccessScopes(const DeclContext *useDC,
41544165
VD, access, useDC,
41554166
/*treatUsableFromInlineAsPublic*/ includeInlineable);
41564167
if (accessScope.getDeclContext() == useDC) return true;
4157-
if (!AccessScope(useDC).isChildOf(accessScope)) return false;
4158-
4168+
if (!AccessScope(useDC).isChildOf(accessScope)) {
4169+
// Grant access if this VD is an inlinable package decl referenced by
4170+
// another module in an interface file.
4171+
if (VD->skipAccessCheckIfInterface(useDC, access, accessScope))
4172+
return true;
4173+
return false;
4174+
}
41594175
// useDC is null only when caller wants to skip non-public type checks.
41604176
if (!useDC) return true;
41614177

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4372,6 +4372,10 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
43724372
requiredAccessScope.requiredAccessForDiagnostics();
43734373
auto proto = conformance->getProtocol();
43744374
auto protoAccessScope = proto->getFormalAccessScope(DC);
4375+
// Skip diagnostics of a witness of a package protocol that is inlinalbe
4376+
// referenced in an interface file.
4377+
if (proto->skipAccessCheckIfInterface(DC, requiredAccess, protoAccessScope))
4378+
return;
43754379
bool protoForcesAccess =
43764380
requiredAccessScope.hasEqualDeclContextWith(protoAccessScope);
43774381
auto diagKind = protoForcesAccess

test/Sema/accessibility_package_interface.swift

Lines changed: 102 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,118 @@
1010
// RUN: -emit-private-module-interface-path %t/Utils.private.swiftinterface
1111

1212
// RUN: %target-swift-typecheck-module-from-interface(%t/Utils.swiftinterface) -I %t
13-
// RUN: %FileCheck %s --check-prefix=CHECK-PUBLIC < %t/Utils.swiftinterface
14-
// CHECK-PUBLIC-NOT: -package-name swift-utils.log
15-
// CHECK-PUBLIC-NOT: package func packageFunc()
16-
// CHECK-PUBLIC: -module-name Utils
17-
// CHECK-PUBLIC: public func publicFunc()
13+
// RUN: %FileCheck %s --check-prefix=CHECK-PUBLIC-UTILS < %t/Utils.swiftinterface
14+
15+
// CHECK-PUBLIC-UTILS-NOT: -package-name swift-utils.log
16+
// CHECK-PUBLIC-UTILS-NOT: package func packageFunc()
17+
// CHECK-PUBLIC-UTILS-NOT: package protocol PackageProto
18+
// CHECK-PUBLIC-UTILS-NOT: var pkgVar
19+
// CHECK-PUBLIC-UTILS-NOT: package class PackageKlass
20+
// CHECK-PUBLIC-UTILS-NOT: package var pkgVar
21+
// CHECK-PUBLIC-UTILS: -module-name Utils
22+
// CHECK-PUBLIC-UTILS: public func publicFunc()
23+
// CHECK-PUBLIC-UTILS: @usableFromInline
24+
// CHECK-PUBLIC-UTILS: package func ufiPackageFunc()
25+
// CHECK-PUBLIC-UTILS: @usableFromInline
26+
// CHECK-PUBLIC-UTILS: package protocol UfiPackageProto
27+
// CHECK-PUBLIC-UTILS: var ufiPkgVar
28+
// CHECK-PUBLIC-UTILS: @usableFromInline
29+
// CHECK-PUBLIC-UTILS: package class UfiPackageKlass
30+
// CHECK-PUBLIC-UTILS: @usableFromInline
31+
// CHECK-PUBLIC-UTILS: package var ufiPkgVar
1832

1933
// RUN: %target-swift-typecheck-module-from-interface(%t/Utils.private.swiftinterface) -module-name Utils -I %t
20-
// RUN: %FileCheck %s --check-prefix=CHECK-PRIVATE < %t/Utils.private.swiftinterface
34+
// RUN: %FileCheck %s --check-prefix=CHECK-PRIVATE-UTILS < %t/Utils.private.swiftinterface
35+
36+
// CHECK-PRIVATE-UTILS-NOT: package func packageFunc()
37+
// CHECK-PRIVATE-UTILS-NOT: package protocol PackageProto
38+
// CHECK-PRIVATE-UTILS-NOT: var pkgVar
39+
// CHECK-PRIVATE-UTILS-NOT: package class PackageKlass
40+
// CHECK-PRIVATE-UTILS-NOT: package var pkgVar
41+
// CHECK-PRIVATE-UTILS: -module-name Utils
42+
// CHECK-PRIVATE-UTILS: swift-module-flags-ignorable-private: -package-name swift-utils.log
43+
// CHECK-PRIVATE-UTILS: public func publicFunc()
44+
// CHECK-PRIVATE-UTILS: @usableFromInline
45+
// CHECK-PRIVATE-UTILS: package func ufiPackageFunc()
46+
// CHECK-PRIVATE-UTILS: @usableFromInline
47+
// CHECK-PRIVATE-UTILS: package protocol UfiPackageProto
48+
// CHECK-PRIVATE-UTILS: var ufiPkgVar
49+
// CHECK-PRIVATE-UTILS: @usableFromInline
50+
// CHECK-PRIVATE-UTILS: package class UfiPackageKlass
51+
// CHECK-PRIVATE-UTILS: @usableFromInline
52+
// CHECK-PRIVATE-UTILS: package var ufiPkgVar
53+
54+
// RUN: %target-swift-frontend -emit-module %t/Client.swift \
55+
// RUN: -module-name Client -swift-version 5 -I %t \
56+
// RUN: -package-name swift-utils.log \
57+
// RUN: -enable-library-evolution \
58+
// RUN: -emit-module-path %t/Client.swiftmodule \
59+
// RUN: -emit-module-interface-path %t/Client.swiftinterface \
60+
// RUN: -emit-private-module-interface-path %t/Client.private.swiftinterface
61+
62+
// RUN: rm -rf %t/Utils.swiftmodule
63+
// RUN: rm -rf %t/Client.swiftmodule
2164

22-
// CHECK-PRIVATE-NOT: package func packageFunc()
23-
// CHECK-PRIVATE: swift-module-flags-ignorable-private: -package-name swift-utils.log
24-
// CHECK-PRIVATE: public func publicFunc()
65+
// RUN: %target-swift-typecheck-module-from-interface(%t/Client.swiftinterface) -I %t -verify
66+
// RUN: %FileCheck %s --check-prefix=CHECK-PUBLIC-CLIENT < %t/Client.swiftinterface
67+
// CHECK-PUBLIC-CLIENT-NOT: -package-name swift-utils.log
68+
// CHECK-PUBLIC-CLIENT: @inlinable public func clientFunc()
69+
// CHECK-PUBLIC-CLIENT: publicFunc()
70+
// CHECK-PUBLIC-CLIENT: ufiPackageFunc()
71+
// CHECK-PUBLIC-CLIENT: let u = UfiPackageKlass()
72+
// CHECK-PUBLIC-CLIENT: return u.ufiPkgVar
73+
// CHECK-PUBLIC-CLIENT: public class ClientKlass1 : Utils.UfiPackageProto
74+
// CHECK-PUBLIC-CLIENT: @usableFromInline
75+
// CHECK-PUBLIC-CLIENT: package var ufiPkgVar: Swift.String
76+
// CHECK-PUBLIC-CLIENT: public class ClientKlass2 : Utils.UfiPackageProto
77+
// CHECK-PUBLIC-CLIENT: public var ufiPkgVar: Swift.String
78+
79+
// RUN: %target-swift-typecheck-module-from-interface(%t/Client.private.swiftinterface) -module-name Client -I %t -verify
2580

26-
// RUN: %target-swift-frontend -typecheck %t/Client.swift -package-name swift-utils.log -I %t -verify
2781

2882
//--- Utils.swift
29-
package func packageFunc() {}
3083
public func publicFunc() {}
3184

85+
package func packageFunc() {}
86+
@usableFromInline
87+
package func ufiPackageFunc() {}
88+
89+
package protocol PackageProto {
90+
var pkgVar: String { get set }
91+
}
92+
package class PackageKlass: PackageProto {
93+
package var pkgVar = ""
94+
}
95+
96+
@usableFromInline
97+
package protocol UfiPackageProto {
98+
var ufiPkgVar: String { get set }
99+
}
100+
101+
@usableFromInline
102+
package class UfiPackageKlass: UfiPackageProto {
103+
@usableFromInline
104+
package init() {}
105+
@usableFromInline
106+
package var ufiPkgVar = ""
107+
}
108+
109+
32110
//--- Client.swift
33111
import Utils
34112

35-
func clientFunc() {
36-
packageFunc()
113+
@inlinable public func clientFunc() -> String {
37114
publicFunc()
115+
ufiPackageFunc()
116+
let u = UfiPackageKlass()
117+
return u.ufiPkgVar
118+
}
119+
120+
public class ClientKlass1: UfiPackageProto {
121+
@usableFromInline
122+
package var ufiPkgVar = "B"
123+
}
124+
125+
public class ClientKlass2: UfiPackageProto {
126+
public var ufiPkgVar = "C"
38127
}

0 commit comments

Comments
 (0)