Skip to content

Commit 5a49e34

Browse files
committed
Serialization: Error on xref to implementation-only dependencies
Introduce a last resort check reporting references to implementation-only dependencies that would appear in the generated swiftmodule. This check is applied at serialization, long after exportability checking applied at typechecking. It should act as a back stop to references missed by typechecking or @_implementationOnly decls that should have been skipped. This check is gated behind CheckImplementationOnlyStrict and should be used with embedded only. rdar://160697599
1 parent ac41fb6 commit 5a49e34

File tree

6 files changed

+180
-8
lines changed

6 files changed

+180
-8
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,10 @@ REMARK(serialization_skipped_invalid_type_unknown_name,none,
887887
ERROR(serialization_failed,none,
888888
"serialization of module %0 failed due to the errors above",
889889
(const ModuleDecl *))
890+
ERROR(serialization_xref_to_hidden_dependency,none,
891+
"invalid reference to implementation-only imported module %0"
892+
"%select{| for %1}1",
893+
(const ModuleDecl *, const Decl *))
890894

891895
WARNING(can_import_invalid_swiftmodule,none,
892896
"canImport() evaluated to false due to invalid swiftmodule: %0", (StringRef))

include/swift/AST/Module.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,11 +1064,15 @@ class ModuleDecl
10641064
void
10651065
getImportedModulesForLookup(SmallVectorImpl<ImportedModule> &imports) const;
10661066

1067-
/// Has \p module been imported via an '@_implementationOnly' import
1068-
/// instead of another kind of import?
1067+
/// Has \p module been imported via an '@_implementationOnly' import and
1068+
/// not by anything more visible?
10691069
///
1070-
/// This assumes that \p module was imported.
1071-
bool isImportedImplementationOnly(const ModuleDecl *module) const;
1070+
/// If \p assumeImported, assume that \p module was imported and avoid the
1071+
/// work to confirm it is imported at all. Transitive modules not reexported
1072+
/// are not considered imported here and may lead to false positive without
1073+
/// this setting.
1074+
bool isImportedImplementationOnly(const ModuleDecl *module,
1075+
bool assumeImported = true) const;
10721076

10731077
/// Finds all top-level decls of this module.
10741078
///

lib/AST/Module.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3078,7 +3078,8 @@ void ModuleDecl::setPackageName(Identifier name) {
30783078
Package = PackageUnit::create(name, *this, getASTContext());
30793079
}
30803080

3081-
bool ModuleDecl::isImportedImplementationOnly(const ModuleDecl *module) const {
3081+
bool ModuleDecl::isImportedImplementationOnly(const ModuleDecl *module,
3082+
bool assumeImported) const {
30823083
if (module == this) return false;
30833084

30843085
auto &imports = getASTContext().getImportCache();
@@ -3099,7 +3100,17 @@ bool ModuleDecl::isImportedImplementationOnly(const ModuleDecl *module) const {
30993100
return false;
31003101
}
31013102

3102-
return true;
3103+
if (assumeImported)
3104+
return true;
3105+
3106+
results.clear();
3107+
getImportedModules(results,
3108+
{ModuleDecl::ImportFilterKind::ImplementationOnly});
3109+
for (auto &desc : results)
3110+
if (imports.isImportedBy(module, desc.importedModule))
3111+
return true;
3112+
3113+
return false;
31033114
}
31043115

31053116
void SourceFile::lookupImportedSPIGroups(

lib/Serialization/Serialization.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,16 @@ IdentifierID Serializer::addContainingModuleRef(const DeclContext *DC,
756756
if (M->isClangHeaderImportModule())
757757
return OBJC_HEADER_MODULE_ID;
758758

759+
// Reject references to hidden dependencies.
760+
if (getASTContext().LangOpts.hasFeature(
761+
Feature::CheckImplementationOnlyStrict) &&
762+
!allowCompilerErrors() &&
763+
this->M->isImportedImplementationOnly(M, /*assumeImported=*/false)) {
764+
getASTContext().Diags.diagnose(SourceLoc(),
765+
diag::serialization_xref_to_hidden_dependency,
766+
M, crossReferencedDecl);
767+
}
768+
759769
auto exportedModuleName = file->getExportedModuleName();
760770
assert(!exportedModuleName.empty());
761771
auto moduleID = M->getASTContext().getIdentifier(exportedModuleName);
@@ -2452,6 +2462,8 @@ void Serializer::writeCrossReference(const Decl *D) {
24522462

24532463
unsigned abbrCode;
24542464

2465+
llvm::SaveAndRestore<const Decl *> SaveDecl(crossReferencedDecl, D);
2466+
24552467
if (auto op = dyn_cast<OperatorDecl>(D)) {
24562468
writeCrossReference(op->getDeclContext(), 1);
24572469

@@ -5397,8 +5409,7 @@ void Serializer::writeASTBlockEntity(const Decl *D) {
53975409

53985410
// Skip non-public @export(interface) functions.
53995411
auto FD = dyn_cast<AbstractFunctionDecl>(D);
5400-
if (FD &&
5401-
FD->getAttrs().hasAttribute<NeverEmitIntoClientAttr>() &&
5412+
if (FD && FD->isNeverEmittedIntoClient() &&
54025413
!FD->getFormalAccessScope(/*useDC*/nullptr,
54035414
/*treatUsableFromInlineAsPublic*/true).isPublicOrPackage())
54045415
return;

lib/Serialization/Serialization.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ class Serializer : public SerializerBase {
118118

119119
bool hadImplementationOnlyImport = false;
120120

121+
/// Current decl being serialized.
122+
const Decl* crossReferencedDecl = nullptr;
123+
121124
/// Helper for serializing entities in the AST block object graph.
122125
///
123126
/// Keeps track of assigning IDs to newly-seen entities, and collecting
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/// Test CheckImplementationOnlyStrict fallback errors at serialization.
2+
3+
// RUN: %empty-directory(%t)
4+
// RUN: split-file --leading-lines %s %t
5+
6+
// RUN: %target-swift-frontend -emit-module %t/HiddenLib.swift -o %t -I %t \
7+
// RUN: -swift-version 5 -target arm64-apple-none-macho \
8+
// RUN: -enable-experimental-feature Embedded
9+
10+
/// Report errors on invalid references. Disable the early checks to trigger
11+
/// underlying ones.
12+
// RUN: not env SWIFT_DISABLE_IMPLICIT_CHECK_IMPLEMENTATION_ONLY=1 \
13+
// RUN: %target-swift-frontend -emit-module %t/MiddleLib.swift -o %t -I %t \
14+
// RUN: -D BROKEN \
15+
// RUN: -swift-version 5 -target arm64-apple-none-macho \
16+
// RUN: -enable-experimental-feature Embedded \
17+
// RUN: -enable-experimental-feature CheckImplementationOnlyStrict 2> %t/out
18+
// RUN: %FileCheck --input-file %t/out %t/MiddleLib.swift
19+
20+
/// Build a valid version of the library, skipping @_implementationOnly decls.
21+
/// for a client to build against.
22+
// RUN: %target-swift-frontend -emit-module %t/MiddleLib.swift -o %t -I %t \
23+
// RUN: -swift-version 5 -target arm64-apple-none-macho \
24+
// RUN: -enable-experimental-feature Embedded \
25+
// RUN: -enable-experimental-feature CheckImplementationOnlyStrict
26+
27+
/// Build an actual client.
28+
// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \
29+
// RUN: -swift-version 5 -target arm64-apple-none-macho \
30+
// RUN: -enable-experimental-feature Embedded \
31+
// RUN: -enable-experimental-feature CheckImplementationOnlyStrict
32+
33+
// REQUIRES: swift_feature_Embedded
34+
// REQUIRES: swift_feature_CheckImplementationOnlyStrict
35+
// REQUIRES: embedded_stdlib_cross_compiling
36+
37+
//--- HiddenLib.swift
38+
39+
public struct A {}
40+
public struct B {}
41+
public struct C {}
42+
public struct D {}
43+
public struct E {}
44+
public struct F {}
45+
46+
public struct OkA {}
47+
public struct OkB {}
48+
public struct OkC {}
49+
public struct OkD {}
50+
public struct OkE {}
51+
public struct OkF {}
52+
53+
public protocol Proto {}
54+
public protocol OkProto {}
55+
56+
//--- MiddleLib.swift
57+
58+
@_implementationOnly import HiddenLib
59+
60+
/// Referenced types
61+
62+
#if BROKEN
63+
internal struct InternalStruct: Proto {
64+
// CHECK-DAG: error: invalid reference to implementation-only imported module 'HiddenLib' for 'Proto'
65+
var a: A
66+
// CHECK-DAG: error: invalid reference to implementation-only imported module 'HiddenLib' for 'A'
67+
}
68+
69+
internal enum InternalEnum {
70+
case b(B)
71+
// CHECK-DAG: error: invalid reference to implementation-only imported module 'HiddenLib' for 'B'
72+
case c(C)
73+
// CHECK-DAG: error: invalid reference to implementation-only imported module 'HiddenLib' for 'C'
74+
}
75+
76+
public class PublicClass {
77+
init() { fatalError() }
78+
internal var internalField: D
79+
// CHECK-DAG: error: invalid reference to implementation-only imported module 'HiddenLib' for 'D'
80+
private var privateField: E
81+
// CHECK-DAG: error: invalid reference to implementation-only imported module 'HiddenLib' for 'E'
82+
}
83+
84+
@export(interface)
85+
private func PrivateFunc(h: F) {}
86+
// CHECK-DAG: error: invalid reference to implementation-only imported module 'HiddenLib' for 'F'
87+
#endif
88+
89+
@_implementationOnly
90+
internal struct OkInternalStruct: OkProto {
91+
var a: OkA
92+
}
93+
94+
@_implementationOnly
95+
internal struct NesterStruct {
96+
var a: OkA
97+
98+
@_implementationOnly
99+
struct Nested {
100+
var b: OkB
101+
}
102+
}
103+
104+
internal struct NesterStructB {
105+
@_implementationOnly
106+
struct Nested {
107+
var b: OkB
108+
}
109+
}
110+
111+
@_implementationOnly
112+
internal enum OkInternalEnum {
113+
case b(OkB)
114+
case c(OkC)
115+
}
116+
117+
@_implementationOnly
118+
internal class OkInternalClass {
119+
init() { fatalError() }
120+
internal var internalField: OkD
121+
private var privateField: OkE
122+
}
123+
124+
@export(interface)
125+
internal func OkPrivateFunc(h: OkF) {}
126+
127+
public struct PublicStruct {}
128+
129+
@export(interface)
130+
public func PublicFunc() -> PublicStruct {
131+
let _: OkA
132+
return PublicStruct()
133+
}
134+
135+
//--- Client.swift
136+
137+
import MiddleLib
138+
139+
let _ = PublicFunc()

0 commit comments

Comments
 (0)