Skip to content

Commit c8b4357

Browse files
committed
[AST] Teach NormalProtocolConformance to resolve known, synthesized witnesses.
There are a number of type witnesses that are introduced by the Clang importer. Immediately resolve those witnesses *without* going through the type checker, because there are cases (i.e., deserialized SIL) where the conformance is created but there is no type checker around. Fixes rdar://problem/30364905, rdar://problem/31053701, rdar://problem/31565413 / SR-4565.
1 parent 25290bd commit c8b4357

File tree

3 files changed

+121
-2
lines changed

3 files changed

+121
-2
lines changed

lib/AST/ProtocolConformance.cpp

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,91 @@ bool NormalProtocolConformance::hasTypeWitness(AssociatedTypeDecl *assocType,
398398
return false;
399399
}
400400

401+
/// Directly resolve type witnesses that are known to the compiler because they
402+
/// were synthesized by the compiler.
403+
///
404+
/// FIXME: This is a hack to work around the fact that we don't have a
405+
/// TypeChecker when we need one.
406+
///
407+
/// \returns true if we resolved the type witness.
408+
static bool resolveKnownTypeWitness(NormalProtocolConformance *conformance,
409+
AssociatedTypeDecl *assocType) {
410+
auto nominal = conformance->getType()->getAnyNominal();
411+
if (!nominal) return false;
412+
413+
if (!nominal->hasClangNode()) return false;
414+
415+
auto proto = conformance->getProtocol();
416+
auto knownKind = proto->getKnownProtocolKind();
417+
if (!knownKind) return false;
418+
419+
auto &ctx = nominal->getASTContext();
420+
421+
// Local function to handle resolution via lookup directly into the nominal
422+
// type.
423+
auto resolveViaLookup = [&] {
424+
for (auto member : nominal->lookupDirect(assocType->getFullName())) {
425+
auto memberType = dyn_cast<TypeDecl>(member);
426+
if (!memberType) continue;
427+
if (memberType->getDeclContext() != nominal) continue;
428+
429+
conformance->setTypeWitness(assocType,
430+
nominal->mapTypeIntoContext(
431+
memberType->getDeclaredInterfaceType()),
432+
memberType);
433+
return true;
434+
}
435+
436+
return false;
437+
};
438+
439+
// RawRepresentable.RawValue.
440+
if (*knownKind == KnownProtocolKind::RawRepresentable) {
441+
assert(assocType->getName() == ctx.Id_RawValue);
442+
if (auto enumDecl = dyn_cast<EnumDecl>(nominal)) {
443+
// First, try to resolve via lookup, so we get the declaration.
444+
if (resolveViaLookup()) return true;
445+
446+
// Otherwise, use the raw type.
447+
if (enumDecl->hasRawType()) {
448+
conformance->setTypeWitness(assocType, enumDecl->getRawType(), nullptr);
449+
return true;
450+
}
451+
452+
return false;
453+
}
454+
455+
// All other cases resolve via lookup.
456+
return resolveViaLookup();
457+
}
458+
459+
// OptionSet.Element.
460+
if (*knownKind == KnownProtocolKind::OptionSet) {
461+
assert(assocType->getName() == ctx.Id_Element);
462+
return resolveViaLookup();
463+
}
464+
465+
// _ObjectiveCBridgeable._ObjectiveCType
466+
if (*knownKind == KnownProtocolKind::ObjectiveCBridgeable) {
467+
assert(assocType->getName() == ctx.Id_ObjectiveCType);
468+
return resolveViaLookup();
469+
}
470+
471+
// _BridgedStoredNSError.Code
472+
if (*knownKind == KnownProtocolKind::BridgedStoredNSError) {
473+
assert(assocType->getName() == ctx.Id_Code);
474+
return resolveViaLookup();
475+
}
476+
477+
// ErrorCodeProtocol._ErrorType.
478+
if (*knownKind == KnownProtocolKind::ErrorCodeProtocol) {
479+
assert(assocType->getName() == ctx.Id_ErrorType);
480+
return resolveViaLookup();
481+
}
482+
483+
return false;
484+
}
485+
401486
std::pair<Type, TypeDecl *>
402487
NormalProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
403488
LazyResolver *resolver) const {
@@ -407,8 +492,11 @@ NormalProtocolConformance::getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
407492
auto known = TypeWitnesses.find(assocType);
408493
if (known == TypeWitnesses.end()) {
409494
PrettyStackTraceRequirement trace("resolving", this, assocType);
410-
assert(resolver && "Unable to resolve type witness");
411-
resolver->resolveTypeWitness(this, assocType);
495+
if (!resolveKnownTypeWitness(const_cast<NormalProtocolConformance *>(this),
496+
assocType)) {
497+
assert(resolver && "Unable to resolve type witness");
498+
resolver->resolveTypeWitness(this, assocType);
499+
}
412500
known = TypeWitnesses.find(assocType);
413501
assert(known != TypeWitnesses.end() && "Didn't resolve witness?");
414502
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Foundation
2+
3+
@inline(__always) @_transparent public func compareToSelf<T: Equatable>(_ t: T) -> Bool {
4+
return t == t
5+
}
6+
7+
@inline(__always) @_transparent public func compareImportedEnumToSelf(_ e: NSRuncingMode) -> Bool {
8+
return compareToSelf(e)
9+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
4+
// FIXME: BEGIN -enable-source-import hackaround
5+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t %clang-importer-sdk-path/swift-modules/CoreGraphics.swift
6+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -emit-module -o %t %clang-importer-sdk-path/swift-modules/Foundation.swift
7+
// FIXME: END -enable-source-import hackaround
8+
9+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -emit-module -o %t -parse-as-library %S/Inputs/use_imported_enums.swift -module-name UsesImportedEnums
10+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk-nosource -I %t) -emit-sil %s | %FileCheck %s
11+
12+
// REQUIRES: objc_interop
13+
14+
import Foundation
15+
import UsesImportedEnums
16+
17+
// CHECK-LABEL: sil hidden @_T04main4testSbSC13NSRuncingModeO1e_tF
18+
func test(e: NSRuncingMode) -> Bool {
19+
// CHECK-NOT: return
20+
// CHECK: _T0s2eeoiSbx_xts16RawRepresentableRzs9Equatable0B5ValueRpzlF
21+
return compareImportedEnumToSelf(e)
22+
}

0 commit comments

Comments
 (0)