Skip to content

Commit 9602df1

Browse files
xymusAlexis Laferrière
authored andcommitted
[Serialization] Keep indirect conformances knowledge with safety
Given a scenario where a public type A, conforms to an internal protocol B, which conforms to a public protocol C. A conforms indirectly to C through a protocol that's hidden from the clients. This is handled in module interface by printing the indirect conformance of A to C explicitly at the end of the swiftinterface. We have the same problem with deserialization safety that used to hide the internal protocols from clients, thus breaking the knowledge of the indirect dependency. To keep the indirect conformances, let's consider all protocols as safe and preserve their conformance information. rdar://105241772
1 parent 08af8a6 commit 9602df1

File tree

4 files changed

+66
-14
lines changed

4 files changed

+66
-14
lines changed

lib/Serialization/Serialization.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3124,14 +3124,11 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
31243124
if (hasSafeMembers)
31253125
return true;
31263126

3127-
// We can mark the extension unsafe only if it has no public
3127+
// We can mark the extension unsafe only if it has no public
31283128
// conformances.
31293129
auto protocols = ext->getLocalProtocols(
31303130
ConformanceLookupKind::OnlyExplicit);
3131-
bool hasSafeConformances = std::any_of(protocols.begin(),
3132-
protocols.end(),
3133-
isDeserializationSafe);
3134-
if (hasSafeConformances)
3131+
if (!protocols.empty())
31353132
return true;
31363133

31373134
// Truly empty extensions are safe, it may happen in swiftinterfaces.
@@ -3141,6 +3138,9 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
31413138
return false;
31423139
}
31433140

3141+
if (isa<ProtocolDecl>(decl))
3142+
return true;
3143+
31443144
auto value = cast<ValueDecl>(decl);
31453145

31463146
// A decl is safe if formally accessible publicly.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: split-file %s %t
3+
4+
// REQUIRES: asserts
5+
6+
//--- Lib.swift
7+
8+
// RUN: %target-swift-frontend -emit-module %t/Lib.swift \
9+
// RUN: -enable-library-evolution -swift-version 5 \
10+
// RUN: -emit-module-path %t/Lib.swiftmodule \
11+
// RUN: -emit-module-interface-path %t/Lib.swiftinterface
12+
13+
// RUN: cat %t/Lib.swiftinterface | %FileCheck %t/Lib.swift
14+
15+
public protocol PublicProtocol : AnyObject {}
16+
// CHECK: public protocol PublicProtocol : AnyObject
17+
18+
protocol InternalProtocol: PublicProtocol {}
19+
// CHECK-NOT: InternalProtocol
20+
21+
public class IndirectConformant {
22+
public init() {}
23+
}
24+
extension IndirectConformant: InternalProtocol {}
25+
// CHECK: extension Lib.IndirectConformant : Lib.PublicProtocol {}
26+
27+
//--- Client.swift
28+
29+
/// Works without safety.
30+
// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t
31+
32+
/// Works with safety.
33+
// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t \
34+
// RUN: -enable-deserialization-safety \
35+
// RUN: -Xllvm -debug-only=Serialization 2>&1 \
36+
// RUN: | %FileCheck %t/Client.swift
37+
38+
/// Works with swiftinterface.
39+
// RUN: rm %t/Lib.swiftmodule
40+
// RUN: %target-swift-frontend -typecheck %t/Client.swift -I %t
41+
42+
import Lib
43+
44+
func requireConformanceToPublicProtocol(_ a: PublicProtocol) {}
45+
requireConformanceToPublicProtocol(IndirectConformant())
46+
47+
/// Deserialization safety should keep the original chain. We're mostly
48+
/// documenting the current safety implementation details here, if we can get
49+
/// without deserializing 'InternalProtocol' it would be even better.
50+
// CHECK: Deserialized: 'IndirectConformant'
51+
// CHECK: Deserialized: 'PublicProtocol'
52+
// CHECK: Deserialized: 'InternalProtocol'

test/Serialization/Safety/unsafe-decls.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@
3434
public protocol PublicProto {}
3535
// SAFETY-PRIVATE: Serialization safety, safe: 'PublicProto'
3636
internal protocol InternalProto {}
37-
// SAFETY-INTERNAL: Serialization safety, unsafe: 'InternalProto'
37+
// SAFETY-INTERNAL: Serialization safety, safe: 'InternalProto'
3838
// NO-SAFETY-INTERNAL: Serialization safety, safe: 'InternalProto'
3939
private protocol PrivateProto {}
40-
// SAFETY-PRIVATE: Serialization safety, unsafe: 'PrivateProto'
40+
// SAFETY-PRIVATE: Serialization safety, safe: 'PrivateProto'
4141
fileprivate protocol FileprivateProto {}
42-
// SAFETY-PRIVATE: Serialization safety, unsafe: 'FileprivateProto'
42+
// SAFETY-PRIVATE: Serialization safety, safe: 'FileprivateProto'
4343

4444
internal struct InternalStruct : PublicProto {
4545
// SAFETY-INTERNAL: Serialization safety, unsafe: 'InternalStruct'

test/Serialization/Safety/unsafe-extensions.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ extension ExtendedPublic : PublicProto {
4747

4848
/// Internal
4949
internal protocol InternalProto {}
50-
// SAFETY-INTERNAL: Serialization safety, unsafe: 'InternalProto'
50+
// SAFETY-INTERNAL: Serialization safety, safe: 'InternalProto'
5151
// NO-SAFETY-INTERNAL: Serialization safety, safe: 'InternalProto'
5252
internal struct ExtendedInternal {}
5353
// SAFETY-INTERNAL: Serialization safety, unsafe: 'ExtendedInternal'
@@ -63,7 +63,7 @@ extension ExtendedInternal : InternalProto {}
6363

6464
/// Private
6565
private protocol PrivateProto {}
66-
// SAFETY-PRIVATE: Serialization safety, unsafe: 'PrivateProto'
66+
// SAFETY-PRIVATE: Serialization safety, safe: 'PrivateProto'
6767
private struct ExtendedPrivate {}
6868
// SAFETY-PRIVATE: Serialization safety, unsafe: 'ExtendedPrivate'
6969
extension ExtendedPrivate {
@@ -75,7 +75,7 @@ extension ExtendedPrivate : PrivateProto {}
7575

7676
/// Fileprivate
7777
private protocol FileprivateProto {}
78-
// SAFETY-PRIVATE: Serialization safety, unsafe: 'FileprivateProto'
78+
// SAFETY-PRIVATE: Serialization safety, safe: 'FileprivateProto'
7979
private struct ExtendedFileprivate {}
8080
// SAFETY-PRIVATE: Serialization safety, unsafe: 'ExtendedFileprivate'
8181
extension ExtendedFileprivate {
@@ -87,14 +87,14 @@ extension ExtendedFileprivate : FileprivateProto {}
8787

8888
/// Back to public
8989
extension ExtendedPublic : InternalProto {
90-
// SAFETY-INTERNAL: Serialization safety, unsafe: 'extension ExtendedPublic'
90+
// SAFETY-INTERNAL: Serialization safety, safe: 'extension ExtendedPublic'
9191
// NO-SAFETY-INTERNAL: Serialization safety, safe: 'extension ExtendedPublic'
9292
}
9393
extension ExtendedPublic : PrivateProto {
94-
// SAFETY-PRIVATE: Serialization safety, unsafe: 'extension ExtendedPublic'
94+
// SAFETY-PRIVATE: Serialization safety, safe: 'extension ExtendedPublic'
9595
}
9696
extension ExtendedPublic : FileprivateProto {
97-
// SAFETY-PRIVATE: Serialization safety, unsafe: 'extension ExtendedPublic'
97+
// SAFETY-PRIVATE: Serialization safety, safe: 'extension ExtendedPublic'
9898
}
9999

100100
extension ExtendedPublic {

0 commit comments

Comments
 (0)