Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2684,8 +2684,29 @@ class ValueDecl : public Decl {

SourceLoc getNameLoc() const { return NameLoc; }

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

/// Returns \c true if this value decl needs a special case handling for an
/// interface file.
///
/// One such case is a reference of an inlinable decl with a `package` access level
/// in an interface file as follows: Package decls are only printed in interface files if
/// they are inlinable (as defined in \c isUsableFromInline). 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, we grant access to such cases.
///
/// \sa useDC The use site where this value decl is referenced.
/// \sa useAcl The access level of its use site.
/// \sa declScope The access scope of this decl site.
bool skipAccessCheckIfInterface(const DeclContext *useDC, AccessLevel useAcl,
AccessScope declScope) const;

/// Returns \c true if this declaration is *not* intended to be used directly
/// by application developers despite the visibility.
bool shouldHideFromEditor() const;
Expand Down
20 changes: 18 additions & 2 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3818,6 +3818,17 @@ bool ValueDecl::isUsableFromInline() const {
return false;
}

bool ValueDecl::skipAccessCheckIfInterface(const DeclContext *useDC,
AccessLevel useAcl,
AccessScope declScope) const {
if (!useDC || useAcl != AccessLevel::Package || !declScope.isPackage() ||
!isUsableFromInline() ||
getDeclContext()->getParentModule() == useDC->getParentModule())
return false;
auto useSF = useDC->getParentSourceFile();
return useSF && useSF->Kind == SourceFileKind::Interface;
}

bool ValueDecl::shouldHideFromEditor() const {
// Hide private stdlib declarations.
if (isPrivateStdlibDecl(/*treatNonBuiltinProtocolsAsPublic*/ false) ||
Expand Down Expand Up @@ -4154,8 +4165,13 @@ static bool checkAccessUsingAccessScopes(const DeclContext *useDC,
VD, access, useDC,
/*treatUsableFromInlineAsPublic*/ includeInlineable);
if (accessScope.getDeclContext() == useDC) return true;
if (!AccessScope(useDC).isChildOf(accessScope)) return false;

if (!AccessScope(useDC).isChildOf(accessScope)) {
// Grant access if this VD is an inlinable package decl referenced by
// another module in an interface file.
if (VD->skipAccessCheckIfInterface(useDC, access, accessScope))
return true;
return false;
}
// useDC is null only when caller wants to skip non-public type checks.
if (!useDC) return true;

Expand Down
4 changes: 4 additions & 0 deletions lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4372,6 +4372,10 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
requiredAccessScope.requiredAccessForDiagnostics();
auto proto = conformance->getProtocol();
auto protoAccessScope = proto->getFormalAccessScope(DC);
// Skip diagnostics of a witness of a package protocol that is inlinalbe
// referenced in an interface file.
if (proto->skipAccessCheckIfInterface(DC, requiredAccess, protoAccessScope))
return;
bool protoForcesAccess =
requiredAccessScope.hasEqualDeclContextWith(protoAccessScope);
auto diagKind = protoForcesAccess
Expand Down
115 changes: 102 additions & 13 deletions test/Sema/accessibility_package_interface.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,118 @@
// RUN: -emit-private-module-interface-path %t/Utils.private.swiftinterface

// RUN: %target-swift-typecheck-module-from-interface(%t/Utils.swiftinterface) -I %t
// RUN: %FileCheck %s --check-prefix=CHECK-PUBLIC < %t/Utils.swiftinterface
// CHECK-PUBLIC-NOT: -package-name swift-utils.log
// CHECK-PUBLIC-NOT: package func packageFunc()
// CHECK-PUBLIC: -module-name Utils
// CHECK-PUBLIC: public func publicFunc()
// RUN: %FileCheck %s --check-prefix=CHECK-PUBLIC-UTILS < %t/Utils.swiftinterface

// CHECK-PUBLIC-UTILS-NOT: -package-name swift-utils.log
// CHECK-PUBLIC-UTILS-NOT: package func packageFunc()
// CHECK-PUBLIC-UTILS-NOT: package protocol PackageProto
// CHECK-PUBLIC-UTILS-NOT: var pkgVar
// CHECK-PUBLIC-UTILS-NOT: package class PackageKlass
// CHECK-PUBLIC-UTILS-NOT: package var pkgVar
// CHECK-PUBLIC-UTILS: -module-name Utils
// CHECK-PUBLIC-UTILS: public func publicFunc()
// CHECK-PUBLIC-UTILS: @usableFromInline
// CHECK-PUBLIC-UTILS: package func ufiPackageFunc()
// CHECK-PUBLIC-UTILS: @usableFromInline
// CHECK-PUBLIC-UTILS: package protocol UfiPackageProto
// CHECK-PUBLIC-UTILS: var ufiPkgVar
// CHECK-PUBLIC-UTILS: @usableFromInline
// CHECK-PUBLIC-UTILS: package class UfiPackageKlass
// CHECK-PUBLIC-UTILS: @usableFromInline
// CHECK-PUBLIC-UTILS: package var ufiPkgVar

// RUN: %target-swift-typecheck-module-from-interface(%t/Utils.private.swiftinterface) -module-name Utils -I %t
// RUN: %FileCheck %s --check-prefix=CHECK-PRIVATE < %t/Utils.private.swiftinterface
// RUN: %FileCheck %s --check-prefix=CHECK-PRIVATE-UTILS < %t/Utils.private.swiftinterface

// CHECK-PRIVATE-UTILS-NOT: package func packageFunc()
// CHECK-PRIVATE-UTILS-NOT: package protocol PackageProto
// CHECK-PRIVATE-UTILS-NOT: var pkgVar
// CHECK-PRIVATE-UTILS-NOT: package class PackageKlass
// CHECK-PRIVATE-UTILS-NOT: package var pkgVar
// CHECK-PRIVATE-UTILS: -module-name Utils
// CHECK-PRIVATE-UTILS: swift-module-flags-ignorable-private: -package-name swift-utils.log
// CHECK-PRIVATE-UTILS: public func publicFunc()
// CHECK-PRIVATE-UTILS: @usableFromInline
// CHECK-PRIVATE-UTILS: package func ufiPackageFunc()
// CHECK-PRIVATE-UTILS: @usableFromInline
// CHECK-PRIVATE-UTILS: package protocol UfiPackageProto
// CHECK-PRIVATE-UTILS: var ufiPkgVar
// CHECK-PRIVATE-UTILS: @usableFromInline
// CHECK-PRIVATE-UTILS: package class UfiPackageKlass
// CHECK-PRIVATE-UTILS: @usableFromInline
// CHECK-PRIVATE-UTILS: package var ufiPkgVar

// RUN: %target-swift-frontend -emit-module %t/Client.swift \
// RUN: -module-name Client -swift-version 5 -I %t \
// RUN: -package-name swift-utils.log \
// RUN: -enable-library-evolution \
// RUN: -emit-module-path %t/Client.swiftmodule \
// RUN: -emit-module-interface-path %t/Client.swiftinterface \
// RUN: -emit-private-module-interface-path %t/Client.private.swiftinterface

// RUN: rm -rf %t/Utils.swiftmodule
// RUN: rm -rf %t/Client.swiftmodule

// CHECK-PRIVATE-NOT: package func packageFunc()
// CHECK-PRIVATE: swift-module-flags-ignorable-private: -package-name swift-utils.log
// CHECK-PRIVATE: public func publicFunc()
// RUN: %target-swift-typecheck-module-from-interface(%t/Client.swiftinterface) -I %t -verify
// RUN: %FileCheck %s --check-prefix=CHECK-PUBLIC-CLIENT < %t/Client.swiftinterface
// CHECK-PUBLIC-CLIENT-NOT: -package-name swift-utils.log
// CHECK-PUBLIC-CLIENT: @inlinable public func clientFunc()
// CHECK-PUBLIC-CLIENT: publicFunc()
// CHECK-PUBLIC-CLIENT: ufiPackageFunc()
// CHECK-PUBLIC-CLIENT: let u = UfiPackageKlass()
// CHECK-PUBLIC-CLIENT: return u.ufiPkgVar
// CHECK-PUBLIC-CLIENT: public class ClientKlass1 : Utils.UfiPackageProto
// CHECK-PUBLIC-CLIENT: @usableFromInline
// CHECK-PUBLIC-CLIENT: package var ufiPkgVar: Swift.String
// CHECK-PUBLIC-CLIENT: public class ClientKlass2 : Utils.UfiPackageProto
// CHECK-PUBLIC-CLIENT: public var ufiPkgVar: Swift.String

// RUN: %target-swift-typecheck-module-from-interface(%t/Client.private.swiftinterface) -module-name Client -I %t -verify

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

//--- Utils.swift
package func packageFunc() {}
public func publicFunc() {}

package func packageFunc() {}
@usableFromInline
package func ufiPackageFunc() {}

package protocol PackageProto {
var pkgVar: String { get set }
}
package class PackageKlass: PackageProto {
package var pkgVar = ""
}

@usableFromInline
package protocol UfiPackageProto {
var ufiPkgVar: String { get set }
}

@usableFromInline
package class UfiPackageKlass: UfiPackageProto {
@usableFromInline
package init() {}
@usableFromInline
package var ufiPkgVar = ""
}


//--- Client.swift
import Utils

func clientFunc() {
packageFunc()
@inlinable public func clientFunc() -> String {
publicFunc()
ufiPackageFunc()
let u = UfiPackageKlass()
return u.ufiPkgVar
}

public class ClientKlass1: UfiPackageProto {
@usableFromInline
package var ufiPkgVar = "B"
}

public class ClientKlass2: UfiPackageProto {
public var ufiPkgVar = "C"
}