Skip to content

Commit 67842e3

Browse files
committed
Grant access to 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 1470023 commit 67842e3

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
@@ -2704,8 +2704,29 @@ class ValueDecl : public Decl {
27042704

27052705
SourceLoc getNameLoc() const { return NameLoc; }
27062706

2707+
/// Returns \c true if this value decl is inlinable with attributes
2708+
/// \c \@usableFromInline, \c \@inlinalbe, and \c \@_alwaysEmitIntoClient
27072709
bool isUsableFromInline() const;
27082710

2711+
/// Returns \c true if this value decl needs a special case handling for an
2712+
/// interface file.
2713+
///
2714+
/// One such case is a reference of an inlinable decl with a `package` access level
2715+
/// in an interface file as follows: Package decls are only printed in interface files if
2716+
/// they are inlinable (as defined in \c isUsableFromInline). They could be
2717+
/// referenced by a module outside of its defining module that belong to the same
2718+
/// package determined by the `package-name` flag. However, the flag is only in
2719+
/// .swiftmodule and .private.swiftinterface, thus type checking references of inlinable
2720+
/// package symbols in public interfaces fails due to the missing flag.
2721+
/// Instead of adding the package-name flag to the public interfaces, which
2722+
/// could raise a security concern, we grant access to such cases.
2723+
///
2724+
/// \sa useDC The use site where this value decl is referenced.
2725+
/// \sa useAcl The access level of its use site.
2726+
/// \sa declScope The access scope of this decl site.
2727+
bool skipAccessCheckIfInterface(const DeclContext *useDC, AccessLevel useAcl,
2728+
AccessScope declScope) const;
2729+
27092730
/// Returns \c true if this declaration is *not* intended to be used directly
27102731
/// by application developers despite the visibility.
27112732
bool shouldHideFromEditor() const;

lib/AST/Decl.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3827,6 +3827,18 @@ bool ValueDecl::isUsableFromInline() const {
38273827
return false;
38283828
}
38293829

3830+
bool ValueDecl::skipAccessCheckIfInterface(const DeclContext *useDC,
3831+
AccessLevel useAcl,
3832+
AccessScope declScope) const {
3833+
if (!useDC || useAcl != AccessLevel::Package || !declScope.isPackage() ||
3834+
!isUsableFromInline() ||
3835+
getDeclContext()->getParentModule()->getBaseIdentifier() ==
3836+
useDC->getParentModule()->getBaseIdentifier())
3837+
return false;
3838+
auto useSF = useDC->getParentSourceFile();
3839+
return useSF && !useSF->isScriptMode();
3840+
}
3841+
38303842
bool ValueDecl::shouldHideFromEditor() const {
38313843
// Hide private stdlib declarations.
38323844
if (isPrivateStdlibDecl(/*treatNonBuiltinProtocolsAsPublic*/ false) ||
@@ -4158,8 +4170,13 @@ static bool checkAccessUsingAccessScopes(const DeclContext *useDC,
41584170
VD, access, useDC,
41594171
/*treatUsableFromInlineAsPublic*/ includeInlineable);
41604172
if (accessScope.getDeclContext() == useDC) return true;
4161-
if (!AccessScope(useDC).isChildOf(accessScope)) return false;
4162-
4173+
if (!AccessScope(useDC).isChildOf(accessScope)) {
4174+
// Grant access if this VD is an inlinable package decl referenced by
4175+
// another module in an interface file.
4176+
if (VD->skipAccessCheckIfInterface(useDC, access, accessScope))
4177+
return true;
4178+
return false;
4179+
}
41634180
// useDC is null only when caller wants to skip non-public type checks.
41644181
if (!useDC) return true;
41654182

lib/Sema/TypeCheckProtocol.cpp

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

test/Sema/accessibility_package_interface.swift

Lines changed: 101 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,117 @@
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/Client.swiftmodule
2163

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

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

2881
//--- Utils.swift
29-
package func packageFunc() {}
3082
public func publicFunc() {}
3183

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

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

0 commit comments

Comments
 (0)