Skip to content

Commit e6ebd14

Browse files
committed
[ModuleInterface] Don't print extensions to implementation-only imported types
Extensions to implementation-only types are accepted at type-checking only if they don't define any public members. However if they declared a conformance to a public type they were also printed in the swiftinterface, making it unparsable because of an unknown type. Still accept such extensions but don't print them. rdar://problem/67516588
1 parent aa22d6f commit e6ebd14

File tree

6 files changed

+92
-0
lines changed

6 files changed

+92
-0
lines changed

include/swift/AST/Module.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,12 @@ class ModuleDecl : public DeclContext, public TypeDecl {
612612
void
613613
getImportedModulesForLookup(SmallVectorImpl<ImportedModule> &imports) const;
614614

615+
/// Has \p module been imported via an '@_implementationOnly' import
616+
/// instead of another kind of import?
617+
///
618+
/// This assumes that \p module was imported.
619+
bool isImportedImplementationOnly(const ModuleDecl *module) const;
620+
615621
/// Uniques the items in \p imports, ignoring the source locations of the
616622
/// access paths.
617623
///

lib/AST/ASTPrinter.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,14 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(bool preferTypeRepr,
168168
if (auto *ED = dyn_cast<ExtensionDecl>(D)) {
169169
if (!shouldPrint(ED->getExtendedNominal(), options))
170170
return false;
171+
172+
// Skip extensions to implementation-only imported types.
173+
auto localModule = ED->getParentModule();
174+
auto nominalModule = ED->getExtendedNominal()->getParentModule();
175+
if (localModule != nominalModule &&
176+
localModule->isImportedImplementationOnly(nominalModule))
177+
return false;
178+
171179
for (const Requirement &req : ED->getGenericRequirements()) {
172180
if (!isPublicOrUsableFromInline(req.getFirstType()))
173181
return false;

lib/AST/Module.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2191,6 +2191,27 @@ bool SourceFile::isImportedImplementationOnly(const ModuleDecl *module) const {
21912191
return !imports.isImportedBy(module, getParentModule());
21922192
}
21932193

2194+
bool ModuleDecl::isImportedImplementationOnly(const ModuleDecl *module) const {
2195+
auto &imports = getASTContext().getImportCache();
2196+
2197+
// Look through non-implementation-only imports to see if module is imported
2198+
// in some other way. Otherwise we assume it's implementation-only imported.
2199+
ModuleDecl::ImportFilter filter;
2200+
filter |= ModuleDecl::ImportFilterKind::Public;
2201+
filter |= ModuleDecl::ImportFilterKind::Private;
2202+
filter |= ModuleDecl::ImportFilterKind::SPIAccessControl;
2203+
filter |= ModuleDecl::ImportFilterKind::ShadowedBySeparateOverlay;
2204+
SmallVector<ModuleDecl::ImportedModule, 4> results;
2205+
getImportedModules(results, filter);
2206+
2207+
for (auto &desc : results) {
2208+
if (imports.isImportedBy(module, desc.second))
2209+
return false;
2210+
}
2211+
2212+
return true;
2213+
}
2214+
21942215
void SourceFile::lookupImportedSPIGroups(
21952216
const ModuleDecl *importedModule,
21962217
llvm::SmallSetVector<Identifier, 4> &spiGroups) const {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Test that we don't print extensions to implementation-only imported types.
2+
3+
// RUN: %empty-directory(%t)
4+
5+
// RUN: %target-swift-frontend -emit-module %s -DIOI_LIB -module-name IOILib -emit-module-path %t/IOILib.swiftmodule
6+
// RUN: %target-swift-frontend -emit-module %s -DEXPORTED_LIB -module-name IndirectLib -emit-module-path %t/IndirectLib.swiftmodule -I %t
7+
// RUN: %target-swift-frontend -emit-module %s -DLIB -module-name Lib -emit-module-path %t/Lib.swiftmodule -I %t
8+
9+
// RUN: %target-swift-frontend-typecheck -emit-module-interface-path %t/out.swiftinterface %s -I %t -swift-version 5 -enable-library-evolution
10+
// RUN: %FileCheck %s < %t/out.swiftinterface
11+
12+
#if IOI_LIB
13+
14+
public struct IOIImportedType {
15+
public func foo() {}
16+
}
17+
18+
#elseif EXPORTED_LIB
19+
20+
public struct ExportedType {
21+
public func foo() {}
22+
}
23+
24+
#elseif LIB
25+
26+
@_exported import IndirectLib
27+
28+
public struct NormalImportedType {
29+
public func foo() {}
30+
}
31+
32+
#else // Client
33+
34+
import Lib
35+
@_implementationOnly import IOILib
36+
37+
public protocol PublicProto {
38+
func foo()
39+
}
40+
extension IOIImportedType : PublicProto {}
41+
// CHECK-NOT: IOIImportedType
42+
43+
extension NormalImportedType : PublicProto {}
44+
// CHECK: extension NormalImportedType
45+
46+
extension ExportedType : PublicProto {}
47+
// CHECK: extension ExportedType
48+
49+
#endif

test/SPI/Inputs/ioi_helper.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
public struct IOIPublicStruct {}

test/SPI/private_swiftinterface.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
// RUN: %empty-directory(%t)
55
// RUN: %target-swift-frontend -emit-module %S/Inputs/spi_helper.swift -module-name SPIHelper -emit-module-path %t/SPIHelper.swiftmodule -swift-version 5 -enable-library-evolution -emit-module-interface-path %t/SPIHelper.swiftinterface -emit-private-module-interface-path %t/SPIHelper.private.swiftinterface
6+
// RUN: %target-swift-frontend -emit-module %S/Inputs/ioi_helper.swift -module-name IOIHelper -emit-module-path %t/IOIHelper.swiftmodule -swift-version 5 -enable-library-evolution -emit-module-interface-path %t/IOIHelper.swiftinterface -emit-private-module-interface-path %t/IOIHelper.private.swiftinterface
67

78
/// Make sure that the public swiftinterface of spi_helper doesn't leak SPI.
89
// RUN: %FileCheck -check-prefix=CHECK-HELPER %s < %t/SPIHelper.swiftinterface
@@ -25,6 +26,7 @@
2526
// CHECK-PUBLIC: import SPIHelper
2627
// CHECK-PRIVATE: @_spi(OtherSPI) @_spi(HelperSPI) import SPIHelper
2728

29+
@_implementationOnly import IOIHelper
2830
public func foo() {}
2931
// CHECK-PUBLIC: foo()
3032
// CHECK-PRIVATE: foo()
@@ -155,6 +157,11 @@ extension PublicType: SPIProto2 where T: SPIProto2 {}
155157
// CHECK-PRIVATE: extension PublicType : {{.*}}.SPIProto2 where T : {{.*}}.SPIProto2
156158
// CHECK-PUBLIC-NOT: _ConstraintThatIsNotPartOfTheAPIOfThisLibrary
157159

160+
public protocol LocalPublicProto {}
161+
extension IOIPublicStruct : LocalPublicProto {}
162+
// CHECK-PRIVATE-NOT: IOIPublicStruct
163+
// CHECK-PUBLIC-NOT: IOIPublicStruct
164+
158165
// The dummy conformance should be only in the private swiftinterface for
159166
// SPI extensions.
160167
@_spi(LocalSPI)

0 commit comments

Comments
 (0)