Skip to content

Commit 93cfc6e

Browse files
authored
[ClangImporter] Make conformance loading lazier. (#10737)
Previously, the importer queued up conformances to complete once it was done importing the current batch of declarations. However, if there was a serialized Swift module that extended an imported type to add a conformance in exactly the wrong way, the importer could end up asking for that conformance later---even before the reference to the imported type was resolved. This led to a crash in the deserializer "while reading conformance for type X". Instead of this "pending actions" queue, we can just use the mechanisms already in place for lazily loading conformances. That way they'll get filled out on demand, which is better all around anyway. This does mean putting the requirement signature into the "lazy" part of the conformance, though. This does as a side effect mean that /all/ of the witnesses for the imported conformance may be opaque---that is, they will never be devirtualized to a particular implementation. However, they previously would have referred to methods implemented in Objective-C anyway, which are always dispatched with objc_msgSend. So this should have no practical effect. rdar://problem/32346184 (cherry picked from commit a8bc132)
1 parent 6239326 commit 93cfc6e

File tree

6 files changed

+49
-18
lines changed

6 files changed

+49
-18
lines changed

include/swift/AST/ProtocolConformance.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,8 @@ class NormalProtocolConformance : public ProtocolConformance,
462462
/// protocol, which line up with the conformance constraints in the
463463
/// protocol's requirement signature.
464464
ArrayRef<ProtocolConformanceRef> getSignatureConformances() const {
465+
if (Resolver)
466+
resolveLazyInfo();
465467
return SignatureConformances;
466468
}
467469

lib/ClangImporter/ImportDecl.cpp

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6348,7 +6348,8 @@ void SwiftDeclConverter::addObjCProtocolConformances(
63486348
auto conformance = ctx.getConformance(dc->getDeclaredTypeInContext(),
63496349
protocols[i], SourceLoc(), dc,
63506350
ProtocolConformanceState::Incomplete);
6351-
Impl.scheduleFinishProtocolConformance(conformance);
6351+
conformance->setLazyLoader(&Impl, /*context*/0);
6352+
conformance->setState(ProtocolConformanceState::Complete);
63526353
conformances.push_back(conformance);
63536354
}
63546355

@@ -7286,6 +7287,9 @@ void ClangImporter::Implementation::finishedImportingEntity() {
72867287

72877288
void ClangImporter::Implementation::finishPendingActions() {
72887289
while (true) {
7290+
// The odd shape of this loop comes from previously having more than one
7291+
// possible kind of pending action. It's left this way to make it easy to
7292+
// add another one back in an `else if` clause.
72897293
if (!RegisteredExternalDecls.empty()) {
72907294
if (hasFinishedTypeChecking()) {
72917295
RegisteredExternalDecls.clear();
@@ -7297,22 +7301,22 @@ void ClangImporter::Implementation::finishPendingActions() {
72977301
if (!nominal->hasDelayedMembers())
72987302
typeResolver->resolveExternalDeclImplicitMembers(nominal);
72997303
}
7300-
} else if (!DelayedProtocolConformances.empty()) {
7301-
NormalProtocolConformance *conformance =
7302-
DelayedProtocolConformances.pop_back_val();
7303-
finishProtocolConformance(conformance);
73047304
} else {
73057305
break;
73067306
}
73077307
}
73087308
}
73097309

7310-
/// Finish the given protocol conformance (for an imported type)
7311-
/// by filling in any missing witnesses.
7312-
void ClangImporter::Implementation::finishProtocolConformance(
7313-
NormalProtocolConformance *conformance) {
7310+
void ClangImporter::Implementation::finishNormalConformance(
7311+
NormalProtocolConformance *conformance,
7312+
uint64_t unused) {
7313+
(void)unused;
73147314
const ProtocolDecl *proto = conformance->getProtocol();
73157315

7316+
PrettyStackTraceType trace(SwiftContext, "completing conformance for",
7317+
conformance->getType());
7318+
PrettyStackTraceDecl traceTo("... to", proto);
7319+
73167320
// Create witnesses for requirements not already met.
73177321
for (auto req : proto->getMembers()) {
73187322
auto valueReq = dyn_cast<ValueDecl>(req);

lib/ClangImporter/ImporterImpl.h

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -496,9 +496,6 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
496496
/// External Decls that we have imported but not passed to the ASTContext yet.
497497
SmallVector<Decl *, 4> RegisteredExternalDecls;
498498

499-
/// Protocol conformances that may be missing witnesses.
500-
SmallVector<NormalProtocolConformance *, 4> DelayedProtocolConformances;
501-
502499
unsigned NumCurrentImportingEntities = 0;
503500

504501
/// Mapping from delayed conformance IDs to the set of delayed
@@ -517,7 +514,6 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
517514
void startedImportingEntity();
518515
void finishedImportingEntity();
519516
void finishPendingActions();
520-
void finishProtocolConformance(NormalProtocolConformance *conformance);
521517

522518
struct ImportingEntityRAII {
523519
Implementation &Impl;
@@ -567,10 +563,6 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
567563
RegisteredExternalDecls.push_back(D);
568564
}
569565

570-
void scheduleFinishProtocolConformance(NormalProtocolConformance *C) {
571-
DelayedProtocolConformances.push_back(C);
572-
}
573-
574566
/// \brief Retrieve the Clang AST context.
575567
clang::ASTContext &getClangASTContext() const {
576568
return Instance->getASTContext();
@@ -1115,6 +1107,9 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
11151107
const Decl *D, uint64_t contextData,
11161108
SmallVectorImpl<ProtocolConformance *> &Conformances) override;
11171109

1110+
void finishNormalConformance(NormalProtocolConformance *conformance,
1111+
uint64_t unused) override;
1112+
11181113
template <typename DeclTy, typename ...Targs>
11191114
DeclTy *createDeclWithClangNode(ClangNode ClangN, Accessibility access,
11201115
Targs &&... Args) {

test/ClangImporter/Inputs/SwiftPrivateAttr.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ struct NSOptions : OptionSet {
107107
static var __PrivA: NSOptions { get }
108108
static var B: NSOptions { get }
109109
}
110-
class __PrivCFType : _CFObject {
110+
class __PrivCFType {
111111
}
112112
@available(swift, obsoleted: 3, renamed: "__PrivCFType")
113113
typealias __PrivCFTypeRef = __PrivCFType

test/ClangImporter/Inputs/custom-modules/Protocols.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,12 @@ Class <FooProto> _Nonnull processFooType(Class <FooProto> _Nonnull);
99
Class <FooProto, AnotherProto> _Nonnull processComboType(Class <FooProto, AnotherProto> _Nonnull);
1010
Class <AnotherProto, FooProto> _Nonnull processComboType2(Class <AnotherProto, FooProto> _Nonnull);
1111

12+
13+
@protocol SubProto <FooProto>
14+
@end
15+
16+
@interface ProtocolTestingBase
17+
@end
18+
19+
@interface SubProtoImpl: ProtocolTestingBase <SubProto>
20+
@end
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-module -o %t/a~partial.swiftmodule -I %S/Inputs/custom-modules -module-name TEST -primary-file %s
3+
// RUN: %target-swift-frontend -emit-module -o %t/test.swiftmodule -I %S/Inputs/custom-modules -module-name TEST %t/a~partial.swiftmodule
4+
5+
// REQUIRES: objc_interop
6+
7+
import TestProtocols
8+
9+
// The protocol in the extension has to refine something that the base class
10+
// conforms to to trigger the error in rdar://problem/32346184.
11+
protocol SomeSwiftProto: Equatable {}
12+
extension ProtocolTestingBase: Equatable {
13+
public static func ==(left: ProtocolTestingBase, right: ProtocolTestingBase) -> Bool {
14+
return left === right
15+
}
16+
}
17+
18+
// The extension going through the typealias also makes a difference.
19+
typealias SpecialObject = SubProtoImpl
20+
extension SpecialObject: SomeSwiftProto {
21+
}

0 commit comments

Comments
 (0)