Skip to content

Commit e7d3250

Browse files
authored
Merge pull request #68384 from tshortli/emit-module-lazy-typecheck-inherited-protocol-printing
ModuleInterface: Fix inheritance clauses in lazily emitted swiftinterfaces
2 parents b7c2cb8 + 7c1b150 commit e7d3250

File tree

9 files changed

+169
-31
lines changed

9 files changed

+169
-31
lines changed

lib/AST/ASTPrinter.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2955,8 +2955,8 @@ static bool usesFeatureRethrowsProtocol(
29552955

29562956
// Check an inheritance clause for a marker protocol.
29572957
auto checkInherited = [&](InheritedTypes inherited) -> bool {
2958-
for (const auto &inheritedEntry : inherited.getEntries()) {
2959-
if (auto inheritedType = inheritedEntry.getType()) {
2958+
for (unsigned i : inherited.getIndices()) {
2959+
if (auto inheritedType = inherited.getResolvedType(i)) {
29602960
if (inheritedType->isExistentialType()) {
29612961
auto layout = inheritedType->getExistentialLayout();
29622962
for (ProtocolDecl *proto : layout.getProtocols()) {
@@ -7688,8 +7688,8 @@ swift::getInheritedForPrinting(
76887688
InheritedTypes inherited = InheritedTypes(decl);
76897689

76907690
// Collect explicit inherited types.
7691-
for (auto entry : inherited.getEntries()) {
7692-
if (auto ty = entry.getType()) {
7691+
for (auto i : inherited.getIndices()) {
7692+
if (auto ty = inherited.getResolvedType(i)) {
76937693
bool foundUnprintable = ty.findIf([&](Type subTy) {
76947694
if (auto aliasTy = dyn_cast<TypeAliasType>(subTy.getPointer()))
76957695
return !options.shouldPrint(aliasTy->getDecl());
@@ -7703,7 +7703,7 @@ swift::getInheritedForPrinting(
77037703
continue;
77047704
}
77057705

7706-
Results.push_back(entry);
7706+
Results.push_back(inherited.getEntry(i));
77077707
}
77087708

77097709
// Collect synthesized conformances.

lib/AST/ConformanceLookupTable.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -226,16 +226,18 @@ void ConformanceLookupTable::inheritConformances(ClassDecl *classDecl,
226226
if (superclassLoc.isValid())
227227
return superclassLoc;
228228

229-
for (const auto &inherited : classDecl->getInherited().getEntries()) {
230-
if (auto inheritedType = inherited.getType()) {
229+
auto inheritedTypes = classDecl->getInherited();
230+
for (unsigned i : inheritedTypes.getIndices()) {
231+
if (auto inheritedType = inheritedTypes.getResolvedType(i)) {
232+
231233
if (inheritedType->getClassOrBoundGenericClass()) {
232-
superclassLoc = inherited.getSourceRange().Start;
234+
superclassLoc = inheritedTypes.getEntry(i).getSourceRange().Start;
233235
return superclassLoc;
234236
}
235237
if (inheritedType->isExistentialType()) {
236238
auto layout = inheritedType->getExistentialLayout();
237239
if (layout.explicitSuperclass) {
238-
superclassLoc = inherited.getSourceRange().Start;
240+
superclassLoc = inheritedTypes.getEntry(i).getSourceRange().Start;
239241
return superclassLoc;
240242
}
241243
}

lib/Frontend/ModuleInterfaceSupport.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -483,15 +483,16 @@ class InheritedProtocolCollector {
483483
bool skipExtra = false) {
484484
llvm::Optional<AvailableAttrList> availableAttrs;
485485

486-
for (InheritedEntry inherited : directlyInherited.getEntries()) {
487-
Type inheritedTy = inherited.getType();
486+
for (int i : directlyInherited.getIndices()) {
487+
Type inheritedTy = directlyInherited.getResolvedType(i);
488488
if (!inheritedTy || !inheritedTy->isExistentialType())
489489
continue;
490490

491491
bool canPrintNormally = canPrintProtocolTypeNormally(inheritedTy, D);
492492
if (!canPrintNormally && skipExtra)
493493
continue;
494494

495+
auto inherited = directlyInherited.getEntry(i);
495496
ExistentialLayout layout = inheritedTy->getExistentialLayout();
496497
for (ProtocolDecl *protoDecl : layout.getProtocols()) {
497498
if (canPrintNormally)
@@ -526,8 +527,9 @@ class InheritedProtocolCollector {
526527
/// For each type directly inherited by \p extension, record any protocols
527528
/// that we would have printed in ConditionalConformanceProtocols.
528529
void recordConditionalConformances(const ExtensionDecl *extension) {
529-
for (TypeLoc inherited : extension->getInherited().getEntries()) {
530-
Type inheritedTy = inherited.getType();
530+
auto inheritedTypes = extension->getInherited();
531+
for (unsigned i : inheritedTypes.getIndices()) {
532+
Type inheritedTy = inheritedTypes.getResolvedType(i);
531533
if (!inheritedTy || !inheritedTy->isExistentialType())
532534
continue;
533535

lib/Serialization/Serialization.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3264,9 +3264,6 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
32643264
size_t addConformances(const IterableDeclContext *declContext,
32653265
ConformanceLookupKind lookupKind,
32663266
SmallVectorImpl<TypeID> &data) {
3267-
// We don't expect to be serializing conformances for skipped decls.
3268-
assert(!S.shouldSkipDecl(declContext->getDecl()));
3269-
32703267
size_t count = 0;
32713268
for (auto conformance : declContext->getLocalConformances(lookupKind)) {
32723269
if (S.shouldSkipDecl(conformance->getProtocol()))
@@ -3353,6 +3350,10 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
33533350
return true;
33543351
}
33553352

3353+
// Paramters don't have meaningful access control.
3354+
if (isa<ParamDecl>(decl) || isa<GenericTypeParamDecl>(decl))
3355+
return true;
3356+
33563357
return false;
33573358
}
33583359

@@ -4897,11 +4898,16 @@ static bool canSkipWhenInvalid(const Decl *D) {
48974898
}
48984899

48994900
bool Serializer::shouldSkipDecl(const Decl *D) const {
4900-
if (Options.SerializeExternalDeclsOnly &&
4901-
!DeclSerializer::isDeserializationSafe(D))
4902-
return true;
4901+
// The presence of -experimental-serialize-external-decls-only is the only
4902+
// reason to omit decls during serialization.
4903+
if (!Options.SerializeExternalDeclsOnly)
4904+
return false;
49034905

4904-
return false;
4906+
// For our purposes, "deserialization safe" is the same thing as "external".
4907+
if (DeclSerializer::isDeserializationSafe(D))
4908+
return false;
4909+
4910+
return true;
49054911
}
49064912

49074913
void Serializer::writeASTBlockEntity(const Decl *D) {

test/Inputs/lazy_typecheck.swift

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,25 @@ public func publicFuncWithOpaqueReturnType() -> some PublicProto { // expected-n
4949

5050
// MARK: - Nominal types
5151

52+
public protocol EmptyPublicProto {}
53+
5254
public protocol PublicProto {
5355
func req() -> Int // expected-note 2 {{protocol requires function 'req()' with type '() -> Int'; add a stub for conformance}}
5456
}
5557

58+
@rethrows public protocol PublicRethrowsProto {
59+
func req() throws -> Int
60+
}
61+
5662
protocol InternalProto {
57-
func goodReq() -> Int // expected-note {{protocol requires function 'goodReq()' with type '() -> Int'; add a stub for conformance}}
63+
func goodReq() -> Int // expected-note 2 {{protocol requires function 'goodReq()' with type '() -> Int'; add a stub for conformance}}
5864
func badReq() -> DoesNotExist // expected-error {{cannot find type 'DoesNotExist' in scope}}
5965
}
6066

67+
protocol InternalProtoConformingToPublicProto: PublicProto {
68+
func internalReq() -> DoesNotExist // expected-error {{cannot find type 'DoesNotExist' in scope}}
69+
}
70+
6171
public struct PublicStruct {
6272
// FIXME: Test properties
6373

@@ -82,6 +92,12 @@ public struct PublicStruct {
8292
}
8393
}
8494

95+
public struct PublicGenericStruct<T> {
96+
public func publicMethod() -> T {
97+
return true // expected-error {{cannot convert return expression of type 'Bool' to return type 'T'}}
98+
}
99+
}
100+
85101
struct InternalStruct: DoesNotExist { // expected-error {{cannot find type 'DoesNotExist' in scope}}
86102
var x: DoesNotExist // expected-error {{cannot find type 'DoesNotExist' in scope}}
87103

@@ -111,6 +127,8 @@ public class PublicClass {
111127
// class func internalClassMethod() -> DoesNotExist {}
112128
}
113129

130+
public class PublicDerivedClass: PublicClass {}
131+
114132
class InternalClass: DoesNotExist { // expected-error {{cannot find type 'DoesNotExist' in scope}}
115133
init(x: DoesNotExist) {} // expected-error {{cannot find type 'DoesNotExist' in scope}}
116134
}
@@ -124,19 +142,36 @@ public struct PublicStructConformingToPublicProto: PublicProto {
124142
}
125143
}
126144

145+
public struct PublicStructIndirectlyConformingToPublicProto: InternalProtoConformingToPublicProto {
146+
public init() {}
147+
public func req() -> Int {
148+
return true // expected-error {{cannot convert return expression of type 'Bool' to return type 'Int'}}
149+
}
150+
}
151+
127152
public class PublicClassConformingToPublicProto: PublicProto {
128153
public init() {}
129154
public func req() -> Int {
130155
return true // expected-error {{cannot convert return expression of type 'Bool' to return type 'Int'}}
131156
}
132157
}
133158

159+
public class PublicClassInheritingConformanceToPublicProto: PublicClassConformingToPublicProto {}
160+
134161
extension String: PublicProto {
135162
public func req() -> Int {
136163
return true // expected-error {{cannot convert return expression of type 'Bool' to return type 'Int'}}
137164
}
138165
}
139166

167+
extension String: InternalProto {} // expected-error {{type 'String' does not conform to protocol 'InternalProto'}}
168+
169+
extension Int: PublicRethrowsProto {
170+
public func req() throws -> Int {
171+
return true // expected-error {{cannot convert return expression of type 'Bool' to return type 'Int'}}
172+
}
173+
}
174+
140175
struct InternalStructConformingToPublicProto: PublicProto { // expected-error {{type 'InternalStructConformingToPublicProto' does not conform to protocol 'PublicProto'}}
141176
}
142177

@@ -146,5 +181,11 @@ extension InternalStruct: PublicProto { // expected-error {{type 'InternalStruct
146181
struct InternalStructConformingToInternalProto: InternalProto { // expected-error {{type 'InternalStructConformingToInternalProto' does not conform to protocol 'InternalProto'}}
147182
}
148183

184+
struct InternalStructForConstraint {}
185+
186+
extension PublicGenericStruct where T == InternalStructForConstraint {}
187+
188+
extension PublicGenericStruct: EmptyPublicProto where T == InternalStructForConstraint {}
189+
149190
// FIXME: Test enums
150191
// FIXME: Test global vars

test/Inputs/lazy_typecheck_client.swift

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func testGlobalFunctions() {
2121
}
2222

2323
func testPublicStruct() {
24-
var s = PublicStruct(x: 1)
24+
let s = PublicStruct(x: 1)
2525
_ = s.publicMethod()
2626
PublicStruct.publicStaticMethod()
2727
}
@@ -30,12 +30,38 @@ func testPublicClass() {
3030
let c = PublicClass(x: 2)
3131
_ = c.publicMethod()
3232
PublicClass.publicClassMethod()
33+
34+
let d = PublicDerivedClass(x: 3)
35+
_ = d.publicMethod()
36+
PublicDerivedClass.publicClassMethod()
3337
}
3438

3539
func testConformances() {
36-
let _: [any PublicProto] = [
40+
let array: [any PublicProto] = [
3741
PublicStructConformingToPublicProto(),
42+
PublicStructIndirectlyConformingToPublicProto(),
3843
PublicClassConformingToPublicProto(),
3944
"string",
45+
PublicClassInheritingConformanceToPublicProto(),
4046
]
47+
48+
for x in array {
49+
_ = x.req()
50+
constrainedGenericPublicFunction(x)
51+
}
52+
}
53+
54+
// FIXME: This conformance ought to be included to verify that a redundant
55+
// conformance diagnostic is emitted.
56+
// However, it turns out that the mechanism implemented by
57+
// https://github.com/apple/swift/pull/20433 doesn't actually work when a
58+
// module is built from .swiftinterface because the dummy conformance is marked
59+
// unavailable.
60+
//extension PublicGenericStruct: EmptyPublicProto {}
61+
62+
func takesEmptyProto<T: EmptyPublicProto>(_ t: T) {}
63+
64+
@available(*, unavailable)
65+
func testConditionalConformance<T>(_ s: PublicGenericStruct<T>) {
66+
takesEmptyProto(s) // expected-error {{global function 'takesEmptyProto' requires}}
4167
}

test/ModuleInterface/lazy-typecheck.swift

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// RUN: %FileCheck %s < %t/lazy_typecheck.swiftinterface
55

66
// RUN: rm -rf %t/*.swiftmodule
7-
// RUN: %target-swift-frontend -package-name Package -typecheck %S/../Inputs/lazy_typecheck_client.swift -I %t
7+
// RUN: %target-swift-frontend -package-name Package -typecheck -verify %S/../Inputs/lazy_typecheck_client.swift -I %t
88

99
// CHECK: import Swift
1010

@@ -26,29 +26,62 @@
2626
// CHECK-NEXT: }
2727
// CHECK-NEXT: }
2828

29+
// CHECK: public protocol EmptyPublicProto {
30+
// CHECK: }
2931
// CHECK: public protocol PublicProto {
3032
// CHECK: func req() -> Swift.Int
3133
// CHECK: }
34+
// CHECK: #if compiler(>=5.3) && $RethrowsProtocol
35+
// CHECK: @rethrows public protocol PublicRethrowsProto {
36+
// CHECK: func req() throws -> Swift.Int
37+
// CHECK: }
38+
// CHECK: #endif
3239
// CHECK: public struct PublicStruct {
3340
// CHECK: public init(x: Swift.Int)
3441
// CHECK: public func publicMethod() -> Swift.Int
3542
// CHECK: public static func publicStaticMethod()
3643
// CHECK: }
44+
// CHECK: public struct PublicGenericStruct<T> {
45+
// CHECK: public func publicMethod() -> T
46+
// CHECK: }
3747
// CHECK: public class PublicClass {
3848
// CHECK: public init(x: Swift.Int)
3949
// CHECK: public func publicMethod() -> Swift.Int
4050
// CHECK: public class func publicClassMethod()
4151
// CHECK: deinit
4252
// CHECK: }
43-
// CHECK: public struct PublicStructConformingToPublicProto : PublicProto {
53+
// CHECK: @_inheritsConvenienceInitializers public class PublicDerivedClass : lazy_typecheck.PublicClass {
54+
// CHECK: override public init(x: Swift.Int)
55+
// CHECK: deinit
56+
// CHECK: }
57+
// CHECK: public struct PublicStructConformingToPublicProto : lazy_typecheck.PublicProto {
58+
// CHECK: public init()
59+
// CHECK: public func req() -> Swift.Int
60+
// CHECK: }
61+
// CHECK: public struct PublicStructIndirectlyConformingToPublicProto {
4462
// CHECK: public init()
4563
// CHECK: public func req() -> Swift.Int
4664
// CHECK: }
47-
// CHECK: public class PublicClassConformingToPublicProto : PublicProto {
65+
// CHECK: public class PublicClassConformingToPublicProto : lazy_typecheck.PublicProto {
4866
// CHECK: public init()
4967
// CHECK: public func req() -> Swift.Int
5068
// CHECK: deinit
5169
// CHECK: }
52-
// CHECK: extension Swift.String : PublicProto {
70+
// CHECK: @_inheritsConvenienceInitializers public class PublicClassInheritingConformanceToPublicProto : lazy_typecheck.PublicClassConformingToPublicProto {
71+
// CHECK: override public init()
72+
// CHECK: deinit
73+
// CHECK: }
74+
// CHECK: extension Swift.String : lazy_typecheck.PublicProto {
5375
// CHECK: public func req() -> Swift.Int
5476
// CHECK: }
77+
// CHECK: #if compiler(>=5.3) && $RethrowsProtocol
78+
// CHECK: extension Swift.Int : lazy_typecheck.PublicRethrowsProto {
79+
// CHECK: public func req() throws -> Swift.Int
80+
// CHECK: }
81+
// CHECK: #endif
82+
// CHECK: @available(*, unavailable)
83+
// CHECK-NEXT: extension lazy_typecheck.PublicGenericStruct : lazy_typecheck.EmptyPublicProto where T : _ConstraintThatIsNotPartOfTheAPIOfThisLibrary {}
84+
// CHECK: extension lazy_typecheck.PublicStructIndirectlyConformingToPublicProto : lazy_typecheck.PublicProto {}
85+
86+
// CHECK: @usableFromInline
87+
// CHECK: internal protocol _ConstraintThatIsNotPartOfTheAPIOfThisLibrary {}

test/Serialization/serialize-external-decls-only-lazy-typecheck.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
// RUN: %target-swift-frontend -swift-version 5 %S/../Inputs/lazy_typecheck.swift -enable-library-evolution -parse-as-library -package-name Package -typecheck -verify
33
// RUN: %target-swift-frontend -swift-version 5 %S/../Inputs/lazy_typecheck.swift -module-name lazy_typecheck -emit-module -emit-module-path %t/lazy_typecheck.swiftmodule -enable-library-evolution -parse-as-library -package-name Package -experimental-lazy-typecheck -experimental-skip-all-function-bodies -experimental-serialize-external-decls-only
44

5-
// RUN: %target-swift-frontend -package-name Package -typecheck %S/../Inputs/lazy_typecheck_client.swift -D TEST_PACKAGE -I %t
5+
// RUN: %target-swift-frontend -package-name Package -typecheck -verify %S/../Inputs/lazy_typecheck_client.swift -D TEST_PACKAGE -I %t

0 commit comments

Comments
 (0)