Skip to content

Commit 61dd307

Browse files
committed
[ParseableInterfaces] Skip value witnesses of resilient conformances
We can't devirtualize through these conformances anyway, so we can get a (probably tiny) speedup by not doing any resolution of non-type witnesses. rdar://problem/43824088
1 parent a16644c commit 61dd307

File tree

4 files changed

+85
-55
lines changed

4 files changed

+85
-55
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 66 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,23 +1173,6 @@ bool WitnessChecker::findBestWitness(
11731173
}
11741174

11751175
if (numViable == 0) {
1176-
// Assume any missing value witnesses for a conformance in a parseable
1177-
// interface can be treated as opaque.
1178-
// FIXME: ...but we should do something better about types.
1179-
if (conformance && !conformance->isInvalid()) {
1180-
if (auto *SF = DC->getParentSourceFile()) {
1181-
if (SF->Kind == SourceFileKind::Interface) {
1182-
auto match = matchWitness(TC, ReqEnvironmentCache, Proto,
1183-
conformance, DC, requirement, requirement);
1184-
assert(match.isViable());
1185-
numViable = 1;
1186-
bestIdx = matches.size();
1187-
matches.push_back(std::move(match));
1188-
return true;
1189-
}
1190-
}
1191-
}
1192-
11931176
if (anyFromUnconstrainedExtension &&
11941177
conformance != nullptr &&
11951178
conformance->isInvalid()) {
@@ -2914,6 +2897,23 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement,
29142897
}
29152898
}
29162899

2900+
ResolveWitnessResult
2901+
ConformanceChecker::resolveWitnessAsOpaque(ValueDecl *requirement) {
2902+
assert(!isa<AssociatedTypeDecl>(requirement) && "Use resolveTypeWitnessVia*");
2903+
auto match = matchWitness(TC, ReqEnvironmentCache, Proto,
2904+
Conformance, DC, requirement, requirement);
2905+
recordWitness(requirement, match);
2906+
return ResolveWitnessResult::Success;
2907+
}
2908+
2909+
static bool isConformanceFromParseableInterface(
2910+
const NormalProtocolConformance *conformance) {
2911+
auto *containingSF = conformance->getDeclContext()->getParentSourceFile();
2912+
if (!containingSF)
2913+
return false;
2914+
return containingSF->Kind == SourceFileKind::Interface;
2915+
}
2916+
29172917
ResolveWitnessResult
29182918
ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
29192919
assert(!isa<AssociatedTypeDecl>(requirement) && "Use resolveTypeWitnessVia*");
@@ -2931,25 +2931,33 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
29312931
}
29322932
}
29332933

2934-
// Determine whether we can derive a witness for this requirement.
2935-
bool canDerive = false;
2936-
2937-
// Can a witness for this requirement be derived for this nominal type?
2938-
if (auto derivable = DerivedConformance::getDerivableRequirement(
2939-
TC,
2940-
nominal,
2941-
requirement)) {
2942-
if (derivable == requirement) {
2943-
// If it's the same requirement, we can derive it here.
2944-
canDerive = true;
2945-
} else {
2946-
// Otherwise, go satisfy the derivable requirement, which can introduce
2947-
// a member that could in turn satisfy *this* requirement.
2948-
auto derivableProto = cast<ProtocolDecl>(derivable->getDeclContext());
2949-
if (auto conformance =
2950-
TC.conformsToProtocol(Adoptee, derivableProto, DC, None)) {
2951-
if (conformance->isConcrete())
2952-
(void)conformance->getConcrete()->getWitnessDecl(derivable, &TC);
2934+
// Determine whether we can get a witness for this requirement some other way.
2935+
bool hasFallbacks = false;
2936+
if (!hasFallbacks)
2937+
hasFallbacks = requirement->getAttrs().hasAttribute<OptionalAttr>();
2938+
if (!hasFallbacks)
2939+
hasFallbacks = requirement->getAttrs().isUnavailable(TC.Context);
2940+
if (!hasFallbacks)
2941+
hasFallbacks = isConformanceFromParseableInterface(Conformance);
2942+
2943+
if (!hasFallbacks) {
2944+
// Can a witness for this requirement be derived for this nominal type?
2945+
if (auto derivable = DerivedConformance::getDerivableRequirement(
2946+
TC,
2947+
nominal,
2948+
requirement)) {
2949+
if (derivable == requirement) {
2950+
// If it's the same requirement, we can derive it here.
2951+
hasFallbacks = true;
2952+
} else {
2953+
// Otherwise, go satisfy the derivable requirement, which can introduce
2954+
// a member that could in turn satisfy *this* requirement.
2955+
auto derivableProto = cast<ProtocolDecl>(derivable->getDeclContext());
2956+
if (auto conformance =
2957+
TC.conformsToProtocol(Adoptee, derivableProto, DC, None)) {
2958+
if (conformance->isConcrete())
2959+
(void)conformance->getConcrete()->getWitnessDecl(derivable, &TC);
2960+
}
29532961
}
29542962
}
29552963
}
@@ -2960,11 +2968,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
29602968
unsigned bestIdx = 0;
29612969
bool doNotDiagnoseMatches = false;
29622970
bool ignoringNames = false;
2963-
bool considerRenames =
2964-
!canDerive && !requirement->getAttrs().hasAttribute<OptionalAttr>() &&
2965-
!requirement->getAttrs().isUnavailable(TC.Context);
2966-
if (findBestWitness(requirement,
2967-
considerRenames ? &ignoringNames : nullptr,
2971+
if (findBestWitness(requirement, hasFallbacks ? nullptr : &ignoringNames,
29682972
Conformance,
29692973
/* out parameters: */
29702974
matches, numViable, bestIdx, doNotDiagnoseMatches)) {
@@ -3182,19 +3186,7 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
31823186
// We have either no matches or an ambiguous match.
31833187

31843188
// If we can derive a definition for this requirement, just call it missing.
3185-
if (canDerive) {
3186-
return ResolveWitnessResult::Missing;
3187-
}
3188-
3189-
// If the requirement is optional, it's okay. We'll satisfy this via
3190-
// our handling of default definitions.
3191-
//
3192-
// FIXME: revisit this once we get default definitions in protocol bodies.
3193-
//
3194-
// Treat 'unavailable' implicitly as if it were 'optional'.
3195-
// The compiler will reject actual uses.
3196-
auto Attrs = requirement->getAttrs();
3197-
if (Attrs.hasAttribute<OptionalAttr>() || Attrs.isUnavailable(TC.Context)) {
3189+
if (hasFallbacks) {
31983190
return ResolveWitnessResult::Missing;
31993191
}
32003192

@@ -3357,10 +3349,29 @@ CheckTypeWitnessResult swift::checkTypeWitness(TypeChecker &tc, DeclContext *dc,
33573349

33583350
ResolveWitnessResult
33593351
ConformanceChecker::resolveWitnessTryingAllStrategies(ValueDecl *requirement) {
3360-
decltype(&ConformanceChecker::resolveWitnessViaLookup) strategies[] = {
3352+
using ResolveWitnessStrategy =
3353+
decltype(&ConformanceChecker::resolveWitnessViaLookup);
3354+
static const constexpr ResolveWitnessStrategy defaultStrategies[] = {
33613355
&ConformanceChecker::resolveWitnessViaLookup,
33623356
&ConformanceChecker::resolveWitnessViaDerivation,
33633357
&ConformanceChecker::resolveWitnessViaDefault};
3358+
ArrayRef<ResolveWitnessStrategy> strategies = defaultStrategies;
3359+
3360+
// Don't try any sort of derivation when processing a parseable interface.
3361+
if (isConformanceFromParseableInterface(Conformance)) {
3362+
if (Conformance->isResilient()) {
3363+
// Resilient conformances don't allow any sort of devirtualization, so
3364+
// don't bother looking up witnesses at all.
3365+
static const constexpr ResolveWitnessStrategy resilientStrategies[] = {
3366+
&ConformanceChecker::resolveWitnessAsOpaque};
3367+
strategies = resilientStrategies;
3368+
} else {
3369+
static const constexpr ResolveWitnessStrategy interfaceStrategies[] = {
3370+
&ConformanceChecker::resolveWitnessViaLookup,
3371+
&ConformanceChecker::resolveWitnessAsOpaque};
3372+
strategies = interfaceStrategies;
3373+
}
3374+
}
33643375

33653376
for (auto strategy : strategies) {
33663377
ResolveWitnessResult result = (this->*strategy)(requirement);

lib/Sema/TypeCheckProtocol.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,9 @@ class ConformanceChecker : public WitnessChecker {
620620
void checkNonFinalClassWitness(ValueDecl *requirement,
621621
ValueDecl *witness);
622622

623+
/// Resolve the (non-type) witness as requiring dynamic dispatch.
624+
ResolveWitnessResult resolveWitnessAsOpaque(ValueDecl *requirement);
625+
623626
/// Resolve a (non-type) witness via name lookup.
624627
ResolveWitnessResult resolveWitnessViaLookup(ValueDecl *requirement);
625628

test/ParseableInterface/Conformances.swiftinterface

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,15 @@ public struct OpaqueStructImpl: MyProto {}
4747
// CHECK: function_ref @$s12Conformances7MyProtoP4propSivs
4848
// CHECK: function_ref @$s12Conformances7MyProtoPyS2icig
4949
// CHECK: end sil function '$s16ConformancesUser10testOpaqueSiyF'
50+
51+
public struct ResilientStructImpl: MyProto {
52+
@available(*, unavailable) // Ignore this, because the conformance is resilient.
53+
public init()
54+
}
55+
56+
// CHECK-LABEL: sil @$s16ConformancesUser13testResilientSiyF
57+
// CHECK: witness_method $ResilientStructImpl, #MyProto.init!allocator.1
58+
// CHECK: witness_method $ResilientStructImpl, #MyProto.method!1
59+
// CHECK: witness_method $ResilientStructImpl, #MyProto.prop!setter.1
60+
// CHECK: witness_method $ResilientStructImpl, #MyProto.subscript!getter.1
61+
// CHECK: end sil function '$s16ConformancesUser13testResilientSiyF'

test/ParseableInterface/Inputs/ConformancesUser.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,7 @@ public func testFull() -> Int {
1414
public func testOpaque() -> Int {
1515
return testGeneric(OpaqueStructImpl.self)
1616
}
17+
18+
public func testResilient() -> Int {
19+
return testGeneric(ResilientStructImpl.self)
20+
}

0 commit comments

Comments
 (0)