Skip to content

Commit 877751a

Browse files
authored
Merge pull request #20419 from jrose-apple/resolution-dissolution
[ParseableInterfaces] Skip value witnesses of resilient conformances
2 parents 41d1c6f + 61dd307 commit 877751a

File tree

5 files changed

+167
-163
lines changed

5 files changed

+167
-163
lines changed

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 138 additions & 130 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

@@ -3355,6 +3347,47 @@ CheckTypeWitnessResult swift::checkTypeWitness(TypeChecker &tc, DeclContext *dc,
33553347
return CheckTypeWitnessResult();
33563348
}
33573349

3350+
ResolveWitnessResult
3351+
ConformanceChecker::resolveWitnessTryingAllStrategies(ValueDecl *requirement) {
3352+
using ResolveWitnessStrategy =
3353+
decltype(&ConformanceChecker::resolveWitnessViaLookup);
3354+
static const constexpr ResolveWitnessStrategy defaultStrategies[] = {
3355+
&ConformanceChecker::resolveWitnessViaLookup,
3356+
&ConformanceChecker::resolveWitnessViaDerivation,
3357+
&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+
}
3375+
3376+
for (auto strategy : strategies) {
3377+
ResolveWitnessResult result = (this->*strategy)(requirement);
3378+
switch (result) {
3379+
case ResolveWitnessResult::Success:
3380+
case ResolveWitnessResult::ExplicitFailed:
3381+
return result;
3382+
case ResolveWitnessResult::Missing:
3383+
// Continue trying.
3384+
break;
3385+
}
3386+
}
3387+
3388+
return ResolveWitnessResult::Missing;
3389+
}
3390+
33583391
/// Attempt to resolve a type witness via member name lookup.
33593392
ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup(
33603393
AssociatedTypeDecl *assocType) {
@@ -3625,60 +3658,14 @@ void ConformanceChecker::ensureRequirementsAreSatisfied(
36253658
}
36263659

36273660
#pragma mark Protocol conformance checking
3628-
void ConformanceChecker::checkConformance(MissingWitnessDiagnosisKind Kind) {
3629-
assert(!Conformance->isComplete() && "Conformance is already complete");
3630-
3631-
FrontendStatsTracer statsTracer(TC.Context.Stats, "check-conformance",
3632-
Conformance);
36333661

3634-
llvm::SaveAndRestore<bool> restoreSuppressDiagnostics(SuppressDiagnostics);
3635-
SuppressDiagnostics = false;
3636-
3637-
// FIXME: Caller checks that this type conforms to all of the
3638-
// inherited protocols.
3639-
3640-
// Emit known diags for this conformance.
3641-
emitDelayedDiags();
3642-
3643-
// If delayed diags have already complained, return.
3644-
if (AlreadyComplained) {
3645-
Conformance->setInvalid();
3646-
return;
3647-
}
3648-
3649-
// Resolve all of the type witnesses.
3650-
resolveTypeWitnesses();
3651-
3652-
// Diagnose missing type witnesses for now.
3653-
diagnoseMissingWitnesses(Kind);
3654-
3655-
// Ensure the conforming type is used.
3656-
//
3657-
// FIXME: This feels like the wrong place for this, but if we don't put
3658-
// it here, extensions don't end up depending on the extended type.
3659-
recordConformanceDependency(DC, Adoptee->getAnyNominal(), Conformance, false);
3660-
3661-
// If we complain about any associated types, there is no point in continuing.
3662-
// FIXME: Not really true. We could check witnesses that don't involve the
3663-
// failed associated types.
3664-
if (AlreadyComplained) {
3665-
Conformance->setInvalid();
3666-
return;
3667-
}
3668-
3669-
// Diagnose missing value witnesses later.
3670-
SWIFT_DEFER { diagnoseMissingWitnesses(Kind); };
3671-
3672-
// Ensure the associated type conformances are used.
3673-
addUsedConformances(Conformance);
3674-
3675-
// Check non-type requirements.
3662+
void ConformanceChecker::resolveValueWitnesses() {
36763663
for (auto member : Proto->getMembers()) {
36773664
auto requirement = dyn_cast<ValueDecl>(member);
36783665
if (!requirement)
36793666
continue;
36803667

3681-
// Associated type requirements handled above.
3668+
// Associated type requirements handled elsewhere.
36823669
if (isa<TypeDecl>(requirement))
36833670
continue;
36843671

@@ -3829,9 +3816,9 @@ void ConformanceChecker::checkConformance(MissingWitnessDiagnosisKind Kind) {
38293816
// If this is an accessor for a storage decl, ignore it.
38303817
if (isa<AccessorDecl>(requirement))
38313818
continue;
3832-
3833-
// Try to resolve the witness via explicit definitions.
3834-
switch (resolveWitnessViaLookup(requirement)) {
3819+
3820+
// Try to resolve the witness.
3821+
switch (resolveWitnessTryingAllStrategies(requirement)) {
38353822
case ResolveWitnessResult::Success:
38363823
finalizeWitness();
38373824
continue;
@@ -3841,41 +3828,62 @@ void ConformanceChecker::checkConformance(MissingWitnessDiagnosisKind Kind) {
38413828
continue;
38423829

38433830
case ResolveWitnessResult::Missing:
3844-
// Continue trying below.
3831+
// Let it get diagnosed later.
38453832
break;
38463833
}
3834+
}
3835+
}
38473836

3848-
// Try to resolve the witness via derivation.
3849-
switch (resolveWitnessViaDerivation(requirement)) {
3850-
case ResolveWitnessResult::Success:
3851-
finalizeWitness();
3852-
continue;
3837+
void ConformanceChecker::checkConformance(MissingWitnessDiagnosisKind Kind) {
3838+
assert(!Conformance->isComplete() && "Conformance is already complete");
38533839

3854-
case ResolveWitnessResult::ExplicitFailed:
3855-
Conformance->setInvalid();
3856-
continue;
3840+
FrontendStatsTracer statsTracer(TC.Context.Stats, "check-conformance",
3841+
Conformance);
38573842

3858-
case ResolveWitnessResult::Missing:
3859-
// Continue trying below.
3860-
break;
3861-
}
3843+
llvm::SaveAndRestore<bool> restoreSuppressDiagnostics(SuppressDiagnostics);
3844+
SuppressDiagnostics = false;
38623845

3863-
// Try to resolve the witness via defaults.
3864-
switch (resolveWitnessViaDefault(requirement)) {
3865-
case ResolveWitnessResult::Success:
3866-
finalizeWitness();
3867-
continue;
3846+
// FIXME: Caller checks that this type conforms to all of the
3847+
// inherited protocols.
38683848

3869-
case ResolveWitnessResult::ExplicitFailed:
3870-
Conformance->setInvalid();
3871-
continue;
3849+
// Emit known diags for this conformance.
3850+
emitDelayedDiags();
38723851

3873-
case ResolveWitnessResult::Missing:
3874-
// Continue trying below.
3875-
break;
3876-
}
3852+
// If delayed diags have already complained, return.
3853+
if (AlreadyComplained) {
3854+
Conformance->setInvalid();
3855+
return;
38773856
}
38783857

3858+
// Resolve all of the type witnesses.
3859+
resolveTypeWitnesses();
3860+
3861+
// Diagnose missing type witnesses for now.
3862+
diagnoseMissingWitnesses(Kind);
3863+
3864+
// Ensure the conforming type is used.
3865+
//
3866+
// FIXME: This feels like the wrong place for this, but if we don't put
3867+
// it here, extensions don't end up depending on the extended type.
3868+
recordConformanceDependency(DC, Adoptee->getAnyNominal(), Conformance, false);
3869+
3870+
// If we complain about any associated types, there is no point in continuing.
3871+
// FIXME: Not really true. We could check witnesses that don't involve the
3872+
// failed associated types.
3873+
if (AlreadyComplained) {
3874+
Conformance->setInvalid();
3875+
return;
3876+
}
3877+
3878+
// Diagnose missing value witnesses later.
3879+
SWIFT_DEFER { diagnoseMissingWitnesses(Kind); };
3880+
3881+
// Ensure the associated type conformances are used.
3882+
addUsedConformances(Conformance);
3883+
3884+
// Check non-type requirements.
3885+
resolveValueWitnesses();
3886+
38793887
emitDelayedDiags();
38803888

38813889
// Except in specific hardcoded cases for Foundation/Swift

lib/Sema/TypeCheckProtocol.h

Lines changed: 11 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

@@ -629,6 +632,11 @@ class ConformanceChecker : public WitnessChecker {
629632
/// Resolve a (non-type) witness via default definition or optional.
630633
ResolveWitnessResult resolveWitnessViaDefault(ValueDecl *requirement);
631634

635+
/// Resolve a (non-type) witness by trying each standard strategy until one
636+
/// of them produces a result.
637+
ResolveWitnessResult
638+
resolveWitnessTryingAllStrategies(ValueDecl *requirement);
639+
632640
/// Attempt to resolve a type witness via member name lookup.
633641
ResolveWitnessResult resolveTypeWitnessViaLookup(
634642
AssociatedTypeDecl *assocType);
@@ -675,6 +683,9 @@ class ConformanceChecker : public WitnessChecker {
675683
/// Resolve all of the type witnesses.
676684
void resolveTypeWitnesses();
677685

686+
/// Resolve all of the non-type witnesses.
687+
void resolveValueWitnesses();
688+
678689
/// Resolve the witness for the given non-type requirement as
679690
/// directly as possible, only resolving other witnesses if
680691
/// needed, e.g., to determine type witnesses used within the

0 commit comments

Comments
 (0)