Skip to content

[5.9] Limit loading error when importing a module with package name built from interface #65854

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 23, 2023
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
25 changes: 24 additions & 1 deletion lib/Sema/ImportResolution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ struct UnboundImport {
void validateResilience(NullablePtr<ModuleDecl> topLevelModule,
SourceFile &SF);
void validateAllowableClient(ModuleDecl *topLevelModule, SourceFile &SF);
void validateInterfaceWithPackageName(ModuleDecl *topLevelModule, SourceFile &SF);

/// Diagnoses an inability to import \p modulePath in this situation and, if
/// \p attrs is provided and has an \p attrKind, invalidates the attribute and
Expand Down Expand Up @@ -652,8 +653,8 @@ void UnboundImport::validateOptions(NullablePtr<ModuleDecl> topLevelModule,
validateTestable(top);
validatePrivate(top);
validateAllowableClient(top, SF);
validateInterfaceWithPackageName(top, SF);
}

validateResilience(topLevelModule, SF);
}

Expand Down Expand Up @@ -756,6 +757,28 @@ void UnboundImport::validateAllowableClient(ModuleDecl *importee,
}
}

void UnboundImport::validateInterfaceWithPackageName(ModuleDecl *topLevelModule,
SourceFile &SF) {
assert(topLevelModule);

// If current source file is interface, don't throw an error
if (SF.Kind == SourceFileKind::Interface)
return;

// If source file is .swift or non-interface, show diags when importing an interface file
ASTContext &ctx = topLevelModule->getASTContext();
if (!topLevelModule->getPackageName().empty() &&
topLevelModule->getPackageName().str() == ctx.LangOpts.PackageName &&
topLevelModule->isBuiltFromInterface()) {
ctx.Diags.diagnose(SourceLoc(),
diag::in_package_module_not_compiled_from_source,
topLevelModule->getBaseIdentifier(),
ctx.LangOpts.PackageName,
topLevelModule->getModuleSourceFilename()
);
}
}

void UnboundImport::validateResilience(NullablePtr<ModuleDecl> topLevelModule,
SourceFile &SF) {
if (import.options.contains(ImportFlags::ImplementationOnly) ||
Expand Down
9 changes: 0 additions & 9 deletions lib/Serialization/SerializedModuleLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -825,15 +825,6 @@ LoadedFile *SerializedModuleLoaderBase::loadAST(
if (loadedModuleFile->isConcurrencyChecked())
M.setIsConcurrencyChecked();
if (!loadedModuleFile->getModulePackageName().empty()) {
if (loadedModuleFile->isBuiltFromInterface() &&
loadedModuleFile->getModulePackageName().str() == Ctx.LangOpts.PackageName) {
Ctx.Diags.diagnose(SourceLoc(),
diag::in_package_module_not_compiled_from_source,
M.getBaseIdentifier(),
Ctx.LangOpts.PackageName,
loadedModuleFile->getModuleSourceFilename()
);
}
M.setPackageName(Ctx.getIdentifier(loadedModuleFile->getModulePackageName()));
}
M.setUserModuleVersion(loadedModuleFile->getUserModuleVersion());
Expand Down
149 changes: 149 additions & 0 deletions test/Sema/accessibility_package_import_interface.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// RUN: %empty-directory(%t)
// RUN: split-file %s %t

// TEST Build Dep with package symbols
// RUN: %target-swift-frontend -emit-module %t/Dep.swift \
// RUN: -module-name Dep -swift-version 5 -I %t \
// RUN: -package-name myPkg \
// RUN: -enable-library-evolution \
// RUN: -emit-module-path %t/Dep.swiftmodule \
// RUN: -emit-module-interface-path %t/Dep.swiftinterface \
// RUN: -emit-private-module-interface-path %t/Dep.private.swiftinterface

// TEST Dep private interface should contain the package name
// RUN: %target-swift-typecheck-module-from-interface(%t/Dep.private.swiftinterface) -module-name Dep -I %t
// RUN: %FileCheck %s --check-prefix=CHECK-DEP-PRIVATE < %t/Dep.private.swiftinterface
// CHECK-DEP-PRIVATE: -package-name myPkg

// TEST Dep.swiftmodule should contain package name and package symbols
// RUN: llvm-bcanalyzer --dump %t/Dep.swiftmodule | %FileCheck %s --check-prefix=CHECK-DEP-BC
// CHECK-DEP-BC: <MODULE_PACKAGE_NAME abbrevid=6/> blob data = 'myPkg'

// TEST Lib should load Dep.swiftmodule and access package decls if in the same package and error if not

// RUN: %target-swift-frontend -typecheck %t/Lib.swift -package-name myPkg -I %t -verify

// RUN: not %target-swift-frontend -typecheck %t/Lib.swift -package-name otherPkg -I %t -Rmodule-loading 2> %t/result-binary-other-pkg.output
// RUN: %FileCheck %s --check-prefix=CHECK-DIFF-PKG < %t/result-binary-other-pkg.output

// RUN: not %target-swift-frontend -typecheck %t/Lib.swift -I %t -Rmodule-loading 2> %t/result-binary-no-pkg.output
// RUN: %FileCheck %s --check-prefix=CHECK-DIFF-PKG < %t/result-binary-no-pkg.output

// CHECK-DIFF-PKG: remark: loaded module 'Dep'
// CHECK-DIFF-PKG: error: cannot find 'packageFuncInlinable' in scope
// CHECK-DIFF-PKG: error: cannot find 'packageFunc' in scope
// CHECK-DIFF-PKG: error: cannot find 'PackageKlassUFI' in scope

// TEST Remove Dep binary and build it from interface
// RUN: rm %t/Dep.swiftmodule
// RUN: %target-swift-frontend -compile-module-from-interface %t/Dep.private.swiftinterface \
// RUN: -module-name Dep -I %t \
// RUN: -o %t/Dep.swiftmodule

// TEST Dep binary built from interface should contain package name but no package symbols
// RUN: llvm-bcanalyzer --dump %t/Dep.swiftmodule | %FileCheck %s --check-prefix=CHECK-DEP-INTER-BC
// CHECK-DEP-INTER-BC: <MODULE_PACKAGE_NAME abbrevid=7/> blob data = 'myPkg'

// TEST Lib should error on loading Dep built from interface and accessing package symbols (unless usableFromInline or inlinable)
// RUN: not %target-swift-frontend -typecheck %t/Lib.swift -package-name myPkg -I %t 2> %t/result-access.output
// RUN: %FileCheck %s --check-prefix CHECK-LIB < %t/result-access.output
// CHECK-LIB: error: module 'Dep' is in package 'myPkg' but was built from interface; modules of the same package can only be loaded if built from source
// CHECK-LIB: error: cannot find 'packageFunc' in scope
// CHECK-LIB: error: value of type 'PackageKlassUFI' has no member 'packageVar'

// TEST Remove and rebuild Dep from source
// RUN: rm %t/Dep.swiftmodule
// RUN: %target-swift-frontend -emit-module %t/Dep.swift \
// RUN: -module-name Dep -swift-version 5 -I %t \
// RUN: -package-name myPkg \
// RUN: -enable-library-evolution \
// RUN: -emit-module-path %t/Dep.swiftmodule

// TEST Build LibPass with package name
// RUN: %target-swift-frontend -emit-module %t/LibPass.swift \
// RUN: -module-name LibPass -swift-version 5 -I %t \
// RUN: -package-name myPkg \
// RUN: -enable-library-evolution \
// RUN: -emit-module-path %t/LibPass.swiftmodule \
// RUN: -emit-module-interface-path %t/LibPass.swiftinterface \
// RUN: -emit-private-module-interface-path %t/LibPass.private.swiftinterface

// TEST Loading LibPass and accessing lib func should pass
// RUN: %target-swift-frontend -typecheck %t/Client.swift -package-name myPkg -I %t -verify

// TEST Building LibPass from interface with Dep (built from interface) should succeed with or without package name
// Without package name
// RUN: rm %t/Dep.swiftmodule
// RUN: rm %t/LibPass.swiftmodule
// RUN: %target-swift-frontend -compile-module-from-interface %t/LibPass.private.swiftinterface \
// RUN: -module-name LibPass -I %t \
// RUN: -o %t/LibPass.swiftmodule

// With package name
// RUN: rm %t/LibPass.swiftmodule
// RUN: %target-swift-frontend -compile-module-from-interface %t/LibPass.private.swiftinterface \
// RUN: -module-name LibPass -I %t \
// RUN: -package-name myPkg \
// RUN: -o %t/LibPass.swiftmodule


//--- Dep.swift
@usableFromInline
package class PackageKlassUFI {
@usableFromInline package init() {}
@usableFromInline package var packageVarUFI: String = "pkgUFI"
package var packageVar: String = "pkg"
}

package func packageFunc() {
print("package func")
}

@inlinable
package func packageFuncInlinable() {
print("inlinable package func")
}

public func publicFunc() {
print("public func")
}

@inlinable
public func publicFuncInlinable() {
print("inlinable public func")
}

//--- Lib.swift
import Dep

public func libFunc() {
publicFuncInlinable()
publicFunc()
packageFuncInlinable()
packageFunc()
let x = PackageKlassUFI()
let y = x.packageVarUFI
let z = x.packageVar
print(x, y, z)
}


//--- LibPass.swift
import Dep

public func libFunc() {
publicFuncInlinable()
publicFunc()
packageFuncInlinable()
let x = PackageKlassUFI()
let y = x.packageVarUFI
print(x, y)
}


//--- Client.swift
import LibPass

public func clientFunc() {
libFunc()
}
105 changes: 105 additions & 0 deletions test/api-digester/import-module-with-package-name.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// REQUIRES: VENDOR=apple

// RUN: %empty-directory(%t)
// RUN: %empty-directory(%t.mod)
// RUN: %empty-directory(%t.sdk)
// RUN: %empty-directory(%t.module-cache)
// RUN: split-file %s %t

// Make sure api-digester loads an interface with package-name correctly

// Generate module Lib
// RUN: %target-swift-frontend %t/Lib.swift -emit-module -module-name Lib -emit-module-path %t.mod/Lib.swiftmodule -emit-module-interface-path %t.mod/Lib.swiftinterface -emit-private-module-interface-path %t.mod/Lib.private.swiftinterface -package-name myLib -parse-as-library -enable-library-evolution -module-cache-path %t.module-cache -swift-version 5

// Dumping sdk for Lib ABI via .swiftmodule file should work
// RUN: %api-digester -dump-sdk -abort-on-module-fail -abi -module Lib -o - -module-cache-path %t.module-cache -I %t.mod > %t.dump-lib-binary.json
// RUN: %FileCheck -check-prefix=LIB-BINARY %s < %t.dump-lib-binary.json
// LIB-BINARY: PkgKlass
// LIB-BINARY: pkgFunc
// LIB-BINARY: PublicStruct
// LIB-BINARY: PkgKlassUFI
// LIB-BINARY: libFunc

// Dumping sdk file for Lib ABI via .swiftinterface file should work
// RUN: %api-digester -dump-sdk -abort-on-module-fail -abi -module Lib -use-interface-for-module Lib -o - -module-cache-path %t.module-cache -I %t.mod > %t.dump-lib-interface.json
// RUN: %FileCheck -check-prefix=LIB-INTERFACE %s < %t.dump-lib-interface.json
// LIB-INTERFACE-NOT: PkgKlass
// LIB-INTERFACE-NOT: pkgFunc
// LIB-INTERFACE: PublicStruct
// LIB-INTERFACE: PkgKlassUFI
// LIB-INTERFACE: libFunc

// Generate module Client
// RUN: %target-swift-frontend %t/Client.swift -emit-module -module-name Client -emit-module-path %t.mod/Client.swiftmodule -emit-module-interface-path %t.mod/Client.swiftinterface -emit-private-module-interface-path %t.mod/Client.private.swiftinterface -package-name myLib -parse-as-library -enable-library-evolution -module-cache-path %t.module-cache -swift-version 5 -I %t.mod

// RUN: rm -f %t.mod/Lib.swiftmodule
// RUN: rm -f %t.module-cache/Lib*.swiftmodule

// Dumping sdk for Client ABI via .swiftmodule file should work
// RUN: %api-digester -dump-sdk -abort-on-module-fail -abi -module Client -o - -module-cache-path %t.module-cache -I %t.mod > %t.dump-client-binary.json
// RUN: %FileCheck -check-prefix=CLIENT-BINARY %s < %t.dump-client-binary.json
// CLIENT-BINARY-NOT: PkgKlass
// CLIENT-BINARY-NOT: pkgFunc
// CLIENT-BINARY-NOT: PublicStruct
// CLIENT-BINARY-NOT: PkgKlassUFI
// CLIENT-BINARY-NOT: libFunc
// CLIENT-BINARY: clientFunc
// CLIENT-BINARY: clientFuncInlinable

// Dumping sdk for Client ABI via .swiftinterface file should work
// RUN: %api-digester -dump-sdk -abort-on-module-fail -abi -module Client -use-interface-for-module Client -o - -module-cache-path %t.module-cache -I %t.mod > %t.dump-client-interface.json
// RUN: %FileCheck -check-prefix=CLIENT-INTERFACE %s < %t.dump-client-interface.json
// CLIENT-INTERFACE-NOT: PkgKlass
// CLIENT-INTERFACE-NOT: pkgFunc
// CLIENT-INTERFACE-NOT: PublicStruct
// CLIENT-INTERFACE-NOT: PkgKlassUFI
// CLIENT-INTERFACE-NOT: libFunc
// CLIENT-INTERFACE: clientFunc
// CLIENT-INTERFACE: clientFuncInlinable


//--- Lib.swift
package class PkgKlass {
package class func foo() {}
package func foo2(_ : Int) {}
package weak var bar : PkgKlass?
package var bar2 : PkgKlass?
}

package func pkgFunc() -> (PkgKlass) -> () { return { _ in } }

public struct PublicStruct {
package init(_ : PkgKlass?) {}
public static func baz(_ arg: String?) {}
}

@usableFromInline
package struct PkgKlassUFI {
@usableFromInline
package init() {}
@usableFromInline
package func ufiFunc() {}
}

@inlinable
public func libFunc() {
PkgKlassUFI().ufiFunc()
}

//--- Client.swift
import Lib

public func clientFunc() {
PkgKlass.foo()
let result = pkgFunc()
let s = PublicStruct(nil)
PublicStruct.baz("")
print(s, result)
}

@inlinable
public func clientFuncInlinable() {
let x = PkgKlassUFI()
libFunc()
print(x)
}